yadflow 3.15.1 → 3.15.2
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 +7 -0
- package/package.json +1 -1
- package/skills/yad-analysis/SKILL.md +4 -0
- package/skills/yad-change/SKILL.md +6 -0
- package/skills/yad-checks/SKILL.md +5 -1
- package/skills/yad-checks/templates/checks/ledger-guard.sh +78 -3
- package/skills/yad-checks/templates/github/yad-hub-checks.yml +3 -1
- package/skills/yad-checks/templates/gitlab/yad-hub-checks.gitlab-ci.yml +3 -1
- package/skills/yad-discovery/SKILL.md +4 -0
- package/skills/yad-epic/SKILL.md +4 -0
- package/skills/yad-epic/references/state-schema.md +10 -0
- package/skills/yad-hub-bridge/SKILL.md +9 -3
- package/skills/yad-hub-bridge/references/bridge.md +18 -3
- package/skills/yad-review-gate/SKILL.md +3 -1
- package/skills/yad-stub/SKILL.md +5 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## [3.15.2](https://github.com/abdelrahmannasr/yadflow/compare/v3.15.1...v3.15.2) (2026-08-11)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **checks:** exempt a new epic's ledger seed from ledger-guard ([ba923c2](https://github.com/abdelrahmannasr/yadflow/commit/ba923c2a3823e8bf17b2fc59b41f0160a3a11a19)), closes [#162](https://github.com/abdelrahmannasr/yadflow/issues/162)
|
|
7
|
+
|
|
1
8
|
## [3.15.1](https://github.com/abdelrahmannasr/yadflow/compare/v3.15.0...v3.15.1) (2026-08-11)
|
|
2
9
|
|
|
3
10
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yadflow",
|
|
3
|
-
"version": "3.15.
|
|
3
|
+
"version": "3.15.2",
|
|
4
4
|
"description": "Yadflow — the gated, team, multi-repo SDLC: author → review → build with a PR-driven review gate and a zero-dependency `yad` CLI (setup, gate, commit, open-pr, ship, repo, thread, reconcile). A BMAD module + 38 yad-* skills.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "AbdelRahman Nasr",
|
|
@@ -138,6 +138,10 @@ Notes:
|
|
|
138
138
|
- Also create an empty approvals ledger `{project-root}/epics/EP-<slug>/.sdlc/approvals.json`
|
|
139
139
|
and an empty comments ledger `{project-root}/epics/EP-<slug>/.sdlc/comments.json`, each containing
|
|
140
140
|
`[]`, and the `reviews/` directory.
|
|
141
|
+
- Commit the seed on the `analysis/EP-<slug>` branch, and cut `review/EP-<slug>/analysis` from it so the
|
|
142
|
+
epic's **first** review PR/MR carries the ledger to the default branch. In bridge mode `ledger-guard`
|
|
143
|
+
exempts a new epic's ledger (creation, not mutation, #162); every later change to it is CI's. See
|
|
144
|
+
`../yad-epic/references/state-schema.md`, "Authoring branches".
|
|
141
145
|
|
|
142
146
|
### Step 7 — Stop at the gate (do NOT advance)
|
|
143
147
|
Report: epic ID, the path to `analysis.md`, and that the next action is **review** via
|
|
@@ -134,6 +134,12 @@ Seed `.sdlc/approvals.json` with one **provenance** record per inherited gate (N
|
|
|
134
134
|
`{ "artifact": "<art>", "step": "<…-review>", "status": "inherited", "from": "<epic>", "boundHash": "<hash>", "date": "<today>" }`.
|
|
135
135
|
Seed `.sdlc/comments.json` = `[]` and create `reviews/`.
|
|
136
136
|
|
|
137
|
+
Commit the seed on the `change/EP-<slug>` branch. It reaches the hub's default branch through this
|
|
138
|
+
change-epic's **first** review PR/MR — cut the `review/EP-<slug>/<artifact>` branch from `change/…` so
|
|
139
|
+
it carries the seed. In bridge mode `ledger-guard` exempts a new epic's ledger (creation, not mutation,
|
|
140
|
+
#162), so no direct push to a protected default branch is needed; every later change to that ledger is
|
|
141
|
+
CI's. See `../yad-epic/references/state-schema.md`, "Authoring branches".
|
|
142
|
+
|
|
137
143
|
When `architecture` is **inherited**, materialize the **pointer-lock** `.sdlc/contract-lock.json`:
|
|
138
144
|
`{ "artifact": "contract.md", "hash": "<parent surface hash, verbatim>", "lockedAt": "<today>", "inheritedFrom": "<epic>", "ref": "../../<epic>/.sdlc/contract-lock.json" }`.
|
|
139
145
|
There is no `contract.md` in the change-epic, so the surface cannot drift, and `contract-check` passes
|
|
@@ -55,7 +55,11 @@ and GitLab CI. This step is **by hand** in Phase 3 — run the gates with the sk
|
|
|
55
55
|
when humans legitimately own the ledger). On review PRs it FAILs any commit that touches the
|
|
56
56
|
CI-owned gate ledger (`.sdlc/{state,approvals,comments,hub-prs}.json`, `reviews/*.md`) unless it
|
|
57
57
|
is a **verified gate-bot commit** — bot-authored AND platform-Verified, since author text alone is
|
|
58
|
-
spoofable. `.sdlc/contract-lock.json` is artifact-side and exempt.
|
|
58
|
+
spoofable. `.sdlc/contract-lock.json` is artifact-side and exempt. So is a **new epic's seed**:
|
|
59
|
+
no CI path can create a ledger (`gate ci` only *advances* an existing chain, at merge, on the
|
|
60
|
+
default branch), so an epic whose `.sdlc/state.json` is absent from the base ref may be created by
|
|
61
|
+
a human on its first review PR/MR — **creation, not mutation** (#162). Once the ledger is on the
|
|
62
|
+
default branch the guard is absolute again. Runs in `yad-hub-checks`
|
|
59
63
|
alongside `verified-commits` (which waives the allowlist for the bot but still requires its
|
|
60
64
|
signature). See `yad-hub-bridge`.
|
|
61
65
|
- `templates/github/yad-verified-commits.yml` + `templates/gitlab/yad-verified-commits.gitlab-ci.yml`
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
# NOT protected:
|
|
12
12
|
# epics/*/.sdlc/contract-lock.json — artifact-side: the architect locks the contract surface in
|
|
13
13
|
# `gate open`, so a human legitimately commits it alongside the architecture artifact.
|
|
14
|
+
# A brand-new epic's ledger — CREATION is not mutation (#162). No CI path can seed one, so the
|
|
15
|
+
# seed rides the first review PR/MR; see the carve-out below. Mutation stays bot-only.
|
|
14
16
|
#
|
|
15
17
|
# A "bot commit" must be BOTH authored by the gate bot (name/email contains yad-gate-sync) AND
|
|
16
18
|
# platform-VERIFIED — author/committer text alone is user-controlled and spoofable, so the platform
|
|
@@ -114,19 +116,81 @@ trusted_bot() {
|
|
|
114
116
|
signature_verified "$1"
|
|
115
117
|
}
|
|
116
118
|
|
|
119
|
+
# ---- seeding carve-out: creation is not mutation (#162) ---------------------------------------
|
|
120
|
+
# A brand-new epic's ledger has no CI author. `gate ci` only ADVANCES an existing chain — it bails on
|
|
121
|
+
# a missing state.json ("the review branch is cut from the default branch, so it should carry it") and
|
|
122
|
+
# writes only at merge, on the default branch — and the engine itself reads a missing state.json as
|
|
123
|
+
# "not seeded yet". `gate open` writes nothing in bridge mode, and `checkpoint` stages back-half
|
|
124
|
+
# ledgers only. So the seed the authoring skills write (yad-epic / yad-change / yad-analysis /
|
|
125
|
+
# yad-discovery / yad-stub) can reach the trunk ONLY through the first review PR/MR — the one place
|
|
126
|
+
# this gate runs. Guarding it there makes the documented flow unshippable on a protected trunk, so an
|
|
127
|
+
# epic whose ledger is absent from the BASE ref is exempt: that ledger is human-authored by
|
|
128
|
+
# construction and the reviewer sees the whole of it in the diff. The moment its state.json is on the
|
|
129
|
+
# base ref the guard is absolute again.
|
|
130
|
+
#
|
|
131
|
+
# Anchored on state.json — the ledger root, the same "is this epic seeded?" question the engine asks —
|
|
132
|
+
# NOT on the individual file: adding hub-prs.json to an epic that IS on the base ref still FAILs.
|
|
133
|
+
# Anchored on the BASE ref, not the parent commit, so delete-then-re-add cannot reset the exemption
|
|
134
|
+
# (the deletion is itself a guarded change).
|
|
135
|
+
#
|
|
136
|
+
# The slug match is CASE-FOLDED on purpose. Git paths are byte-exact, but macOS/Windows checkouts are
|
|
137
|
+
# not: seeding `epics/ep-x/.sdlc/state.json` beside an on-base `epics/EP-X/` would probe as a brand-new
|
|
138
|
+
# epic, pass, and then land ON TOP of the real ledger in every case-insensitive clone — a mutation
|
|
139
|
+
# laundered as a creation. So the base's seeded slugs are read once (one ls-tree, not one probe per
|
|
140
|
+
# path) and compared folded.
|
|
141
|
+
#
|
|
142
|
+
# Slugs are held in ARRAYS read from NUL-delimited git output, never in a space- or newline-delimited
|
|
143
|
+
# string: git permits a newline inside a path, and a `EP-<newline>x` slug split across two records
|
|
144
|
+
# would drop the real epic out of the on-base list and let a mutation through as a "creation". Both
|
|
145
|
+
# arrays carry one empty sentinel element so `"${a[@]}"` is safe under `set -u` on bash 3.2 (macOS),
|
|
146
|
+
# which has no associative arrays; a slug is never empty, so the sentinel can never match.
|
|
147
|
+
base_slugs=("") # every epic with a ledger on BASE, lowercased
|
|
148
|
+
base_slugs_loaded=0
|
|
149
|
+
noted_slugs=("") # slugs already announced, so the note prints once each
|
|
150
|
+
fold() { printf '%s' "$1" | tr '[:upper:]' '[:lower:]'; }
|
|
151
|
+
in_list() { # $1 = needle, $2… = haystack
|
|
152
|
+
_needle="$1"; shift
|
|
153
|
+
for _item in "$@"; do [ "$_item" = "$_needle" ] && return 0; done
|
|
154
|
+
return 1
|
|
155
|
+
}
|
|
156
|
+
is_seeding() { # $1 = epic slug; 0 when that epic has no ledger on BASE
|
|
157
|
+
if [ "$base_slugs_loaded" = 0 ]; then
|
|
158
|
+
while IFS= read -r -d '' _p; do
|
|
159
|
+
case "$_p" in
|
|
160
|
+
epics/*/.sdlc/state.json)
|
|
161
|
+
_s="${_p#epics/}"; _s="${_s%%/*}"
|
|
162
|
+
base_slugs[${#base_slugs[@]}]="$(fold "$_s")"
|
|
163
|
+
;;
|
|
164
|
+
esac
|
|
165
|
+
done < <(git -c core.quotePath=false ls-tree -r --name-only -z "${BASE}" -- epics 2>/dev/null || true)
|
|
166
|
+
base_slugs_loaded=1
|
|
167
|
+
fi
|
|
168
|
+
_f="$(fold "$1")"
|
|
169
|
+
in_list "$_f" "${base_slugs[@]}" && return 1
|
|
170
|
+
in_list "$_f" "${noted_slugs[@]}" && return 0
|
|
171
|
+
noted_slugs[${#noted_slugs[@]}]="$_f"
|
|
172
|
+
echo "note [ledger-guard]: epics/$1 has no ledger on ${BASE} — new epic, its seed is exempt (creation, not mutation)."
|
|
173
|
+
return 0
|
|
174
|
+
}
|
|
175
|
+
|
|
117
176
|
violations=0
|
|
118
177
|
for sha in $commits; do
|
|
119
178
|
touches_ledger=0
|
|
120
|
-
|
|
179
|
+
# quotePath=false + -z on both git reads: a path git chose to escape ("epics/EP-caf\303\251/…") and a
|
|
180
|
+
# path holding a newline both match no arm below, so the gate would fail OPEN on exactly the paths it
|
|
181
|
+
# is meant to guard. NUL is the only byte a git path cannot contain.
|
|
182
|
+
while IFS= read -r -d '' f; do
|
|
121
183
|
[ -n "$f" ] || continue
|
|
122
184
|
case "$f" in
|
|
123
185
|
epics/*/.sdlc/contract-lock.json) ;; # artifact-side — allowed
|
|
124
186
|
epics/*/.sdlc/state.json|epics/*/.sdlc/approvals.json|epics/*/.sdlc/comments.json|epics/*/.sdlc/hub-prs.json|epics/*/reviews/*.md)
|
|
187
|
+
_slug="${f#epics/}"; _slug="${_slug%%/*}"
|
|
188
|
+
is_seeding "$_slug" && continue # a new epic's seed — not a mutation of a CI-owned ledger
|
|
125
189
|
touches_ledger=1
|
|
126
190
|
echo " ${sha} (author $(git show -s --format='%an' "$sha")) → $f"
|
|
127
191
|
;;
|
|
128
192
|
esac
|
|
129
|
-
done < <(git diff-tree --no-commit-id --name-only -r "$sha")
|
|
193
|
+
done < <(git -c core.quotePath=false diff-tree --no-commit-id --name-only -r -z "$sha")
|
|
130
194
|
if [ "$touches_ledger" = 1 ] && ! trusted_bot "$sha"; then
|
|
131
195
|
violations=$((violations + 1))
|
|
132
196
|
fi
|
|
@@ -134,7 +198,18 @@ done
|
|
|
134
198
|
|
|
135
199
|
if [ "$violations" -gt 0 ]; then
|
|
136
200
|
echo "FAIL [ledger-guard]: ${violations} commit(s) change CI-owned gate files without a verified gate-bot signature. The ledger is CI-owned — let CI sync the gate; do not commit .sdlc/*.json or reviews/*.md yourself."
|
|
201
|
+
# An epic's seed is exempt only while its ledger is off the base ref. Once the first review PR merges
|
|
202
|
+
# (squashed or rebased, so the SHAs differ), those same seed commits still sitting on a sibling
|
|
203
|
+
# authoring branch read as mutations — the author did nothing wrong and the remedy is a rebase, so
|
|
204
|
+
# name it rather than leaving them with "do not commit the ledger yourself".
|
|
205
|
+
echo "hint [ledger-guard]: if these are seed commits from an already-merged review PR, rebase this branch onto the updated ${BASE} — the seed carve-out applies only until the ledger is on the base ref."
|
|
137
206
|
exit 1
|
|
138
207
|
fi
|
|
139
|
-
|
|
208
|
+
# Say WHICH rule passed the range: claiming "every change is a bot commit" would be false on a range
|
|
209
|
+
# whose only ledger change was a human seed the carve-out let through.
|
|
210
|
+
if [ "${#noted_slugs[@]}" -gt 1 ]; then # >1: the sentinel element is always there
|
|
211
|
+
echo "PASS [ledger-guard]: every CI-owned gate change in ${RANGE} is a verified gate-bot commit or a new epic's seed."
|
|
212
|
+
else
|
|
213
|
+
echo "PASS [ledger-guard]: every CI-owned gate change in ${RANGE} is a verified gate-bot commit."
|
|
214
|
+
fi
|
|
140
215
|
exit 0
|
|
@@ -55,7 +55,9 @@ jobs:
|
|
|
55
55
|
body="$(mktemp)"; printf '%s' "$PR_BODY" > "$body"
|
|
56
56
|
bash checks/pr-template.sh --profile hub --head "$PR_HEAD" --changed "$changed" "$body"
|
|
57
57
|
|
|
58
|
-
# The gate ledger is CI-owned: reject non-bot commits to .sdlc
|
|
58
|
+
# The gate ledger is CI-owned: reject non-bot commits to .sdlc/{state,approvals,comments,hub-prs}.json
|
|
59
|
+
# or reviews/*.md (.sdlc/contract-lock.json is artifact-side and allowed). The one other exception is a
|
|
60
|
+
# brand-new epic's seed, which no CI path can write, so it rides this first review PR (#162).
|
|
59
61
|
ledger-guard:
|
|
60
62
|
runs-on: ubuntu-latest
|
|
61
63
|
if: github.event.action != 'edited'
|
|
@@ -44,7 +44,9 @@ yad-hub-pr-template:
|
|
|
44
44
|
- body="$(mktemp)"; printf '%s' "$CI_MERGE_REQUEST_DESCRIPTION" > "$body"
|
|
45
45
|
- bash checks/pr-template.sh --profile hub --head "$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME" --changed "$changed" "$body"
|
|
46
46
|
|
|
47
|
-
# The gate ledger is CI-owned: reject non-bot commits to .sdlc
|
|
47
|
+
# The gate ledger is CI-owned: reject non-bot commits to .sdlc/{state,approvals,comments,hub-prs}.json
|
|
48
|
+
# or reviews/*.md (.sdlc/contract-lock.json is artifact-side and allowed). The one other exception is a
|
|
49
|
+
# brand-new epic's seed, which no CI path can write, so it rides this first review MR (#162).
|
|
48
50
|
yad-hub-ledger-guard:
|
|
49
51
|
extends: .yad_hub_mr_only
|
|
50
52
|
needs: []
|
|
@@ -112,6 +112,10 @@ Notes:
|
|
|
112
112
|
never escalates to domain owners (no contract surface is touched yet).
|
|
113
113
|
- Also create an empty approvals ledger `.sdlc/approvals.json` and comments ledger
|
|
114
114
|
`.sdlc/comments.json`, each containing `[]`, and the `reviews/` directory.
|
|
115
|
+
- Commit the seed on the `discovery/EP-discovery` branch, and cut `review/EP-discovery/discovery` from
|
|
116
|
+
it so the **first** review PR/MR carries the ledger to the default branch. In bridge mode
|
|
117
|
+
`ledger-guard` exempts a new epic's ledger (creation, not mutation, #162); every later change to it
|
|
118
|
+
is CI's. See `../yad-epic/references/state-schema.md`, "Authoring branches".
|
|
115
119
|
|
|
116
120
|
### Step 6 — Stop at the gate (do NOT advance)
|
|
117
121
|
Report: the path to the discovery set, and that the next action is **review** via `yad-review-gate`
|
package/skills/yad-epic/SKILL.md
CHANGED
|
@@ -152,6 +152,10 @@ Notes:
|
|
|
152
152
|
- `test-cases` / `test-cases-review` are a **parallel, non-blocking track**: they seed `blocked` and open
|
|
153
153
|
when `stories-review` passes — at which point the epic is already `ready-for-build`, so the build half
|
|
154
154
|
runs alongside the tester. They never gate `ready-for-build` (see `references/state-schema.md`).
|
|
155
|
+
- Commit the seed on this step's authoring branch. It reaches the hub's default branch through the
|
|
156
|
+
epic's **first** review PR/MR — cut `review/EP-<slug>/epic` from the authoring branch so it carries
|
|
157
|
+
the seed. In bridge mode `ledger-guard` exempts a new epic's ledger (creation, not mutation, #162);
|
|
158
|
+
every later change to it is CI's. See `references/state-schema.md`, "Authoring branches".
|
|
155
159
|
- Also create an empty approvals ledger `{project-root}/epics/EP-<slug>/.sdlc/approvals.json`
|
|
156
160
|
and an empty comments ledger `{project-root}/epics/EP-<slug>/.sdlc/comments.json`, each containing
|
|
157
161
|
`[]`, and the `reviews/` directory. (`comments.json` is the machine-readable counterpart to the
|
|
@@ -94,6 +94,16 @@ The shared procedure (run once the `EP-<slug>` is known):
|
|
|
94
94
|
hub's default branch (`git checkout -b <step>/EP-<slug>`).
|
|
95
95
|
3. Author and commit the step's artifact(s) on that branch. The bridge's `review/…` branch is created
|
|
96
96
|
separately at review time and is untouched by this step.
|
|
97
|
+
|
|
98
|
+
**How the seed reaches the default branch.** The `.sdlc/` ledger is seeded once, by hand, on the
|
|
99
|
+
**entry** step's authoring branch (`analysis/…`, `epic/…`, `change/…`, `discovery/…`) — no CLI or CI
|
|
100
|
+
path creates one (`yad gate ci` only *advances* an existing chain, at merge, on the default branch).
|
|
101
|
+
So for the **first** gate of an epic, cut `review/EP-<slug>/<artifact-base>` from that authoring
|
|
102
|
+
branch: the review PR/MR then carries the seed alongside the artifact, and the ledger lands on the
|
|
103
|
+
default branch when it merges. In bridge mode `ledger-guard` exempts exactly this case — **creation,
|
|
104
|
+
not mutation** (#162) — so no direct push to a protected default branch is needed. For every **later**
|
|
105
|
+
gate the ledger is already on the default branch: cut the review branch from there, commit the
|
|
106
|
+
artifact only, and leave `.sdlc/{state,approvals,comments,hub-prs}.json` and `reviews/*.md` to CI.
|
|
97
107
|
| `type` | `author` \| `review+approve` | Authoring step or a team review gate. |
|
|
98
108
|
| `artifact` | filename or folder | The file/folder this step produces or gates. |
|
|
99
109
|
| `assistance` | `none` \| `review` \| `heavy` | Dial 1 — how much AI helps (build plan §2). |
|
|
@@ -52,7 +52,10 @@ each required domain-owner to a platform `login` via the roster (a roster `name`
|
|
|
52
52
|
### Step 2 — `open` (create the review PR/MR)
|
|
53
53
|
1. From the hub default branch, create `review/EP-<slug>/<artifact-base>` and ensure the artifact file
|
|
54
54
|
(and, for architecture, `contract.md` + `.sdlc/contract-lock.json`) is committed on it. Push as the
|
|
55
|
-
local user.
|
|
55
|
+
local user. **First gate of a new epic:** cut the review branch from the **authoring** branch
|
|
56
|
+
(`epic/…`, `change/…`, `analysis/…`, `discovery/…`) instead, so it carries the `.sdlc/` **seed** —
|
|
57
|
+
that PR/MR is the only way a new epic's ledger reaches the default branch, and `ledger-guard`
|
|
58
|
+
exempts it (see step 4).
|
|
56
59
|
2. Open the PR/MR with `gh`/`glab` using the hub body template (`yad-pr-template` `templates/hub/…`),
|
|
57
60
|
filled with the epic, artifact, gate step, owner, `epic.repos`, and the step's risk tags.
|
|
58
61
|
3. **Request the required reviewers** (their logins) and add a `domain:<repo>` label per touched repo so
|
|
@@ -68,7 +71,9 @@ each required domain-owner to a platform `login` via the roster (a roster `name`
|
|
|
68
71
|
A human commit touching the gate-state files (`.sdlc/{state,approvals,comments,hub-prs}.json` or
|
|
69
72
|
`reviews/*.md`; `.sdlc/contract-lock.json` is artifact-side and allowed) on a review PR is rejected
|
|
70
73
|
by the `ledger-guard` check. (The `yad gate open` CLI behaves the same: in bridge mode it opens the
|
|
71
|
-
PR only and writes no ledger.)
|
|
74
|
+
PR only and writes no ledger.) The **one** exception is a brand-new epic's **seed** — no CI path can
|
|
75
|
+
create a ledger, so an epic whose `.sdlc/state.json` is absent from the base ref may be created by a
|
|
76
|
+
human on this first PR/MR (#162). Every later change to it is CI's alone.
|
|
72
77
|
5. Report the PR/MR URL and the required reviewers. **Do not** record approvals or advance — reviewers
|
|
73
78
|
act on the platform; CI (`yad gate ci`) reconciles it onto the default branch at merge.
|
|
74
79
|
|
|
@@ -143,7 +148,8 @@ default branch. (File-only mode keeps `yad gate sync` as the local writer.)
|
|
|
143
148
|
- **Protect the hub default branch.** Require that `epics/**` artifacts change only through a review
|
|
144
149
|
PR/MR (branch protection). This keeps revoke-on-change sound: it removes the only window where a
|
|
145
150
|
delayed reconcile could advance on an out-of-band post-merge artifact change (see `references/bridge.md`,
|
|
146
|
-
"Known limitation").
|
|
151
|
+
"Known limitation"). Safe to require: a new epic's `.sdlc/` seed rides its first review PR/MR, so
|
|
152
|
+
nobody needs a direct push to the default branch to start an epic (#162).
|
|
147
153
|
- **Degrade gracefully.** No platform / disabled bridge / no CLI → the gate runs file-only with no error.
|
|
148
154
|
|
|
149
155
|
## Reference
|
|
@@ -155,7 +155,9 @@ During review CI writes nothing: the platform PR/MR is the source of truth (nati
|
|
|
155
155
|
threads). The CLI is self-sufficient at merge: it derives the epic + artifact from the
|
|
156
156
|
`review/EP-<slug>/<artifact-base>` head branch, takes the PR/MR number from the event (GitHub) or
|
|
157
157
|
resolves it from the platform (GitLab), upserts the `hub-prs.json` entry itself, and **re-reads
|
|
158
|
-
approvals fresh from the platform** — so no ledger needs to be pre-seeded on the branch.
|
|
158
|
+
approvals fresh from the platform** — so no ledger needs to be pre-seeded on the branch. (It only
|
|
159
|
+
*advances* a chain, though: it cannot **create** one. A brand-new epic's seed therefore travels the
|
|
160
|
+
other way — up through its first review PR/MR; see "the seed of a new epic" below.)
|
|
159
161
|
|
|
160
162
|
| Platform event | Phase | CI action |
|
|
161
163
|
|---|---|---|
|
|
@@ -174,13 +176,26 @@ commit — the advance plus the `draft → approved` status flip — lands on th
|
|
|
174
176
|
check (yad-checks) FAILs any commit on a review PR that touches `.sdlc/{state,approvals,comments,hub-prs}
|
|
175
177
|
.json` or `reviews/*.md` (`.sdlc/contract-lock.json` is artifact-side and allowed). Under Path B **no
|
|
176
178
|
CI commit lands in a review PR at all**, so the only ledger change the guard can see there is a human
|
|
177
|
-
edit — which it rejects
|
|
179
|
+
edit — which it rejects, with one carve-out for a new epic's seed (below). (The `verified-commits`
|
|
180
|
+
gate still vets every commit's signature + author;
|
|
178
181
|
its gate-bot exemption is now vestigial in-PR because CI no longer commits there.) `yad gate open`
|
|
179
182
|
opens the PR only; local `yad gate sync` is advisory in bridge mode (writes nothing). After a merge,
|
|
180
183
|
everyone `git checkout <default> && git pull`. (Without the bridge, humans own the ledger locally and
|
|
181
184
|
these guards are no-ops.)
|
|
182
185
|
|
|
183
|
-
**The one sanctioned human ledger write
|
|
186
|
+
**The one sanctioned human ledger write *in a review PR*: the seed of a new epic.** `gate ci` only
|
|
187
|
+
**advances** an existing chain — it bails on a missing `state.json`, and the engine reads that absence
|
|
188
|
+
as "not seeded yet" — so no CI path can ever create a ledger. The seed the authoring skills write
|
|
189
|
+
(`yad-epic`, `yad-change`, `yad-analysis`, `yad-discovery`, `yad-stub`) can reach the default branch
|
|
190
|
+
only through the epic's **first** review PR/MR, which is exactly where `ledger-guard` runs. So the gate
|
|
191
|
+
exempts **creation**: an epic whose `.sdlc/state.json` is absent from the PR's base ref may have its
|
|
192
|
+
ledger written by a human there (#162). It stays a narrow carve-out — the probe is against the base
|
|
193
|
+
ref, not the parent commit, so deleting an on-trunk `state.json` to "re-seed" it is itself a rejected
|
|
194
|
+
mutation, and the moment the ledger is on the default branch every further change is CI's alone. Cut
|
|
195
|
+
that first review branch from the authoring branch (`epic/…`, `change/…`) so it carries the seed; no
|
|
196
|
+
direct push to a protected default branch is needed.
|
|
197
|
+
|
|
198
|
+
**The one sanctioned human ledger write *on the default branch*: `yad gate repair`.** It heals a `YAD-STATE-005` chain (an
|
|
184
199
|
authoring step stranded behind a review gate that already advanced) by writing `state.json` alone. This
|
|
185
200
|
is not a `ledger-guard` gap: the repair commits to the **default branch**, where `ledger-guard` — which
|
|
186
201
|
only inspects review PRs — never runs, and where the `yad-update-guard` (platform-Verified signature +
|
|
@@ -176,7 +176,9 @@ PR only — against the `review/<epic>/<artifact>` branch, which must already ex
|
|
|
176
176
|
records this skill describes. The skill's
|
|
177
177
|
job is the human half: presenting the artifact, helping the owner address comments, and narrating the
|
|
178
178
|
gate. Local `yad gate sync` is advisory in bridge mode (reads the platform, prints status, writes
|
|
179
|
-
nothing); a human must never commit gate-state files (the `ledger-guard` check rejects it).
|
|
179
|
+
nothing); a human must never commit gate-state files (the `ledger-guard` check rejects it). The single
|
|
180
|
+
exception is an epic's **seed** — no CI path can create a ledger, so a brand-new epic's `.sdlc/` rides
|
|
181
|
+
its **first** review PR/MR, cut from the authoring branch (creation, not mutation, #162).
|
|
180
182
|
|
|
181
183
|
Under that CLI the gate **advances on merge**: a review PR/MR whose reviewer rule is satisfied, whose
|
|
182
184
|
comment threads are **all resolved**, and which has been **merged** auto-marks the step `done` and
|
package/skills/yad-stub/SKILL.md
CHANGED
|
@@ -115,6 +115,11 @@ into normal authoring with zero re-seeding.
|
|
|
115
115
|
Also create the empty ledgers `{.sdlc/approvals.json}` and `{.sdlc/comments.json}` (each `[]`) and the
|
|
116
116
|
`reviews/` directory. **Do NOT** write a `contract-lock.json` — a stub has no locked surface yet.
|
|
117
117
|
|
|
118
|
+
Commit the seed on this step's authoring branch; it reaches the hub's default branch through the
|
|
119
|
+
epic's **first** review PR/MR (or, for a stub, the PR that carries the stub itself). In bridge mode
|
|
120
|
+
`ledger-guard` exempts a new epic's ledger — creation, not mutation (#162) — while every later change
|
|
121
|
+
to it stays CI's. See `../yad-epic/references/state-schema.md`, "Authoring branches".
|
|
122
|
+
|
|
118
123
|
### Step 6 — Stop; hand off (NO auto-advance)
|
|
119
124
|
Report the new `EP-<slug>`, that it is a **stub (backfill pending)**, and the two next moves:
|
|
120
125
|
- **File bugs now:** `yad-change` (`--parent EP-<slug>`, `kind: defect|change`) — the defect threads off
|