yadflow 2.13.0 → 2.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -2
- package/README.md +28 -16
- package/bin/yad.mjs +55 -9
- package/cli/artifact-status.mjs +102 -0
- package/cli/doctor.mjs +35 -2
- package/cli/epic-state.mjs +86 -11
- package/cli/gate.mjs +130 -60
- package/cli/lib.mjs +3 -0
- package/cli/manifest.mjs +2 -0
- package/cli/next.mjs +123 -0
- package/cli/platform.mjs +44 -6
- package/cli/repo.mjs +8 -4
- package/cli/setup.mjs +215 -80
- package/package.json +1 -1
- package/skills/sdlc/config.yaml +8 -0
- package/skills/yad-analysis/SKILL.md +6 -3
- package/skills/yad-architecture/SKILL.md +8 -3
- package/skills/yad-checks/SKILL.md +7 -0
- package/skills/yad-checks/templates/checks/ledger-guard.sh +117 -0
- package/skills/yad-checks/templates/checks/verified-commits.sh +9 -1
- package/skills/yad-checks/templates/github/yad-hub-checks.yml +8 -0
- package/skills/yad-checks/templates/gitlab/yad-hub-checks.gitlab-ci.yml +7 -0
- package/skills/yad-epic/SKILL.md +4 -1
- package/skills/yad-hub-bridge/SKILL.md +41 -14
- package/skills/yad-hub-bridge/references/bridge.md +93 -51
- package/skills/yad-hub-bridge/templates/github/yad-gate-sync.yml +85 -35
- package/skills/yad-hub-bridge/templates/gitlab/yad-gate-sync.gitlab-ci.yml +63 -32
- package/skills/yad-review-gate/SKILL.md +12 -10
- package/skills/yad-stories/SKILL.md +8 -3
- package/skills/yad-test-cases/SKILL.md +10 -5
- package/skills/yad-ui/SKILL.md +8 -3
|
@@ -1,33 +1,41 @@
|
|
|
1
1
|
# yad-managed-include: yad-hub-bridge
|
|
2
|
-
#
|
|
3
|
-
# root .gitlab-ci.yml via:
|
|
2
|
+
# Merge-time gate sync for the PRODUCT HUB, as an INCLUDABLE fragment (Path B). Pulled into the
|
|
3
|
+
# hub's root .gitlab-ci.yml via:
|
|
4
4
|
# include:
|
|
5
5
|
# - local: '.gitlab/ci/yad-gate-sync.yml'
|
|
6
6
|
# so wiring never edits the foreign root pipeline beyond that one include line. The job carries
|
|
7
7
|
# `needs: []` and no `stage:` (same merge-safety as the yad-checks fragment).
|
|
8
8
|
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# the
|
|
9
|
+
# CI is the SOLE writer of the ledger, and it writes ONLY at merge, ONLY to the default branch.
|
|
10
|
+
# During review there is NO CI write: the MR itself is the source of truth (native approvals +
|
|
11
|
+
# threads). CI never pushes to the MR source (review) branch, so an in-flight approval is never
|
|
12
|
+
# dismissed by a CI commit. Because nothing is pushed to the source branch, you can safely enable
|
|
13
|
+
# "Remove all approvals when commits are added to the source branch" — only the artifact owner's own
|
|
14
|
+
# pushes will dismiss approvals (the intended revoke-on-change).
|
|
13
15
|
#
|
|
14
|
-
# GitLab is the DEGRADED
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
16
|
+
# GitLab is the DEGRADED platform, stated honestly: it fires no pipeline on an approval, and the
|
|
17
|
+
# merge push to the default branch may omit the source-branch name (squash / fast-forward). So the
|
|
18
|
+
# ledger is reconciled from the PLATFORM (glab API), not from any per-branch ledger:
|
|
19
|
+
# - MERGE push (default branch, commit names a review branch): resolve the merged MR's IID from its
|
|
20
|
+
# source branch, then advance + flip status on the default branch — near-immediate.
|
|
21
|
+
# - SCHEDULED sweep (catch-up): enumerate recently-merged review MRs via the API and advance any
|
|
22
|
+
# not yet advanced (idempotent — a step already `done` is skipped). Covers bare approvals and
|
|
23
|
+
# squash merges whose commit message dropped the branch name. Create a pipeline schedule
|
|
24
|
+
# (one-time, cannot be committed as code): cron `*/15 * * * *` with variable SDLC_GATE_SYNC=true.
|
|
25
|
+
# UI: CI/CD > Schedules, or:
|
|
19
26
|
# glab api projects/:id/pipeline_schedules -X POST \
|
|
20
27
|
# -f description='yad gate sync' -f ref=main -f cron='*/15 * * * *'
|
|
21
28
|
# glab api "projects/:id/pipeline_schedules/<id>/variables" -X POST \
|
|
22
29
|
# -f key=SDLC_GATE_SYNC -f value=true
|
|
23
30
|
#
|
|
24
|
-
# Token
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
31
|
+
# Token: CI_JOB_TOKEN can neither read the approvals API nor push to a protected branch. Create a
|
|
32
|
+
# PROJECT ACCESS TOKEN with `read_api` + `write_repository` (role Developer+, allowed to push to the
|
|
33
|
+
# default branch — the merge advance pushes there) and store it as a masked CI/CD variable
|
|
34
|
+
# SDLC_GATE_TOKEN. Without it the job fails visibly; recover by setting the token, then re-run the
|
|
35
|
+
# pipeline or run `yad gate ci --branch <review-branch> --pr <iid> --merged` locally on the default
|
|
36
|
+
# branch (advisory `yad gate sync` is read-only in bridge mode and cannot recover a stuck gate).
|
|
29
37
|
variables:
|
|
30
|
-
GIT_DEPTH: "0" # full history: gate ci
|
|
38
|
+
GIT_DEPTH: "0" # full history: gate ci pushes the advance to the default branch
|
|
31
39
|
|
|
32
40
|
yad-gate-sync:
|
|
33
41
|
needs: []
|
|
@@ -38,32 +46,55 @@ yad-gate-sync:
|
|
|
38
46
|
tags: [$YAD_RUNNER_TAGS]
|
|
39
47
|
image: node:20
|
|
40
48
|
rules:
|
|
41
|
-
# MR-event path: fires on MR open / push to the review branch — NOT on approval (see header).
|
|
42
|
-
- if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME =~ /^review\/EP-/
|
|
43
49
|
# Merge path: branch pipeline on the default branch whose merge commit names a review branch.
|
|
44
50
|
# Squash/fast-forward merges may omit the branch name — those advance on the next scheduled sweep.
|
|
45
51
|
- if: $CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_COMMIT_MESSAGE =~ /review\/EP-/
|
|
46
|
-
# Scheduled sweep: the only path that picks up approvals.
|
|
52
|
+
# Scheduled sweep: the only path that picks up bare approvals (advances merged reviews it finds).
|
|
47
53
|
- if: $CI_PIPELINE_SOURCE == "schedule" && $SDLC_GATE_SYNC == "true"
|
|
48
54
|
script:
|
|
49
55
|
# Pinned glab binary (node:20 has no glab). Alternative: image registry.gitlab.com/gitlab-org/cli.
|
|
50
56
|
- GLAB_VERSION=1.55.0
|
|
51
57
|
- curl -fsSL "https://gitlab.com/gitlab-org/cli/-/releases/v${GLAB_VERSION}/downloads/glab_${GLAB_VERSION}_linux_amd64.deb" -o /tmp/glab.deb && dpkg -i /tmp/glab.deb
|
|
52
|
-
# The ledger lives on the default branch; gate ci overlays the artifact from the review branch itself.
|
|
53
|
-
- git fetch origin "$CI_DEFAULT_BRANCH"
|
|
54
|
-
- git checkout -B "$CI_DEFAULT_BRANCH" "origin/$CI_DEFAULT_BRANCH"
|
|
55
58
|
- git config user.name "yad-gate-sync" && git config user.email "yad-gate-sync@noreply.${CI_SERVER_HOST}"
|
|
56
59
|
- git remote set-url origin "https://oauth2:${SDLC_GATE_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git"
|
|
57
60
|
- export GITLAB_TOKEN="$SDLC_GATE_TOKEN" GITLAB_HOST="$CI_SERVER_URL"
|
|
61
|
+
- git fetch origin "$CI_DEFAULT_BRANCH"
|
|
62
|
+
- git checkout -B "$CI_DEFAULT_BRANCH" "origin/$CI_DEFAULT_BRANCH"
|
|
58
63
|
- |
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
rc=0
|
|
65
|
+
if [ "$CI_PIPELINE_SOURCE" = "schedule" ]; then
|
|
66
|
+
# SCHEDULED SWEEP — discover merged review MRs from the platform (Path B keeps no per-branch
|
|
67
|
+
# ledger), then advance each. `gate ci` is idempotent: a step already `done` is skipped.
|
|
68
|
+
# A stuck review MR (a squash merge whose commit dropped the branch name, or a failed merge
|
|
69
|
+
# push) is always RECENT, so sweep a generous recent window and PAGINATE it fully (--paginate)
|
|
70
|
+
# — this bounds cost without the old hard 50-row cap that could permanently strand older MRs.
|
|
71
|
+
# Aggregate failures into rc and exit nonzero so a persistent failure surfaces (red pipeline)
|
|
72
|
+
# instead of silently stranding a merged gate. Read from a file, not a pipe (a piped `while`
|
|
73
|
+
# runs in a subshell, losing rc). An MR stuck beyond the window needs manual recovery — run
|
|
74
|
+
# `yad gate ci --branch <review-branch> --pr <iid> --merged` locally on the default branch.
|
|
75
|
+
SINCE="$(date -u -d '7 days ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-7d +%Y-%m-%dT%H:%M:%SZ)"
|
|
76
|
+
glab api --paginate "projects/:id/merge_requests?state=merged&updated_after=${SINCE}&per_page=100&order_by=updated_at" \
|
|
77
|
+
--jq '.[] | select(.source_branch | startswith("review/EP-")) | "\(.iid) \(.source_branch)"' > /tmp/yad-merged-mrs || rc=1
|
|
78
|
+
while read -r IID REF; do
|
|
79
|
+
[ -n "$IID" ] || continue
|
|
80
|
+
git checkout -q -B "$CI_DEFAULT_BRANCH" "origin/$CI_DEFAULT_BRANCH"
|
|
81
|
+
npx -y -p yadflow@3 yad gate ci --branch "$REF" --pr "$IID" --merged || rc=1
|
|
82
|
+
done < /tmp/yad-merged-mrs
|
|
67
83
|
else
|
|
68
|
-
|
|
84
|
+
# MERGE push to the default branch whose commit names a review branch: resolve the merged MR's
|
|
85
|
+
# IID from its source branch so `gate ci` can re-read approvals, then advance there.
|
|
86
|
+
REVIEW_BRANCH="$(printf '%s' "$CI_COMMIT_MESSAGE" | grep -oE 'review/EP-[A-Za-z0-9._/-]+' | head -n1 || true)"
|
|
87
|
+
if [ -n "$REVIEW_BRANCH" ]; then
|
|
88
|
+
IID="$(glab api "projects/:id/merge_requests?source_branch=${REVIEW_BRANCH}&state=merged" --jq '.[0].iid' 2>/dev/null || true)"
|
|
89
|
+
if [ -n "$IID" ]; then
|
|
90
|
+
# Pass --pr + IID as two distinct args (avoid a fragile, shell-dependent ${IID:+...} split).
|
|
91
|
+
npx -y -p yadflow@3 yad gate ci --branch "$REVIEW_BRANCH" --pr "$IID" --merged || rc=1
|
|
92
|
+
else
|
|
93
|
+
# Without the IID, gate ci cannot re-read approvals — fail visibly (the scheduled sweep
|
|
94
|
+
# retries) rather than running a green no-op that silently leaves the gate unadvanced.
|
|
95
|
+
echo "yad-gate-sync: could not resolve merged MR IID for ${REVIEW_BRANCH}" >&2
|
|
96
|
+
rc=1
|
|
97
|
+
fi
|
|
98
|
+
fi
|
|
69
99
|
fi
|
|
100
|
+
exit $rc
|
|
@@ -165,21 +165,23 @@ If the predicate **passes**:
|
|
|
165
165
|
now `ready-for-build`, with `test-cases` running in parallel).
|
|
166
166
|
|
|
167
167
|
### PR-driven automation (the `yad gate` CLI)
|
|
168
|
-
When the hub has a platform, the
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
168
|
+
When the hub has a platform, **CI is the sole writer of the ledger**. `yad gate open` opens the review
|
|
169
|
+
PR only; CI (`yad gate ci`) writes the `.sdlc/` + `reviews/` records this skill describes. The skill's
|
|
170
|
+
job is the human half: presenting the artifact, helping the owner address comments, and narrating the
|
|
171
|
+
gate. Local `yad gate sync` is advisory in bridge mode (reads the platform, prints status, writes
|
|
172
|
+
nothing); a human must never commit gate-state files (the `ledger-guard` check rejects it).
|
|
173
173
|
|
|
174
174
|
Under that CLI the gate **advances on merge**: a review PR/MR whose reviewer rule is satisfied, whose
|
|
175
175
|
comment threads are **all resolved**, and which has been **merged** auto-marks the step `done` and
|
|
176
176
|
unblocks the next step. (Until those three hold, the step stays `in_review`.)
|
|
177
177
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
178
|
+
The flow is **merge-driven** (wired by `yad-hub-bridge` `wire`): during review CI writes nothing — the
|
|
179
|
+
platform PR/MR is the source of truth (native approvals + threads), and CI never touches the review
|
|
180
|
+
branch (so an in-flight approval is never dismissed and required checks never strand). On the human
|
|
181
|
+
**merge** CI re-reads approvals, advances the step, and flips the artifact `status:` on the **default
|
|
182
|
+
branch**. After a merge, `git checkout <default> && git pull` to see it. The predicate and the human
|
|
183
|
+
merge are unchanged — CI never approves and never merges. File-only mode (no platform) keeps the local
|
|
184
|
+
write path.
|
|
183
185
|
|
|
184
186
|
### Hard rules (build plan §1, §5)
|
|
185
187
|
- **The merge click is the human approval act.** A front step advances only when a human merges the
|
|
@@ -24,9 +24,14 @@ There is **no `sm` agent** (Phase 0 Deviation 1): the `pm` lens breaks down the
|
|
|
24
24
|
## On Activation
|
|
25
25
|
|
|
26
26
|
### Step 1 — Resolve the epic and check the gate
|
|
27
|
-
Resolve the `EP-<slug>` (ask if not provided).
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
Resolve the `EP-<slug>` (ask if not provided).
|
|
28
|
+
|
|
29
|
+
**Precondition gate (rail):** run `yad next EP-<slug> --check stories` first. If it exits non-zero,
|
|
30
|
+
**STOP** — surface the blocker it prints and point the user at `yad next EP-<slug>`. Do not author until
|
|
31
|
+
it passes.
|
|
32
|
+
|
|
33
|
+
This passes when `stories` is the next runnable step per the state sequence — every prior step
|
|
34
|
+
(through the UI review) is `done` and `stories` is not already `done`.
|
|
30
35
|
|
|
31
36
|
### Step 1b — Open the authoring branch
|
|
32
37
|
Open the stories authoring branch `stories/EP-<slug>` per the shared procedure
|
|
@@ -36,11 +36,16 @@ the Markdown artifact only — the testing tool is additive, exactly like the de
|
|
|
36
36
|
## On Activation
|
|
37
37
|
|
|
38
38
|
### Step 1 — Resolve the epic and check the track
|
|
39
|
-
Resolve the `EP-<slug>` (ask if not provided).
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
**
|
|
43
|
-
|
|
39
|
+
Resolve the `EP-<slug>` (ask if not provided).
|
|
40
|
+
|
|
41
|
+
**Precondition gate (rail):** run `yad next EP-<slug> --check test-cases` first. If it exits non-zero,
|
|
42
|
+
**STOP** — surface the blocker it prints and point the user at `yad next EP-<slug>`. Do not author until
|
|
43
|
+
it passes. The check is track-aware: it keys off the `test-cases` step's predecessors, **not**
|
|
44
|
+
`currentStep` (this is a parallel track — `currentStep` stays at `ready-for-build`).
|
|
45
|
+
|
|
46
|
+
This passes once the `test-cases` step is runnable — its predecessor `stories-review` is `done` (so the
|
|
47
|
+
step has opened to `in_progress`) and `test-cases` is not already `done`. While it is still `blocked`,
|
|
48
|
+
the stories review has not passed.
|
|
44
49
|
|
|
45
50
|
### Step 1b — Open the authoring branch
|
|
46
51
|
Open the test-cases authoring branch `test-cases/EP-<slug>` per the shared procedure
|
package/skills/yad-ui/SKILL.md
CHANGED
|
@@ -33,9 +33,14 @@ like Impeccable.
|
|
|
33
33
|
## On Activation
|
|
34
34
|
|
|
35
35
|
### Step 1 — Resolve the epic and check the gate
|
|
36
|
-
Resolve the `EP-<slug>` (ask if not provided).
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
Resolve the `EP-<slug>` (ask if not provided).
|
|
37
|
+
|
|
38
|
+
**Precondition gate (rail):** run `yad next EP-<slug> --check ui-design` first. If it exits non-zero,
|
|
39
|
+
**STOP** — surface the blocker it prints and point the user at `yad next EP-<slug>`. Do not author until
|
|
40
|
+
it passes.
|
|
41
|
+
|
|
42
|
+
This passes when `ui-design` is the next runnable step per the state sequence — every prior step
|
|
43
|
+
(through the architecture review) is `done` and `ui-design` is not already `done`.
|
|
39
44
|
|
|
40
45
|
### Step 1b — Open the authoring branch
|
|
41
46
|
Open the UI authoring branch `ui-design/EP-<slug>` per the shared procedure
|