yadflow 3.18.1 → 4.0.0-next.1

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.
Files changed (156) hide show
  1. package/CHANGELOG.md +355 -0
  2. package/README.md +79 -26
  3. package/bin/commands.mjs +41 -0
  4. package/bin/yad.mjs +437 -124
  5. package/cli/artifact-status.mjs +34 -15
  6. package/cli/checkpoint.mjs +69 -49
  7. package/cli/codeowners-command.mjs +170 -0
  8. package/cli/codeowners.mjs +397 -0
  9. package/cli/commit.mjs +13 -9
  10. package/cli/companion.mjs +2 -2
  11. package/cli/dial.mjs +183 -0
  12. package/cli/docs.mjs +88 -32
  13. package/cli/doctor.mjs +1472 -97
  14. package/cli/epic-state.mjs +3478 -232
  15. package/cli/epic.mjs +506 -0
  16. package/cli/errors.mjs +4 -1
  17. package/cli/gate.mjs +1002 -209
  18. package/cli/history.mjs +556 -0
  19. package/cli/hook.mjs +266 -55
  20. package/cli/hubcommit.mjs +6 -17
  21. package/cli/index-command.mjs +87 -0
  22. package/cli/ledger.mjs +57 -7
  23. package/cli/lib.mjs +184 -18
  24. package/cli/manifest.mjs +367 -56
  25. package/cli/migrate.mjs +726 -53
  26. package/cli/mode.mjs +170 -0
  27. package/cli/next.mjs +349 -90
  28. package/cli/openpr.mjs +191 -39
  29. package/cli/people.mjs +654 -0
  30. package/cli/plan.mjs +417 -132
  31. package/cli/platform.mjs +110 -129
  32. package/cli/product-index.mjs +287 -0
  33. package/cli/protection.mjs +706 -0
  34. package/cli/reconcile.mjs +38 -12
  35. package/cli/repo-publish.mjs +24 -26
  36. package/cli/repo.mjs +23 -14
  37. package/cli/report.mjs +21 -15
  38. package/cli/review.mjs +24 -27
  39. package/cli/riskmap-command.mjs +289 -0
  40. package/cli/riskmap.mjs +373 -0
  41. package/cli/setup.mjs +139 -287
  42. package/cli/ship.mjs +7 -6
  43. package/cli/skill.mjs +180 -0
  44. package/cli/skip.mjs +211 -30
  45. package/cli/thread.mjs +42 -17
  46. package/cli/tidy.mjs +20 -20
  47. package/cli/update-commit.mjs +22 -22
  48. package/cli/usage.mjs +115 -109
  49. package/package.json +3 -3
  50. package/skills/sdlc/config.yaml +166 -87
  51. package/skills/sdlc/module-help.csv +35 -35
  52. package/skills/yad-analysis/SKILL.md +125 -65
  53. package/skills/yad-architecture/SKILL.md +34 -23
  54. package/skills/yad-architecture/references/contract-format.md +10 -8
  55. package/skills/yad-backfill/SKILL.md +14 -8
  56. package/skills/yad-backfill/references/backfill.md +1 -1
  57. package/skills/yad-change/SKILL.md +127 -52
  58. package/skills/yad-change/references/triage.md +42 -28
  59. package/skills/yad-checks/SKILL.md +89 -45
  60. package/skills/yad-checks/references/check-gates.md +315 -92
  61. package/skills/yad-checks/templates/checks/build-test-lint.sh +25 -7
  62. package/skills/yad-checks/templates/checks/commit-message.sh +17 -3
  63. package/skills/yad-checks/templates/checks/contract-check.sh +58 -2
  64. package/skills/yad-checks/templates/checks/epic-open.sh +3 -3
  65. package/skills/yad-checks/templates/checks/install-deps.sh +46 -0
  66. package/skills/yad-checks/templates/checks/ledger-guard.sh +94 -18
  67. package/skills/yad-checks/templates/checks/lineage-check.sh +23 -9
  68. package/skills/yad-checks/templates/checks/package-manager.sh +140 -0
  69. package/skills/yad-checks/templates/checks/reconcile-debt-check.sh +4 -4
  70. package/skills/yad-checks/templates/checks/risk-map-check.sh +438 -0
  71. package/skills/yad-checks/templates/checks/verified-commits.sh +20 -46
  72. package/skills/yad-checks/templates/github/yad-checks.yml +37 -5
  73. package/skills/yad-checks/templates/github/yad-hub-checks.yml +5 -5
  74. package/skills/yad-checks/templates/github/yad-update-guard.yml +3 -4
  75. package/skills/yad-checks/templates/github/yad-verified-commits.yml +4 -4
  76. package/skills/yad-checks/templates/gitlab/.gitlab-ci.yml +7 -1
  77. package/skills/yad-checks/templates/gitlab/yad-checks.gitlab-ci.yml +22 -4
  78. package/skills/yad-checks/templates/gitlab/yad-hub-checks.gitlab-ci.yml +5 -5
  79. package/skills/yad-checks/templates/gitlab/yad-verified-commits.gitlab-ci.yml +4 -4
  80. package/skills/yad-checks/templates/hooks/ledger-guard-cursor.sh +91 -0
  81. package/skills/yad-checks/templates/hooks/ledger-guard.sh +38 -7
  82. package/skills/yad-commit/SKILL.md +6 -6
  83. package/skills/yad-connect-design/SKILL.md +6 -6
  84. package/skills/yad-connect-design/references/design-context.md +1 -1
  85. package/skills/yad-connect-design/references/design-registry.md +2 -2
  86. package/skills/yad-connect-docs/SKILL.md +12 -12
  87. package/skills/yad-connect-docs/references/docs-registry.md +1 -1
  88. package/skills/yad-connect-learning/SKILL.md +5 -5
  89. package/skills/yad-connect-learning/references/learning-registry.md +2 -2
  90. package/skills/yad-connect-repos/SKILL.md +92 -54
  91. package/skills/yad-connect-repos/references/code-context.md +6 -6
  92. package/skills/yad-connect-repos/references/hub-config.md +68 -58
  93. package/skills/yad-connect-repos/references/repos-registry.md +10 -9
  94. package/skills/yad-connect-repos/references/risk-map.md +81 -0
  95. package/skills/yad-connect-testing/SKILL.md +6 -6
  96. package/skills/yad-connect-testing/references/testing-context.md +3 -4
  97. package/skills/yad-connect-testing/references/testing-registry.md +2 -2
  98. package/skills/yad-defects/SKILL.md +8 -8
  99. package/skills/yad-discovery/SKILL.md +130 -94
  100. package/skills/yad-discovery/references/discovery-schema.md +23 -7
  101. package/skills/yad-discovery/references/foundation-schema.md +374 -0
  102. package/skills/yad-docs/SKILL.md +16 -11
  103. package/skills/yad-docs/references/data-mapping.md +9 -7
  104. package/skills/yad-docs/templates/app/package-lock.json +3 -3
  105. package/skills/yad-docs-overview/SKILL.md +32 -17
  106. package/skills/yad-docs-overview/references/pipeline-model.md +47 -28
  107. package/skills/yad-docs-sync/SKILL.md +10 -5
  108. package/skills/yad-docs-sync/references/staleness.md +8 -7
  109. package/skills/yad-engineer-review/SKILL.md +88 -24
  110. package/skills/yad-engineer-review/references/ship-and-record.md +25 -16
  111. package/skills/yad-epic/SKILL.md +178 -100
  112. package/skills/yad-epic/references/state-schema.md +626 -117
  113. package/skills/yad-hub-bridge/SKILL.md +66 -48
  114. package/skills/yad-hub-bridge/references/bridge.md +110 -83
  115. package/skills/yad-hub-bridge/references/login-roster.md +163 -70
  116. package/skills/yad-hub-bridge/templates/checks/hub-route.sh +22 -19
  117. package/skills/yad-hub-bridge/templates/github/yad-gate-sync.yml +34 -14
  118. package/skills/yad-hub-bridge/templates/gitlab/gitlab-ci.include-root.yml +2 -2
  119. package/skills/yad-hub-bridge/templates/gitlab/yad-gate-sync.gitlab-ci.yml +22 -12
  120. package/skills/yad-implement/SKILL.md +29 -15
  121. package/skills/yad-implement/references/implement-conventions.md +2 -2
  122. package/skills/yad-learn/SKILL.md +9 -9
  123. package/skills/yad-learn/references/learning-state.md +2 -2
  124. package/skills/yad-open-pr/SKILL.md +64 -29
  125. package/skills/yad-pair-review/SKILL.md +18 -16
  126. package/skills/yad-pair-review/references/session-state.md +4 -4
  127. package/skills/yad-pr-template/SKILL.md +48 -27
  128. package/skills/yad-pr-template/references/risk-routing.md +97 -24
  129. package/skills/yad-pr-template/templates/checks/pr-template.sh +37 -15
  130. package/skills/yad-pr-template/templates/checks/pr-title.sh +27 -13
  131. package/skills/yad-pr-template/templates/checks/risk-route.sh +107 -14
  132. package/skills/yad-pr-template/templates/github/pull_request_template.md +7 -5
  133. package/skills/yad-pr-template/templates/gitlab/merge_request_templates/Default.md +7 -5
  134. package/skills/yad-pr-template/templates/hub/github/pull_request_template.md +15 -14
  135. package/skills/yad-pr-template/templates/hub/gitlab/merge_request_templates/Default.md +15 -13
  136. package/skills/yad-reconcile/SKILL.md +3 -3
  137. package/skills/yad-report/SKILL.md +5 -5
  138. package/skills/yad-review-companion/SKILL.md +12 -9
  139. package/skills/yad-review-gate/SKILL.md +198 -79
  140. package/skills/yad-review-gate/references/gating.md +230 -54
  141. package/skills/yad-run/SKILL.md +86 -56
  142. package/skills/yad-run/references/run-loop.md +67 -45
  143. package/skills/yad-ship/SKILL.md +18 -14
  144. package/skills/yad-spec/SKILL.md +31 -17
  145. package/skills/yad-spec/references/spec-handoff.md +17 -5
  146. package/skills/yad-status/SKILL.md +114 -56
  147. package/skills/yad-stories/SKILL.md +42 -27
  148. package/skills/yad-stories/references/story-schema.md +10 -9
  149. package/skills/yad-stub/SKILL.md +59 -48
  150. package/skills/yad-sync-repos/SKILL.md +3 -3
  151. package/skills/yad-test-cases/SKILL.md +37 -30
  152. package/skills/yad-test-cases/references/test-cases-schema.md +8 -5
  153. package/skills/yad-timeline/SKILL.md +8 -7
  154. package/skills/yad-ui/SKILL.md +46 -25
  155. package/cli/roster.mjs +0 -164
  156. package/skills/sdlc/install.sh +0 -68
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: yad-review-gate
3
- description: 'The reusable team review + approve gate for the SDLC. Shares an authored artifact for review, records reviewer comments and approvals as files, enforces the owner + 1 reviewer rule (escalating to domain owners on contract/auth/payments), and advances the epic state ONLY when approval is recorded. Use when the user says "review the analysis/epic/architecture/UI/stories/test-cases", "comment", "approve", or "advance the gate".'
3
+ description: 'The reusable team review + approve gate for the SDLC. Shares an authored artifact for review, records reviewer comments and approvals as files, enforces the approver count (1 distinct approver; contract/auth/payments raise the full count, advisory), and advances the epic state ONLY when approval is recorded. Use when the user says "review the analysis/epic/architecture/UI/stories/test-cases", "comment", "approve", or "advance the gate".'
4
4
  ---
5
5
 
6
6
  # SDLC — Team Review Gate (build plan §3 piece 2, §4, §5)
@@ -8,18 +8,20 @@ description: 'The reusable team review + approve gate for the SDLC. Shares an au
8
8
  **Goal:** One reusable step type that turns any authored artifact into a gated, human-approved
9
9
  review. Every `review+approve` step in the workflow (the optional analysis, epic, architecture+contract,
10
10
  UI, stories, test-cases) uses this exact gate. **No step advances until its review is approved** and
11
- recorded as a file. The `analysis-review`, `epic`/`ui-design`, and `test-cases` reviews use the **base**
12
- rule (owner + 1 reviewer); escalation applies only where `risk_tags` or per-repo routing call for it.
11
+ recorded as a file. Every review uses the same **count** rule: distinct approvers, base 1. A step's
12
+ `risk_tags` raise the full count. The engine caps that count at the number of active people less one
13
+ and shows it (E72), but only the base holds the gate; the risk step is advisory.
13
14
 
14
- This gate is **swappable and file-driven**: it talks only through files. A front step advances only on a
15
- human act — recording an approval and `advance`, or (with the bridge) **merging the approved,
15
+ This gate is **swappable and file-driven**: it talks only through files. A Shape step advances only on a
16
+ human act — recording an approval and `advance`, or (with a verified ledger) **merging the approved,
16
17
  fully-resolved review PR/MR**. It works the same whether a human or the `yad gate` CLI triggers it — the
17
18
  trigger is a parameter, not a hardcoded human.
18
19
 
19
20
  ## Conventions
20
21
  - `{project-root}` resolves from the project working directory.
21
- - Operate on one epic: `{project-root}/epics/EP-<slug>/`.
22
- - State files: `.sdlc/state.json`, `.sdlc/approvals.json`, `.sdlc/comments.json`, and (when the bridge is
22
+ - Operate on one epic: `{project-root}/epics/EP-<slug>/`. The Product level is the exception:
23
+ `EP-foundation` lives at `{project-root}/foundation/` (its `.sdlc/` and `reviews/` are there).
24
+ - State files: `.sdlc/state.json`, `.sdlc/approvals.json`, `.sdlc/comments.json`, and (when the verified ledger is
23
25
  used) `.sdlc/hub-prs.json`. Review records: `reviews/`.
24
26
  - The artifact base name drops the extension (`epic.md` → `epic`; story `stories/...S01.md` → `stories-S01`).
25
27
 
@@ -27,8 +29,8 @@ trigger is a parameter, not a hardcoded human.
27
29
  - `epic`: the `EP-<slug>` to operate on.
28
30
  - `artifact`: the file under the epic being reviewed (e.g. `epic.md`).
29
31
  - `action`: one of `open` | `comment` | `approve` | `sync` | `advance` (default: `open`).
30
- - For `comment` / `approve`: the reviewer name and role (`owner` | `reviewer` | `domain-owner`),
31
- and for domain owners the `domain` (repo/area). Ask if not provided.
32
+ - For `comment` / `approve`: the reviewer's platform login (or the name they give, when there is no
33
+ platform). No role and no domain. Ask if not provided.
32
34
  - `sync` needs no reviewer input — it reads the platform PR/MR review state (via `yad-hub-bridge`).
33
35
 
34
36
  ## On Activation
@@ -36,46 +38,94 @@ trigger is a parameter, not a hardcoded human.
36
38
  ### Step 1 — Load state
37
39
  Read `.sdlc/state.json`. Find the `review+approve` step whose `artifact` matches the input (or the
38
40
  step named `currentStep` if it is a review step). Read `.sdlc/approvals.json`. Read `epic.md` for the
39
- epic's `repos` (the **touched domains**). Determine the **reviewer rule** for this step:
40
- - **Base rule:** `owner + 1 reviewer` — at least one `owner` approval AND at least one distinct
41
- non-owner `reviewer` approval.
42
- - **Escalation option (risk-driven):** if the step's `risk_tags` intersect `{contract, auth,
43
- payments}`, ALSO require at least one `domain-owner` approval **per touched domain** (build plan §4,
44
- §5). For the **architecture+contract** review (`risk_tags: ["contract"]`), the touched domains are
45
- the epic's `repos` — each repo's owner must sign off on the shared surface, so it escalates by
46
- default.
47
- - **Per-repo routing option (stories):** for the **stories** review, the relevant domain engineer
48
- reviews the stories touching their repo: treat each repo's engineer as a `domain-owner` for that
49
- repo's stories. The touched domains are the **union of every story's `repos`** under `stories/`
50
- (build plan §4 step 8). The `domain` field on each approval is the repo name.
51
-
52
- Escalation and per-repo routing are **options of this one gate**, selected by `risk_tags` and the
53
- touched `repos` — never a forked or copied gate.
41
+ epic's `repos` (the **touched domains**). Determine the **count** for this step. It counts people, not
42
+ roles — there are no roles, and yadflow keeps no list of people:
43
+ - **Base (enforced):** at least **1 distinct approver**, who should not be the author. The engine does
44
+ not check that: GitHub stops you approving your own PR, GitLab does only when the project's approval
45
+ settings say so, and on a local ledger nothing does — so do not record the author's own approval.
46
+ - **Full count, capped and reported (E72):** `needed = base 1 + risk step`. The risk step comes from the
47
+ step's `risk_tags`: `contract` +2, `auth`/`payments` +1 (the "high" tier). Take the highest tag, never
48
+ the sum. The engine then **caps** that ask at `active − 1`, with a floor of 1. `active` is the live
49
+ count of people who committed or approved lately (E71). Example: a contract gate asks 3; with 2 active
50
+ people the capped ask is 1, with 3 it is 2, with 4 or more all 3. The `− 1` is one seat left for the
51
+ author — a seat, not a check: the platform decides whether the author may approve.
52
+ - **The risk step is ADVISORY, capped or not.** Report the shortfall against the capped ask
53
+ (`short`); never block on it. Why: the count of people errs high in the normal case. A commit is
54
+ counted by its git name, an approval by its platform login, and yadflow never joins the two without
55
+ exact evidence — so a two-person team can read as four. At four the cap lowers nothing, and an enforced
56
+ contract gate would ask three approvals of a team with one person who is not the author. A later yadflow
57
+ change turns the capped count on together with `yad gate lower --reason`, the way out, once the count
58
+ is accurate.
59
+ - **Relay a warning line.** Under a team gate that has not passed, `yad gate status` and `yad gate sync`
60
+ print `! may not be met: …` (today's one required approval may have nobody but the author to give
61
+ it) or `! if the risk step were enforced: …` (a what-if about the extra approvals) when the count of
62
+ people suggests the gate may not pass (E73). Never block on it and never write it into the ledger. Tell the
63
+ reviewers while the review is open, because a merged review PR can no longer take approvals.
64
+ In solo mode, `yad gate status` prints `! solo mode is on, but …` instead when the count shows more than
65
+ one person may work on the Product (E74). Relay it as a suggestion to run `yad mode team`; never switch
66
+ the mode yourself, and never read it as a fault.
67
+ - **When the people cannot be counted** (`active: null` — for example Product CI, which checks out only
68
+ the hub, so the connected repos are not on disk): **no cap is computed or shown**, and the base holds as
69
+ always. `yad gate status` and `yad gate sync` print the arithmetic — read it from there rather than
70
+ recomputing it.
71
+ - **Touched domains** only name and label the review; they add no approvals. For a step with a risk
72
+ tag (the **architecture+contract** review) they are the epic's `repos`. For the **stories** review they
73
+ are the **union of every story's `repos`** under `stories/`. `stories-review` is an ordinary count gate.
74
+
75
+ This is **one gate** for every step — never a forked or copied gate. The tags change the full count
76
+ and nothing else.
54
77
 
55
78
  ### Step 2 — Dispatch on `action`
56
79
 
57
- > **Check the mode first — in bridge mode you write nothing to the ledger.** Read `.sdlc/hub.json`:
58
- > **bridge mode** is `platform` set AND `bridge_enabled` (or legacy `bridge`) `true`. Under the bridge
80
+ > **Check the mode first — in verified mode you write nothing to the ledger.** Read `.sdlc/hub.json`:
81
+ > **verified mode** is `platform` set AND `ledger: "verified"` — or, on a project that has not run `yad migrate` yet, `bridge_enabled` (or legacy `bridge`) `true`. `ledger` wins whenever it is present. Under the verified ledger
59
82
  > the ledger is CI-owned — `ledger-guard` rejects any non-bot commit touching
60
- > `epics/*/.sdlc/{state,approvals,comments,hub-prs}.json` or `epics/*/reviews/*.md`, local `yad gate
83
+ > `epics/*/.sdlc/{state,approvals,comments,product-prs,hub-prs}.json` or `epics/*/reviews/*.md` (and the
84
+ > same files under `foundation/`), local `yad gate
61
85
  > sync` is advisory, and `yad gate ci --merged` writes the whole transition when the review PR merges.
62
- > So every "set / append / write" instruction below is the **file-only, or a platform with no
63
- > gate-sync CI** path. In bridge mode do the human-facing half — present the artifact, route the
64
- > required reviewers, help the owner address comments — and let the platform PR/MR carry the review
86
+ > So every "set / append / write" instruction below is the **local, or a platform with no
87
+ > gate-sync CI** path. In verified mode do the human-facing half — present the artifact, say how many
88
+ > approvers the step needs, help the owner address comments — and let the platform PR/MR carry the review
65
89
  > state; the approvals, comments, review records and the advance all land through CI at merge.
66
90
 
67
- **`open`** — Present the artifact for review. Summarise what changed, list the required reviewers per
68
- the rule above, and tell reviewers how to comment/approve. Set the step `status` to `in_review` and
69
- `currentStep` to this step in `state.json` if not already. Do not advance.
91
+ **`open`** — Present the artifact for review. Summarise what changed, say how many approvers the step
92
+ needs (base, and the full count from its risk tags), and tell reviewers how to comment/approve. Then
93
+ make the transition with the engine:
70
94
 
71
- If `.sdlc/hub.json` has a non-null `platform` and `bridge_enabled: true` (or legacy `bridge: true` —
72
- `.sdlc/hub.json` is the only source the CLI reads, see `isBridge` in `cli/gate.mjs`), and `gh`/`glab`
73
- is authenticated, **also open a review PR/MR on the hub** by invoking `yad-hub-bridge action: open`
74
- (epic + artifact), and report the URL + required reviewers. **CI records the PR** in
75
- `epics/<epic>/.sdlc/hub-prs.json` (`{step, artifact, platform, number, url, branch, lastSyncedAt}`) —
76
- write that file yourself only on the file-only path. Otherwise (no platform / disabled / no CLI)
77
- proceed **file-only** exactly as before — no error. Opening the PR records no approvals and never
78
- advances.
95
+ ```bash
96
+ yad gate open <epic> <artifact>
97
+ ```
98
+
99
+ **Do not hand-write this one.** It does three things, and the second is the one a transcription keeps
100
+ forgetting:
101
+
102
+ 1. marks the review step `in_review`;
103
+ 2. **closes the paired authoring step as `done`** — a gate cannot open on an unauthored artifact, and
104
+ an author step left `in_progress` behind a passed gate blocks every step after it
105
+ (`YAD-STATE-005`, issue #131) until someone runs `yad gate repair`;
106
+ 3. moves `currentStep` to the gate — **except** when the epic is already `ready-for-build`, where it
107
+ leaves it alone, so opening the parallel `test-cases` gate never pulls the epic back from Build.
108
+
109
+ With **no platform** configured it writes the ledger and simply opens no PR, so this works offline.
110
+ With a platform, the `review/<epic>/<artifact>` branch must already be **on origin** — cut it from the
111
+ authoring branch and push it, or run `yad open-pr` from it, which pushes first and then delegates here.
112
+ In **verified mode** the command deliberately writes nothing: CI owns the ledger and performs the whole
113
+ transition at merge.
114
+
115
+ Do not advance.
116
+
117
+ If `.sdlc/hub.json` has a non-null `platform` and `gh`/`glab` is authenticated, **`yad gate open` also
118
+ opens the review PR/MR on the Product** (the recipe is `yad-hub-bridge action: open`; do not open a
119
+ second one), and you report the URL. This holds for a verified ledger and for a local one. The PR
120
+ requests **no reviewers**; the command prints
121
+ `no reviewers were requested — ask them on the PR itself`, so tell the author to ask people on the PR.
122
+ The PR is recorded in `epics/<epic>/.sdlc/hub-prs.json` (`{step, artifact, platform, number, url, branch, lastSyncedAt}`):
123
+ **by CI at merge** in verified mode (`ledger: "verified"`, or, before `yad migrate`, `bridge_enabled: true` /
124
+ legacy `bridge: true` — `.sdlc/hub.json` is the only source the CLI reads, see `isVerifiedLedger` in
125
+ `cli/manifest.mjs`), and by `yad gate open` itself on a local ledger. Never write it by hand (see `sync`
126
+ below). With no platform, or when the PR cannot be opened on a local ledger, the step is still
127
+ `in_review` locally — no error. (In verified mode a failed open writes nothing; open the PR by hand and
128
+ CI records the gate at merge.) Opening the PR records no approvals and never advances.
79
129
 
80
130
  **`comment`** — Capture reviewer feedback. Append/create a review file
81
131
  `reviews/<artifact-base>--<YYYY-MM-DD>--comments.md` with a heading per reviewer:
@@ -83,7 +133,7 @@ advances.
83
133
  ```markdown
84
134
  # Review comments — <artifact> — <YYYY-MM-DD>
85
135
 
86
- ## <reviewer> (<role>)
136
+ ## <reviewer>
87
137
  - <comment>
88
138
  - <comment>
89
139
  ```
@@ -92,40 +142,43 @@ Also append a **machine-readable** participation record to `.sdlc/comments.json`
92
142
  absent — the markdown stays the human-readable record, this makes commenter names queryable, the
93
143
  counterpart to `approvals.json`):
94
144
  ```json
95
- { "artifact": "<artifact>", "step": "<step id>", "commenter": "<name>", "role": "<owner|reviewer|domain-owner>", "domain": "<optional>", "round": <n>, "count": <comments this round>, "date": "<YYYY-MM-DD>" }
145
+ { "artifact": "<artifact>", "step": "<step id>", "commenter": "<platform login, or the name given with no platform>", "round": <n>, "count": <comments this round>, "date": "<YYYY-MM-DD>" }
96
146
  ```
147
+ Write no `role` or `domain`. Older records may carry them; nothing reads them.
97
148
  `round` increments each comment→address cycle for the artifact; upsert by `(step, commenter, round)`.
98
149
 
99
150
  Then help the **owner address the comments** using the agent lens listed for this step
100
151
  (analysis → `analyst`; epic → `pm`; architecture → `architect`; ui-design → `ux-designer`;
101
- stories → `pm`, with `architect` for technical detail — there is **no `sm` agent**, Phase 0
102
- Deviation 1; test-cases → `test architect` / Murat, `bmad-tea`). Update the authored artifact in place.
152
+ stories → `pm`, with `architect` for technical detail; test-cases → `test architect`). Update the authored artifact in place.
103
153
  Repeat comment→address rounds until reviewers are satisfied. **Commenting never advances the gate.**
104
154
 
105
155
  **`approve`** — Record an approval. Append to `.sdlc/approvals.json`:
106
156
  ```json
107
- { "artifact": "<artifact>", "step": "<step id>", "approver": "<name>", "role": "<owner|reviewer|domain-owner>", "domain": "<optional>", "status": "approved", "date": "<YYYY-MM-DD>", "engagement": "<verified|none>" }
157
+ { "artifact": "<artifact>", "step": "<step id>", "approver": "<platform login, or the name given with no platform>", "status": "approved", "date": "<YYYY-MM-DD>", "engagement": "<verified|none>" }
108
158
  ```
159
+ One record per person. Write no `role` or `domain`. The approver must not be the artifact's author.
160
+ A hand-written approval has no platform evidence and no `artifactHash`, so an edit to the artifact does not
161
+ revoke it — after a real change, remove it and record it again once the reviewer has seen the new content.
109
162
  `engagement` records whether the approval came through the [Review Companion](../yad-review-companion/SKILL.md)
110
163
  (a real trailer/cards/chat session = `verified`) or as a bare click (`none`). It is soft by default
111
164
  (both count; a bare approve draws a friendly nudge) and only gates when `hub.review.requireEngagement`
112
165
  is on — see `references/gating.md`. The signal is gameable by design ("visible, not impossible").
113
- Also write/refresh `reviews/<artifact-base>--<YYYY-MM-DD>--approved.md` as a **named roster** with three
166
+ Also write/refresh `reviews/<artifact-base>--<YYYY-MM-DD>--approved.md` as a **named record** with three
114
167
  sections, so every participant is attributable in one place:
115
168
 
116
169
  ```markdown
117
170
  # Approval record — <artifact> — <YYYY-MM-DD>
118
171
 
119
- Reviewer rule in force: **<base | escalated | per-repo>** (<why — e.g. risk_tags / touched repos>).
172
+ Count: **<have> distinct approver(s)** — <the sum, e.g. `3 approvers = base 1 + contract risk 2`>; <the engine's suffix, e.g. `capped to 1: 2 active people, less one seat for the author — base enforced, risk step advisory`, or just `base enforced, risk step advisory` when no cap lowered the ask; nothing after the sum when the step has no risk tag>[, short <N> — recorded here, never blocking].
120
173
 
121
174
  ## Approved by
122
- - <name> — <role>[ (<domain>)] — approved <date>
175
+ - <approver> — approved <date>[ (<source>)]
123
176
 
124
177
  ## Reviewed / commented by (participation, from comments.json)
125
- - <name> — <role> — <n> comment(s) across <r> round(s)
178
+ - <commenter> — <n> comment(s) across <r> round(s)
126
179
 
127
180
  ## Still required to pass the gate
128
- - <missing owner/reviewer/domain-owner, or "none">
181
+ - <"1 approval(s)", or "none">
129
182
 
130
183
  Gate status: **<PASSED | BLOCKED>** — <reason>.
131
184
  ```
@@ -133,22 +186,31 @@ Gate status: **<PASSED | BLOCKED>** — <reason>.
133
186
  Then **re-evaluate the rule** (Step 3). Recording an approval does NOT itself advance — advancement is
134
187
  a separate, explicit check.
135
188
 
136
- **`sync`** — (the platform bridge input path) Pull the hub review PR/MR's review state into the ledger,
189
+ **`sync`** — (the platform bridge input path) Pull the Product review PR/MR's review state into the ledger,
137
190
  then re-evaluate the rule (Step 3). Read the PR for this step from `.sdlc/hub-prs.json` and use
138
191
  `yad-hub-bridge`'s read recipes (`../yad-hub-bridge/references/bridge.md`) to fetch reviews + comments
139
192
  via the local user's `gh`/`glab`. For each:
140
- - map the platform `login` → SDLC `name` + `role` via `.sdlc/hub.json`'s roster (a roster `name` equal
141
- to a repo's `domain_owner` in `repos.json` becomes that repo's `domain-owner` for a touched domain;
142
- an unmapped login is a plain `reviewer`, flagged, never promoted);
193
+ - the platform `login` is the name written — `approver` / `commenter` is the login itself. There is no
194
+ lookup and no role (see `../yad-hub-bridge/references/login-roster.md`);
143
195
  - an `APPROVED` review / MR approval → append an `approved` record to `approvals.json` tagged
144
- `"source": "bridge"`; a `COMMENTED`/`CHANGES_REQUESTED`/note → write to
145
- `reviews/<artifact-base>--<YYYY-MM-DD>--comments.md` + `comments.json` (never an approval).
146
- **Idempotent:** upsert bridge approvals by `(step, approver, role, domain)`, supersede revoked ones
196
+ `"source": "bridge"`; a `CHANGES_REQUESTED` review or an unresolved thread → write to
197
+ `reviews/<artifact-base>--<YYYY-MM-DD>--comments.md` + `comments.json` (never an approval). A resolved
198
+ thread, and a companion comment marked `<!-- yad:noblock -->`, is not written.
199
+ **Idempotent:** upsert bridge approvals by `(step, approver)` — one record per person. An older record
200
+ that still carries `role`/`domain` and names a person by their old roster name is recognised as
201
+ `../yad-hub-bridge/references/login-roster.md` → "Older records" describes, and replaced by one
202
+ login-named record that keeps its fingerprint. Every sync write (`yad gate sync`, `yad gate ci`) also
203
+ records the login on the older records the roster can place for certain, on every step (same reference → "Recording the login on older records").
204
+ A bridge approval records the platform's evidence — `approvedAt`, and on GitHub `commit`, `url` and
205
+ `reviewId` — for the record only; the gate decides on the login and the fingerprint. Supersede revoked ones
147
206
  **while the step is open** (a step already `done` keeps its approvals — they are the record of why it
148
- passed), and key comments on the platform comment id (re-running `sync` does not duplicate). **Manual approvals (no
149
- `source` tag) are never touched.** For the architecture+contract step, discard bridge approvals dated
150
- before a new contract lock (re-lock invalidates platform approvals too). Then refresh the `approved.md`
151
- roster, set `hub-prs.json` `lastSyncedAt`, and **re-evaluate Step 3**. Under the PR-driven CLI (`yad
207
+ passed). Comment records are upserted by `(step, commenter, round)`: when the latest round already
208
+ has the same commenters and counts, it is rewritten in place, so re-running `sync` does not duplicate
209
+ it. **Manual approvals (no `source` tag) are never touched.** For the architecture+contract step, the
210
+ fingerprint a bridge approval is bound to is the contract surface, so a re-lock makes every approval of
211
+ the old surface stale: it stays on disk and no longer counts (re-lock invalidates platform approvals
212
+ too). Then refresh the `approved.md`
213
+ record, set the PR ledger's `lastSyncedAt`, and **re-evaluate Step 3**. **Never hand-write either PR-ledger file.** It lives under two names while the rename settles — `product-prs.json` and `hub-prs.json` — and the engine writes both together. Writing one leaves the pair disagreeing, and `yad doctor` will report it. Use `yad gate`, which keeps them in step. Under the PR-driven CLI (`yad
152
214
  gate sync`), `sync` advances the step when Step 3 passes on a **merged**, fully-resolved, approved PR
153
215
  (the merge is the human act); otherwise it records state and holds the step `in_review`.
154
216
 
@@ -156,9 +218,15 @@ gate sync`), `sync` advances the step when Step 3 passes on a **merged**, fully-
156
218
 
157
219
  ### Step 3 — Gate predicate (the only path that advances)
158
220
  The step may advance **iff ALL hold**:
159
- 1. `automation` is `human_approve` (it always is for front steps) and the required approvals exist:
160
- ≥1 `owner` AND ≥`review_gate.default_reviewers` (1) distinct non-owner `reviewer`, AND — if the
161
- step is escalated — ≥1 `domain-owner` for each touched domain.
221
+ 1. the advance dial is `human` (`automation: human_approve` — it always is for Shape steps) and the base
222
+ is met: **≥1 distinct approver** (counted by `approver`, so two records from one person are one).
223
+ If it is not, the missing line is `1 approval(s)`. **The risk step (Step 1) is NOT a condition
224
+ here, capped or not** — it is advisory. A step short of the capped ask still advances when
225
+ the base holds. Report the shortfall in the record; do not hold the step on it.
226
+ This matches `gatePredicate`, which returns `rule: "count"`, the count as `gateRule`/`have`/`short`,
227
+ `active`, and `cap` (null when `active` is null), and never puts the risk step in `missing`.
228
+ `active: null` means no source could be read, never "nobody". With `hub.review.requireEngagement`
229
+ on, only `verified` approvals count.
162
230
  2. The artifact has not changed since the latest approval round (no newer authored edit than the
163
231
  newest `approved` record). If it changed, approvals are stale → return to `comment`. For the
164
232
  **architecture+contract** review, also recompute the contract-surface hash (see
@@ -169,32 +237,82 @@ If the predicate **fails**: report exactly which approvals are still missing and
169
237
  `currentStep`.
170
238
 
171
239
  If the predicate **passes**:
172
- - Mark this review step `status: "done"`.
240
+
241
+ > **These rules are a TRANSCRIPTION of `advanceState`, and that is deliberate — it is the one
242
+ > transition with no engine verb behind it.** Everything else this skill does now calls the engine, and
243
+ > so do the authoring skills: `yad epic new` seeds a chain, `yad gate open` closes an authoring step and
244
+ > opens its gate. But there is no verb for *"an approval landed, advance the chain"* on a Product with
245
+ > **no platform**: `yad gate sync` and `yad gate ci` both return immediately without one, and
246
+ > `advanceState` — the function holding the rules below — has no other caller. So on a local-only
247
+ > Product these bullets ARE the engine's rules, written out. Keep them in step with `advanceState` in
248
+ > `cli/epic-state.mjs`, including the author-step close, until a local approve verb exists.
249
+ >
250
+ > With a platform, you do not perform them at all: `yad gate sync` (local ledger) or `yad gate ci`
251
+ > (verified) runs the same transition from that function.
252
+ >
253
+ > One other skill still writes a chain by hand, and it is not an oversight: `yad-backfill promote`
254
+ > rewrites one, and needs its own verb. (`yad-discovery` used to be one; since E75 it runs
255
+ > `yad foundation new`. `yad-change` used to seed a threaded chain by hand; since E42 it runs
256
+ > `yad epic new --parent`.)
257
+
258
+ - Mark this review step `status: "done"` **and give it a closing record** (E18):
259
+ `"closed": { "by": "<who performs this advance, or null>", "date": "<YYYY-MM-DD>", "via": "approved", "hash": "<the artifact hash the approvals bind to>" }`.
260
+ `approved`, not `merge`: nothing merged, so there is no `pr` or `commit` to write.
261
+ **In solo mode** (`solo: true` in `.sdlc/hub.json`, or the older `review_gate.solo: true`), add `"waived": "solo"` to that record: the gate passed
262
+ without counting approvals, and the record says so (E10). Only on the review step, never on its author step.
263
+ **In team mode, on a gate that counted approvals, when the cap lowered the ask** (E72), add
264
+ `"capped": { "needed": <full count>, "to": <capped ask>, "active": <people counted> }` — every cap is
265
+ recorded. Read `active` and the capped ask from `yad gate status` (its `active people:` line and the
266
+ step's `capped to N` suffix); write nothing when it prints `NOT COUNTED`. It records what the gate
267
+ ASKED, not what held it: the base held. `yad gate status` prints it back as
268
+ `count capped from 3 to 1 (2 active people)`. Absent in solo mode (nothing was counted), on a step that
269
+ passed by its skip or inherited shortcut (nothing was asked), and when the cap lowered nothing.
270
+ - **Close its paired authoring step if it is not `done` already.** `advanceState` does this defensively
271
+ (issue #131) because a passed gate can never leave its author step behind. Skipping it strands every
272
+ later step behind `YAD-STATE-005`. Give it `"closed": { "by": …, "date": …, "via": "review-passed" }`.
273
+ - **Never write a `closed` over one already on a step.** The first close wins.
173
274
  - **`stories-review`** is the end of the gating chain: set `currentStep: "ready-for-build"` (the Phase 3
174
275
  handoff sentinel; intentionally not a `steps[]` entry) **and** open the parallel **`test-cases`** track
175
- (set its step `blocked` → `in_progress`). The build half can now start **and** the tester can work
276
+ (if its step is `todo`, set it to `in_progress`). Build can now start **and** the tester can work
176
277
  `test-cases` at the same time.
177
278
  - **`test-cases-review`** is the parallel track's gate: mark it `done` but **leave `currentStep` at
178
- `ready-for-build`** — completing test cases must never pull the epic back from the build half.
179
- - Any **other** review step: set the next step in `steps[]` from `blocked` to `in_progress` (authoring)
180
- or `in_review`, and set `currentStep` to that next step.
279
+ `ready-for-build`** — completing test cases must never pull the epic back from Build.
280
+ - **`foundation-review`** (the Product level, `foundation/`) ends at its own sentinel: set
281
+ `currentStep: "foundation-done"`, never `ready-for-build` — the product level has no Build part. The
282
+ old spelling does the same: **`discovery-review`** sets `currentStep: "discovery-done"`.
283
+ - **On any review step that passes**, remove `"debt": true` from it, and from its author step once that
284
+ step is `done` — passing the review is what pays a debt back (E41), and nothing else clears the flag.
285
+ - A review step that passed **behind the chain** — a step re-opened with a late `yad undefer`, where
286
+ `currentStep` is already past it or is `ready-for-build` — changes nothing else: do not open the step
287
+ after it (that work is already finished) and do not move `currentStep`.
288
+ - Any **other** review step: find the next step in `steps[]` that has not already passed — not `skipped`,
289
+ `deferred`, `satisfied` (inherited from a parent epic) or `done` — set
290
+ it to `in_progress` (authoring) or `in_review` **only if it is `todo`**, and set `currentStep` to it. A
291
+ skipped step was marked N/A with `yad skip`, and a deferred one set aside for later with `yad defer`;
292
+ both stay as they are. A step already started or finished keeps its status.
293
+ If every later step is skipped or deferred, set `currentStep: "ready-for-build"`. A step waiting its turn is `todo` from shape 7 on. An older file may still say
294
+ `blocked` with no `record` on it, which means the same thing; a `blocked` step **with** a `record` is
295
+ waiting on someone outside the workflow, so leave it as it is.
296
+ - When a gate **opens** (the review starts), move `currentStep` to it only if it is not already past it:
297
+ opening the review of a re-opened step never pulls the chain back.
181
298
  - Write `state.json`. Report the advance and what the next authored artifact is (or that the epic is
182
299
  now `ready-for-build`, with `test-cases` running in parallel).
183
300
 
184
301
  ### PR-driven automation (the `yad gate` CLI)
185
- When the hub has a platform, **CI is the sole writer of the ledger**. `yad gate open` opens the review
302
+ When the Product has a platform and a **verified** ledger, **CI is the sole writer of the ledger**. (With a
303
+ platform and a local ledger, `yad gate open` and `yad gate sync` write it instead.) `yad gate open` opens the review
186
304
  PR only — against the `review/<epic>/<artifact>` branch, which must already exist (create it and run
187
305
  `yad open-pr` from it, which pushes it first). CI (`yad gate ci`) writes the `.sdlc/` + `reviews/`
188
306
  records this skill describes. The skill's
189
307
  job is the human half: presenting the artifact, helping the owner address comments, and narrating the
190
- gate. Local `yad gate sync` is advisory in bridge mode (reads the platform, prints status, writes
308
+ gate. Local `yad gate sync` is advisory in verified mode (reads the platform, prints status, writes
191
309
  nothing); a human must never commit gate-state files (the `ledger-guard` check rejects it, and the
192
310
  `hooks/ledger-guard.sh` harness hook refuses an agent the edit up front, naming `yad gate open`
193
311
  instead — see `yad-checks`). The single
194
312
  exception is an epic's **seed** — no CI path can create a ledger, so a brand-new epic's `.sdlc/` rides
195
313
  its **first** review PR/MR, cut from the authoring branch (creation, not mutation, #162).
196
314
 
197
- Under that CLI the gate **advances on merge**: a review PR/MR whose reviewer rule is satisfied, whose
315
+ Under that CLI the gate **advances on merge**: a review PR/MR whose base count is met, whose
198
316
  comment threads are **all resolved**, and which has been **merged** auto-marks the step `done` and
199
317
  unblocks the next step. (Until those three hold, the step stays `in_review`.)
200
318
 
@@ -210,22 +328,23 @@ platform PR/MR is the source of truth (native approvals + threads), and CI never
210
328
  branch (so an in-flight approval is never dismissed and required checks never strand). On the human
211
329
  **merge** CI re-reads approvals, advances the step, and flips the artifact `status:` on the **default
212
330
  branch**. After a merge, `git checkout <default> && git pull` to see it. The predicate and the human
213
- merge are unchanged — CI never approves and never merges. File-only mode (no platform) keeps the local
331
+ merge are unchanged — CI never approves and never merges. Local mode (no platform) keeps the local
214
332
  write path.
215
333
 
216
334
  ### Hard rules (build plan §1, §5)
217
- - **The merge click is the human approval act.** A front step advances only when a human merges the
335
+ - **The merge click is the human approval act.** A Shape step advances only when a human merges the
218
336
  approved, fully-resolved review PR — there is no machine-driven advance. A step `locked: true` may not
219
- be switched to `machine_advance`; refuse such a request.
337
+ be switched to `advance: auto`; refuse such a request.
220
338
  - **Approvals are revoked when the reviewed artifact changes.** `sync` re-hashes the artifact (the locked
221
- contract surface for architecture) and drops any approval bound to a stale hash, so a reviewer must
339
+ contract surface for architecture; every other file without its frontmatter `status:` line, which the
340
+ gate and Build rewrite after review) and drops any approval bound to a stale hash, so a reviewer must
222
341
  re-approve the new content. Unresolved comments / `CHANGES_REQUESTED` hold the gate `in_review`.
223
342
  - The gate talks only through `.sdlc/` and `reviews/` files — never hidden state.
224
343
  - **The platform is an input path only.** `open`/`sync` use the local user's own `gh`/`glab` (no stored
225
344
  tokens), and the **file ledger remains the source of truth** — the Step 3 predicate is unchanged
226
- whether approvals arrive manually or via `sync`. With no hub platform / no CLI, the gate runs file-only
345
+ whether approvals arrive manually or via `sync`. With no Product platform / no CLI, the gate runs local
227
346
  with no error (record approvals manually and `advance`).
228
347
 
229
348
  ## Reference
230
349
  - Gating details and worked example: `references/gating.md`.
231
- - The platform PR/MR bridge (`open`/`sync` mechanics, read recipes, roster): `../yad-hub-bridge/SKILL.md`.
350
+ - The platform PR/MR bridge (`open`/`sync` mechanics, read recipes, login attribution): `../yad-hub-bridge/SKILL.md`.