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,42 +1,46 @@
1
1
  ---
2
2
  name: yad-change
3
- description: 'Phase 6 post-lock change management — the change-request/defect INTAKE + TRIAGE step of a feature thread. After the contract locks and code ships, a change must NOT mutate a locked artifact; it becomes a NEW epic threaded to its parent. This skill classifies the change DEPTH (defect-fix / behavioral-no-surface / contract-surface / new-capability), seeds a new EP-<slug> change-epic threaded to its parent (lineage frontmatter kind/parent/thread/inherits/supersedes + a state.json whose inherited steps are pre-marked done and only the changed steps run; a pointer-lock contract-lock.json when architecture is inherited), and records the intake in change.json (escape_stage + root_cause for defects). For hotfixes it records the ship-first exception and opens reconcile-debt.json. Never auto-advances — hands off to the normal authoring skills + the team review gate. Use when the user says "log a change request", "file a defect", "thread a change off EP-…", "open a hotfix", or after a shipped feature needs a fix.'
3
+ description: 'Phase 6 post-lock change management — the change-request/defect INTAKE + TRIAGE step of a feature thread. After the contract locks and code ships, a change must NOT mutate a locked artifact; it becomes a NEW epic threaded to its parent. This skill classifies the change DEPTH (defect-fix / behavioral-no-surface / contract-surface / new-capability), seeds a new EP-<slug> change-epic threaded to its parent (lineage frontmatter kind+type/parent/thread/inherits/supersedes; then `yad epic new --parent` writes the state.json whose inherited steps are carried by reference and only the changed steps run, plus a pointer-lock contract-lock.json when architecture is inherited), and records the intake in change.json (escape_stage + root_cause for defects). For hotfixes it records the ship-first exception and opens reconcile-debt.json. Never auto-advances — hands off to the normal authoring skills + the team review gate. Use when the user says "log a change request", "file a defect", "thread a change off EP-…", "open a hotfix", or after a shipped feature needs a fix.'
4
4
  ---
5
5
 
6
6
  # SDLC — Change/Defect Intake + Triage (Phase 6, the entry of a feature thread)
7
7
 
8
8
  **Goal:** Turn a post-lock change request, defect, or hotfix into a **new epic threaded to its parent**,
9
9
  so the feature's locked artifacts are never mutated — only *superseded*. The change-epic **inherits**
10
- the front artifacts it does not change (by reference) and **re-authors** only the ones it does, so the
10
+ the Shape artifacts it does not change (by reference) and **re-authors** only the ones it does, so the
11
11
  thread head always describes current behaviour and the SDLC stays a trusted source of truth for the next
12
12
  change. This skill does the **intake + triage + seeding** and then hands off to the normal authoring
13
- skills + `yad-review-gate`. It is a **front state**: human-confirmed, **never auto-advances**.
13
+ skills + `yad-review-gate`. It is a **Shape step**: human-confirmed, **never auto-advances**.
14
14
 
15
- This is the answer to "the front/spec docs go stale after the contract locks": a behavioural change can
16
- no longer ship through the build half against an old story — `epic-open` seals a fully-shipped epic, so
15
+ This is the answer to "the Shape/spec docs go stale after the contract locks": a behavioural change can
16
+ no longer ship through Build against an old story — `epic-open` seals a fully-shipped epic, so
17
17
  new behaviour must enter here, and its re-authored stories/test-cases describe the change.
18
18
 
19
19
  ## Conventions
20
20
 
21
- - `{project-root}` resolves from the product hub.
21
+ - `{project-root}` resolves from the Product.
22
22
  - Artifacts live under `{project-root}/epics/EP-<slug>/` — the change-epic gets its OWN `EP-<slug>`
23
- (assigned here, never renamed) and its own `stories/EP-<slug>-S0N`, so every existing gate, the bridge,
24
- `yad next`, and the build-half traceability keep working unchanged.
23
+ (assigned here, never renamed) and its own `stories/EP-<slug>-S0N`, so every existing gate, the verified ledger,
24
+ `yad next`, and the Build traceability keep working unchanged.
25
25
  - The thread is **derived** from `parent:` frontmatter (no registry); `thread:` is a cache that must
26
26
  equal the computed root (`yad doctor` flags a mismatch). Thread id = the genesis epic's id.
27
27
  - Lineage frontmatter, the inherited-step shape, the pointer-lock, `change.json`, and
28
28
  `reconcile-debt.json` are all defined in `../yad-epic/references/state-schema.md` (Phase 6 section).
29
- - Genesis epics authored before Phase 6 must be **migrated once** (`kind: feature`, `thread: <self>` in
29
+ - Genesis epics authored before Phase 6 must be **migrated once** (`kind: feature` + `type: feature`, `thread: <self>` in
30
30
  their `epic.md`) before a change threads off them — see `references/triage.md`.
31
- - Speak in the configured `communication_language`; write documents in `document_output_language`.
31
+ - Speak in the `communication_language` set in `{project-root}/.sdlc/config.yaml`; write documents in `document_output_language`.
32
32
 
33
33
  ## Inputs
34
34
 
35
35
  - `parent` — **required.** The `EP-<slug>` this change evolves (the thread predecessor; usually the
36
36
  feature's current tip).
37
- - `title` — **required.** One line describing the change.
38
- - `kind` — `change` | `defect` | `hotfix` (default `change`). `feature` is reserved for a genesis epic
39
- (use `yad-epic`, not this skill).
37
+ - `title` — **required.** One line describing the change. It is written twice, with the same words:
38
+ as `title:` in `epic.md` (Step 4) and as `title` in `change.json` (Step 6).
39
+ - `kind` (also accepted as `type`) — the work-item **type**: `change` | `defect` | `hotfix`
40
+ (default `change`). Both input names mean the same thing, the same way both frontmatter keys do.
41
+ `feature` and
42
+ `chore` are the genesis types and are reserved for a work item with no parent (use `yad-epic`,
43
+ not this skill). Two names carry this value — see Step 4.
40
44
  - `origin` — defect/hotfix only: `production` | `staging` | `qa` | `review`.
41
45
  - `severity` — defect/hotfix only: `sev1`..`sev4`.
42
46
  - `escape_stage` — defect/hotfix only: the SDLC gate that *should* have caught it (`stories`,
@@ -51,12 +55,12 @@ new behaviour must enter here, and its re-authored stories/test-cases describe t
51
55
  Confirm `parent` exists (`epics/<parent>/epic.md` + `.sdlc/state.json`). Read its lineage and resolve
52
56
  the thread root (`yad thread <parent>` / `resolveThread`). **STOP** if the parent is missing, or its
53
57
  lineage is broken (a cycle, or a `thread` cache ≠ the computed root) — fix the parent first. If the
54
- parent is a **genesis epic not yet migrated** (no `kind:`), migrate it now: add `kind: feature` and
58
+ parent is a **genesis epic not yet migrated** (no type at all), migrate it now: add `kind: feature`, `type: feature` and
55
59
  `thread: <its own id>` to its `epic.md` (a one-line, non-gated frontmatter add).
56
60
 
57
61
  **Missing parent (brownfield) — never silent.** If the requested `parent` does **not exist at all**
58
62
  because the feature was built before it had an epic, do NOT dead-end: point the user at **`yad-stub`** to
59
- mint a stub genesis epic (a minimal `kind: feature` thread anchor) for that feature first, then re-run
63
+ mint a stub genesis epic (a minimal `feature`-type thread anchor) for that feature first, then re-run
60
64
  this skill with that stub as the `parent`. A change MUST still thread to a real parent — `yad-stub` just
61
65
  creates the smallest real one so the defect can be captured now (the `yad-reconcile` → anchor → change
62
66
  discipline).
@@ -67,7 +71,7 @@ in `references/triage.md`).
67
71
 
68
72
  ### Step 2 — Gather the change + triage the DEPTH (auto-propose, human-confirm)
69
73
  With the requester, classify the change into one **depth** (the `yad-backfill` discipline: auto-propose,
70
- human-confirm). The depth decides which front states are **re-authored** vs **inherited**:
74
+ human-confirm). The depth decides which Shape steps are **re-authored** vs **inherited**:
71
75
 
72
76
  | depth | re-authors (active) | inherits (pre-done, by reference) | first step |
73
77
  |-------|---------------------|-----------------------------------|-----------|
@@ -86,15 +90,37 @@ a word if needed). Create `{project-root}/epics/EP-<slug>/`. Open the `change/EP
86
90
  per the shared "Authoring branches" procedure (git/greenfield-safe).
87
91
 
88
92
  ### Step 4 — Write `epic.md` (the change brief + lineage frontmatter)
89
- Write a thin brief carrying the lineage frontmatter:
93
+ Write a thin brief carrying the lineage frontmatter.
94
+
95
+ **Write the work-item type under BOTH names, with the same value.** `kind:` is the original name and
96
+ is still the one every reader uses; `type:` is the name from shape 5 on. Writing `type:` alone is the
97
+ one mistake here that breaks something: `lineage-check.sh` runs inside the code repo and reads
98
+ `kind:`. Finding none it defaults to `feature`, decides the epic is a parent-free genesis, and stops
99
+ requiring the `parent:` a change, defect or hotfix must have. `yad doctor` FAILS on that.
100
+
101
+ **Carry the parent's `theme:` down.** A theme is the free grouping tag that puts several epics under
102
+ one heading (there is no rung above the Epic; grouping is a label). Read the parent's `epic.md`: if it
103
+ has a `theme:`, copy the spelling **exactly** onto this one, so the change groups with the work it
104
+ changes. If the parent has none, leave `theme:` empty. Never invent a new spelling of an existing
105
+ theme — `yad doctor` reports that, because two spellings group as two themes. Write the tag bare, with
106
+ no `#`: `yad next` and `yad thread` print it as `#checkout-revamp`, but that `#` is decoration on the
107
+ screen and is kept as part of the value if it lands in the file.
108
+
109
+ **Write `title:` in `epic.md` too**, with the same words as the `title` input. It is the name a list of
110
+ work items shows for this change (E111). Write it on ONE line, as plain words. If it begins with `[` and ends with `]` (such as `[Mobile] Checkout [v2]`), wrap the whole title in double quotes, writing an inner `"` as `\"` — otherwise it is read as a list, which is not a title. Never end it with a `#` comment (the comment becomes part of the title), and never use a YAML block (`>` or `|`): only one line is read. "The same words" means the title as it READS: quotes you add around it in `epic.md` are not part of it, so do not copy them into `change.json`. The full rules are the `title` row of the `epic.md` frontmatter table in `../yad-epic/references/state-schema.md`. Set it now — the
111
+ review gate is bound to a hash of the file, so a title changed after approval drops the approval.
112
+
90
113
 
91
114
  ```markdown
92
115
  ---
93
116
  id: EP-<slug>
117
+ title: <the title input, exactly as it goes into change.json>
94
118
  status: draft
95
119
  kind: <change|defect|hotfix>
120
+ type: <the same value as kind>
96
121
  parent: <EP-parent>
97
122
  thread: <EP-genesis>
123
+ theme: <the parent's theme, or leave empty>
98
124
  inherits: [<the inherited bases>]
99
125
  supersedes: [<parent story ids this replaces, optional>]
100
126
  owner:
@@ -110,7 +136,12 @@ root_cause: <tag>
110
136
  <!-- what is wrong / what must change, and why now -->
111
137
 
112
138
  ## Resolved current truth (input)
113
- <!-- run `yad thread <parent>`: which epic currently owns each artifact this change builds on -->
139
+ <!-- run `yad thread <parent>`: which epic currently owns each artifact this change builds on.
140
+ Run `yad doctor` FIRST and resolve anything it reports about the parent's ledger. That map is
141
+ built from each epic's `state.json`, and a ledger the engine cannot parse falls back to "this
142
+ epic owns everything" — the safe answer for a read-only display, and the wrong one to copy into
143
+ an `inheritedFrom`, which is a provenance record. An artifact shown as `(none)` has no owner:
144
+ the parent's route never produced it, so there is nothing to inherit and nothing to point at. -->
114
145
 
115
146
  ## Re-authored vs inherited
116
147
  <!-- the Step 2 split, for the reviewers -->
@@ -119,38 +150,61 @@ root_cause: <tag>
119
150
  For a **contract-surface** depth, do NOT inherit `architecture` — it will be re-authored (and re-locked)
120
151
  by `yad-architecture` downstream.
121
152
 
122
- ### Step 5 — Seed `state.json` (inherited steps pre-done; only the changed steps run)
123
- Create `.sdlc/state.json` with the **same 10-step chain** as `yad-epic` (so `advanceState`/`nextAction`/
124
- `gatePredicate`/the bridge run unchanged), but:
125
- - **Inherited** authoring steps **and their review gates**: `status: "done"`, `"inherited": true`,
126
- `"inheritedFrom": "<owning epic from the resolved truth>"`, `"boundHash": "<that artifact's current
127
- hash>"`.
128
- - The **first re-authored** authoring step: `status: "in_progress"`; its review: `status: "in_review"`
129
- only once authored — seed it `blocked` and let the authoring skill open it. Set `currentStep` to the
130
- first re-authored authoring step.
131
- - Remaining re-authored steps: `blocked`.
132
-
133
- Seed `.sdlc/approvals.json` with one **provenance** record per inherited gate (NOT a forged approval):
134
- `{ "artifact": "<art>", "step": "<…-review>", "status": "inherited", "from": "<epic>", "boundHash": "<hash>", "date": "<today>" }`.
135
- Seed `.sdlc/comments.json` = `[]` and create `reviews/`.
136
-
137
- Commit the seed on the `change/EP-<slug>` branch. It reaches the hub's default branch through this
153
+ ### Step 5 — Seed the ledger with the engine (inherited steps carried; only the changed steps run)
154
+ Run it from the Product, after Step 4 has written `epic.md`:
155
+
156
+ ```bash
157
+ yad epic new EP-<slug> --type <change|defect|hotfix> --parent <EP-parent> --inherits <bases>
158
+ ```
159
+
160
+ `<bases>` is the Step 2 inherit list, comma-separated — for a defect-fix off a `classic` parent,
161
+ `epic,architecture,contract,ui-design`. It must be the same list as `inherits:` in `epic.md`: the command
162
+ reads that header, takes its answer when a flag is left off, and refuses a flag that contradicts it. Leave
163
+ `--profile` off. The child takes the **parent's** route — a 4-step `chore` parent gives a 4-step child —
164
+ and the command refuses a route that differs.
165
+
166
+ The engine writes everything this step used to spell out by hand (E42):
167
+
168
+ - `.sdlc/state.json` — the parent's chain and route. Every inherited step **and its review gate** is
169
+ `satisfied`, with `inherited: true`, `inheritedFrom` (the epic along the parent's line that owns the
170
+ artifact — not always the parent), `boundHash` (that artifact's current hash) and a `record`. The first
171
+ re-authored step is `in_progress` and is the `currentStep`; the rest are `todo`.
172
+ - `.sdlc/approvals.json` — one **provenance** record per inherited gate
173
+ (`{ artifact, step, status: "inherited", from, boundHash, date }`) — never a forged approval.
174
+ - `.sdlc/comments.json` = `[]` and `reviews/`.
175
+ - `.sdlc/contract-lock.json` — the **pointer-lock**, only when `architecture` and `contract` are inherited:
176
+ the owner's hash copied verbatim, with `inheritedFrom` and `ref`. There is no `contract.md` in the
177
+ change-epic, so the surface cannot drift, and `contract-check` passes unchanged because the hash is
178
+ identical. (Field shapes: `references/triage.md`.)
179
+
180
+ **A refusal means the triage is wrong, and nothing is written.** Fix the inherit list and run it again:
181
+
182
+ | The command says | Why | What to do |
183
+ |---|---|---|
184
+ | `<owner> skipped <step>` | a skip is a decision, not an artifact — carrying it would claim a review nobody did | leave that base out of `inherits`, then `yad skip` it on this epic if it does not apply here either |
185
+ | `<owner> deferred <step>` | that work is still owed | leave the base out and author it on this epic, so the owed work follows the thread |
186
+ | `<owner> has not finished <step>` | nothing approved exists yet | finish it on the owner, or author it here |
187
+ | `holds no usable contract lock` / `surface no longer matches its lock` | you may skip authoring a contract, never having one | lock (or re-lock) the owner's surface first (`yad-architecture` Step 5) |
188
+ | `stories cannot be inherited` / `test-cases cannot be inherited` | every change writes its own; `stories-review` is what hands an epic to Build | leave them out |
189
+ | `architecture and contract are inherited together` | the architecture step writes `contract.md` | list both, or neither (a contract-surface change) |
190
+ | `route has no <base> step` | a short-lane parent never had it | leave it out |
191
+ | `architecture is owned by <A> but the contract by <B>` | one step writes both, so one epic must own both | in the epic between them, list both in `inherits:`, or neither |
192
+ | `ledger carries <step> from <epic>, but its epic.md does not list <base>` | that epic's two records disagree about where the artifact lives | fix that epic's `inherits:` first |
193
+ | `epic.md says …, --parent says …` · `--inherits says …` · `thread: …` · `has no parent:` / `has no thread:` · `write it as inherits: [a, b]` | the header is what `yad thread` reads, so it must say the same as the flags — in brackets for a list | fix `epic.md` (Step 4), or drop the flag |
194
+ | `already has a contract-lock.json` · `already has an approvals.json with records in it` | a file already there is somebody's record | remove it only if it is left over from a mistake |
195
+
196
+ Commit the seed on the `change/EP-<slug>` branch. It reaches the Product's default branch through this
138
197
  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,
198
+ it carries the seed. In verified mode `ledger-guard` exempts a new epic's ledger (creation, not mutation,
140
199
  #162), so no direct push to a protected default branch is needed; every later change to that ledger is
141
200
  CI's. See `../yad-epic/references/state-schema.md`, "Authoring branches".
142
201
 
143
- When `architecture` is **inherited**, materialize the **pointer-lock** `.sdlc/contract-lock.json`:
144
- `{ "artifact": "contract.md", "hash": "<parent surface hash, verbatim>", "lockedAt": "<today>", "inheritedFrom": "<epic>", "ref": "../../<epic>/.sdlc/contract-lock.json" }`.
145
- There is no `contract.md` in the change-epic, so the surface cannot drift, and `contract-check` passes
146
- unchanged because the hash is identical. (Exact recipe + field shapes: `references/triage.md`.)
147
-
148
202
  **Stub parent (brownfield, no locked surface yet).** When the `parent` is a **stub**
149
203
  (`stub: backfill-pending` / `verified: false`, minted by `yad-stub`), it has no `architecture.md` /
150
- `contract.md` / `ui-design.md` to inherit — the surface has not been documented yet. So for the bases the
151
- stub lacks, mark the inherited steps `"inherited": true, "inheritedFrom": "<stub>", "boundHash": null`
204
+ `contract.md` / `ui-design.md` to inherit — the surface has not been documented yet. `yad epic new`
205
+ sees the anchor and carries those bases as `"inherited": true, "inheritedFrom": "<stub>", "boundHash": null`
152
206
  (a `null` boundHash is treated as "nothing locked upstream → no drift" by the gate predicate, so the step
153
- passes and never blocks), and write **NO** `contract-lock.json` (there is no surface to point at — the
207
+ passes and never blocks), and it writes **NO** `contract-lock.json` (there is no surface to point at — the
154
208
  child never touches `specs/*/contracts/**`, so `contract-check` passes trivially). Contract protection on
155
209
  this thread begins only after `yad-backfill promote` documents and locks the feature. Record
156
210
  `"parentStub": true` in `change.json` so it is auditable that the change threaded off an undocumented
@@ -158,35 +212,56 @@ stub. Prefer the `defect-fix` / `behavioral-no-surface` depth against a stub —
158
212
  change against an undocumented feature should wait until the stub is promoted (backfilled + a real
159
213
  contract locked).
160
214
 
215
+ **Short-lane parent (no architecture step ever existed).** When the parent records `profile: "chore"`
216
+ or `"spike"`, it has no `architecture.md`, no `contract.md`, no `ui-design.md` and no lock — not
217
+ because they are undocumented, as with a stub, but because **its route never had those steps**. The
218
+ engine already handles the ledger: it seeds the parent's short chain and route, writes no lock, and
219
+ refuses `architecture`, `contract` or `ui-design` in `inherits`, because inheriting a step the parent
220
+ never walked would stamp `inherited: true` on a review that never happened anywhere. What is left for
221
+ this skill:
222
+
223
+ - **Record `"parentShortLane": "<the parent's profile>"` in `change.json`**, the same way a stub parent
224
+ records `"parentStub": true`, so it is auditable that this thread inherited from a route with no
225
+ locked surface.
226
+ - **A `contract-surface` depth is not available here.** Its first runnable step is `architecture`,
227
+ which this chain does not have. If the change really does move the shared cross-repo surface, it is
228
+ not a thread off this parent — seed a **new epic on `classic`** and note the parent in its lineage.
229
+ Prefer `defect-fix` or `behavioral-no-surface`, exactly as against a stub.
230
+
161
231
  ### Step 6 — Write `.sdlc/change.json` (intake + triage record)
162
- Record the intake: `epicId`, `thread`, `parent`, `kind`, `depth`, `intakeBy`, `intakeDate`, `title`,
232
+ Record the intake: `epicId`, `thread`, `parent`, `kind` **and `type`** (the same value under both
233
+ names, as in `epic.md`), `depth`, `intakeBy`, `intakeDate`, `title`,
163
234
  `description`, `affectedArtifacts`, `reauthors`, `inherits`, and for a defect/hotfix the `defect` block
164
235
  (`origin`, `severity`, `escape_stage`, `root_cause`). This is what `yad-defects` reads to attribute the
165
236
  defect to the gate that should have caught it. Add `"parentStub": true` when the parent is an
166
237
  un-promoted stub (Step 5) — omit it (or `false`) otherwise.
167
238
 
168
239
  ### Step 7 — Hotfix only: record the ship-first exception + open reconcile debt
169
- If `kind: hotfix`, the build half MAY run before these front gates approve (severity demands it). Record
240
+ If the type is `hotfix`, Build MAY run before these Shape gates approve (severity demands it). Record
170
241
  `hotfix: { "shipFirst": true }` in `change.json` and **append** to `.sdlc/reconcile-debt.json`:
171
242
  `{ "thread": "<…>", "epicId": "<…>", "openedDate": "<today>", "reason": "<why>", "requires": ["artifacts-updated","regression-test"], "status": "open", "paidDate": null, "paidBy": null, "evidence": { "artifacts": [], "regressionTest": "" } }`.
172
243
  Tell the user the debt **freezes the next normal change** on this thread (`reconcile-debt` gate) until it
173
- is paid (front artifacts updated **and** a regression test added, then `status: "paid"`).
244
+ is paid (Shape artifacts updated **and** a regression test added, then `status: "paid"`).
174
245
 
175
246
  ### Step 8 — Stop; hand off (NO auto-advance)
176
247
  Report: the new `EP-<slug>`, its thread + parent, the re-author-vs-inherit split, the seeded
177
- `currentStep`, and the next skill — `yad-architecture` (contract-surface), else `yad-stories` /
178
- `yad-test-cases` — followed by `yad-review-gate`. Front states do not auto-advance. Suggest
248
+ `currentStep`, and the next skill — run `yad next EP-<slug>` and name what it names, followed by
249
+ `yad-review-gate`. Do NOT name a skill from memory: which skill runs a step is the project's setting
250
+ (`.sdlc/skills.json`), so a team that bound its own would be sent to one this epic will never use
251
+ again. Unbound, that command names `yad-architecture` for a contract-surface change and `yad-stories`
252
+ / `yad-test-cases` otherwise, which is what this step used to say. Shape steps do not auto-advance. Suggest
179
253
  `yad thread <thread>` to see the evolution and `yad-timeline` / `yad-defects` to render it.
180
254
 
181
255
  ## Hard rules
182
256
 
183
257
  - **Never mutate a locked artifact.** A change is a new threaded epic, not an edit to a shipped one.
184
258
  - **A change MUST thread to a real parent.** Validate the parent + thread first; STOP on a broken lineage.
185
- - **Inherit by reference, never copy.** Inherited steps are pre-done with `inherited: true` + a
186
- `boundHash`; the pointer-lock carries the parent hash verbatim. The gate never re-reviews them.
259
+ - **Inherit by reference, never copy.** `yad epic new --parent` carries inherited steps as `satisfied` with
260
+ `inherited: true` + a `boundHash`; the pointer-lock carries the owner's hash verbatim. The gate never
261
+ re-reviews them, and only work written AND approved upstream can be carried.
187
262
  - **Contract-surface ⇒ re-author architecture.** Omitting `architecture` from `inherits` is the ONLY way
188
- to change the surface; it re-locks (new hash) and routes through the escalated architecture review —
189
- the same mechanism as the build-half `Contract-Change` route, unified.
263
+ to change the surface; it re-locks (new hash) and routes through the architecture review again (its `contract` tag raises the full approver count, which stays advisory — E72 reports it capped at the active people less one) —
264
+ the same mechanism as the Build `Contract-Change` route, unified.
190
265
  - **A hotfix opens debt, never waives it.** Ship-first is allowed once; the thread freezes for new work
191
266
  until the debt is paid.
192
267
  - **Never auto-advances.** This skill seeds + records; humans author and approve via the normal gates.
@@ -1,4 +1,4 @@
1
- # Change triage — depth, seeding, the pointer-lock, migration, concurrency
1
+ # Change triage — depth, what the seed writes, the pointer-lock, migration, concurrency
2
2
 
3
3
  This is the detail behind `yad-change`. The lineage frontmatter + ledger schemas live in
4
4
  `../../yad-epic/references/state-schema.md` (Phase 6 section); this file is the *how*.
@@ -21,35 +21,45 @@ enum/identifier in the genesis (or current-truth) `contract.md` `CONTRACT-SURFAC
21
21
  contract-surface. If it only changes internal behaviour, validation, or a repo-private field → not the
22
22
  surface.
23
23
 
24
- ## The seeded `state.json` (worked shape)
24
+ ## What `yad epic new --parent` writes (worked shape)
25
25
 
26
- Same 10 steps as `yad-epic`. For a **defect-fix** that inherits epic/architecture/ui-design and
27
- re-authors stories+test-cases:
26
+ The engine writes this (E42); it is shown so the author and the reviewers know what to expect, and a
27
+ test keeps it equal to what the engine really writes. The child's chain is always the PARENT's chain and
28
+ `profile`: 10 steps under a `classic` parent, 4 under a `chore` one (no `architecture`/`ui-design` rows to
29
+ inherit and no pointer-lock — see "Short-lane parent" in `../SKILL.md` Step 5).
30
+
31
+ For a **defect-fix** off a `classic` parent, inheriting epic/architecture/contract/ui-design and re-authoring
32
+ stories+test-cases:
28
33
 
29
34
  ```json
30
35
  {
31
- "schemaVersion": 1,
32
- "epicId": "EP-<slug>", "createdAt": "<today>", "currentStep": "stories",
36
+ "schemaVersion": 10,
37
+ "epicId": "EP-<slug>", "createdAt": "<today>", "type": "<the same value as epic.md kind/type>",
38
+ "profile": "classic",
39
+ "currentStep": "stories",
33
40
  "steps": [
34
- { "id": "epic", "type": "author", "artifact": "epic.md", "assistance": "review", "automation": "human_approve", "locked": true, "status": "done", "inherited": true, "inheritedFrom": "EP-<genesis>", "boundHash": "sha256:…", "risk_tags": [] },
35
- { "id": "epic-review", "type": "review+approve", "artifact": "epic.md", "assistance": "review", "automation": "human_approve", "locked": true, "status": "done", "inherited": true, "inheritedFrom": "EP-<genesis>", "boundHash": "sha256:…", "risk_tags": [] },
36
- { "id": "architecture", "type": "author", "artifact": "architecture.md", "assistance": "review", "automation": "human_approve", "locked": true, "status": "done", "inherited": true, "inheritedFrom": "EP-<genesis>", "boundHash": "sha256:…", "risk_tags": [] },
37
- { "id": "architecture-review", "type": "review+approve", "artifact": "architecture.md", "assistance": "review", "automation": "human_approve", "locked": true, "status": "done", "inherited": true, "inheritedFrom": "EP-<genesis>", "boundHash": "sha256:…", "risk_tags": ["contract"] },
38
- { "id": "ui-design", "type": "author", "artifact": "ui-design.md", "assistance": "review", "automation": "human_approve", "locked": true, "status": "done", "inherited": true, "inheritedFrom": "EP-<genesis>", "boundHash": "sha256:…", "risk_tags": [] },
39
- { "id": "ui-design-review", "type": "review+approve", "artifact": "ui-design.md", "assistance": "review", "automation": "human_approve", "locked": true, "status": "done", "inherited": true, "inheritedFrom": "EP-<genesis>", "boundHash": "sha256:…", "risk_tags": [] },
40
- { "id": "stories", "type": "author", "artifact": "stories/", "assistance": "review", "automation": "human_approve", "locked": true, "status": "in_progress", "risk_tags": [] },
41
- { "id": "stories-review", "type": "review+approve", "artifact": "stories/", "assistance": "review", "automation": "human_approve", "locked": true, "status": "blocked", "risk_tags": [] },
42
- { "id": "test-cases", "type": "author", "artifact": "test-cases.md", "assistance": "review", "automation": "human_approve", "locked": true, "status": "blocked", "risk_tags": [] },
43
- { "id": "test-cases-review", "type": "review+approve", "artifact": "test-cases.md", "assistance": "review", "automation": "human_approve", "locked": true, "status": "blocked", "risk_tags": [] }
41
+ { "id": "epic", "type": "author", "artifact": "epic.md", "assistance": "review", "driver": "pair", "automation": "human_approve", "advance": "human", "locked": true, "status": "satisfied", "inherited": true, "inheritedFrom": "EP-<genesis>", "boundHash": "sha256:…", "record": { "reason": "carried by reference from EP-<genesis>", "by": null, "date": "<today>", "link": "EP-<genesis>" }, "risk_tags": [] },
42
+ { "id": "epic-review", "type": "review+approve", "artifact": "epic.md", "assistance": "review", "driver": "pair", "automation": "human_approve", "advance": "human", "locked": true, "status": "satisfied", "inherited": true, "inheritedFrom": "EP-<genesis>", "boundHash": "sha256:…", "record": { "reason": "carried by reference from EP-<genesis>", "by": null, "date": "<today>", "link": "EP-<genesis>" }, "risk_tags": [] },
43
+ { "id": "architecture", "type": "author", "artifact": "architecture.md", "assistance": "review", "driver": "pair", "automation": "human_approve", "advance": "human", "locked": true, "status": "satisfied", "inherited": true, "inheritedFrom": "EP-<genesis>", "boundHash": "sha256:…", "record": { "reason": "carried by reference from EP-<genesis>", "by": null, "date": "<today>", "link": "EP-<genesis>" }, "risk_tags": [] },
44
+ { "id": "architecture-review", "type": "review+approve", "artifact": "architecture.md", "assistance": "review", "driver": "pair", "automation": "human_approve", "advance": "human", "locked": true, "status": "satisfied", "inherited": true, "inheritedFrom": "EP-<genesis>", "boundHash": "sha256:…", "record": { "reason": "carried by reference from EP-<genesis>", "by": null, "date": "<today>", "link": "EP-<genesis>" }, "risk_tags": ["contract"] },
45
+ { "id": "ui-design", "type": "author", "artifact": "ui-design.md", "assistance": "review", "driver": "pair", "automation": "human_approve", "advance": "human", "locked": true, "status": "satisfied", "inherited": true, "inheritedFrom": "EP-<genesis>", "boundHash": "sha256:…", "record": { "reason": "carried by reference from EP-<genesis>", "by": null, "date": "<today>", "link": "EP-<genesis>" }, "risk_tags": [] },
46
+ { "id": "ui-design-review", "type": "review+approve", "artifact": "ui-design.md", "assistance": "review", "driver": "pair", "automation": "human_approve", "advance": "human", "locked": true, "status": "satisfied", "inherited": true, "inheritedFrom": "EP-<genesis>", "boundHash": "sha256:…", "record": { "reason": "carried by reference from EP-<genesis>", "by": null, "date": "<today>", "link": "EP-<genesis>" }, "risk_tags": [] },
47
+ { "id": "stories", "type": "author", "artifact": "stories/", "assistance": "review", "driver": "pair", "automation": "human_approve", "advance": "human", "locked": true, "status": "in_progress", "risk_tags": [] },
48
+ { "id": "stories-review", "type": "review+approve", "artifact": "stories/", "assistance": "review", "driver": "pair", "automation": "human_approve", "advance": "human", "locked": true, "status": "todo", "risk_tags": [] },
49
+ { "id": "test-cases", "type": "author", "artifact": "test-cases.md", "assistance": "review", "driver": "pair", "automation": "human_approve", "advance": "human", "locked": true, "status": "todo", "risk_tags": [] },
50
+ { "id": "test-cases-review", "type": "review+approve", "artifact": "test-cases.md", "assistance": "review", "driver": "pair", "automation": "human_approve", "advance": "human", "locked": true, "status": "todo", "risk_tags": [] }
44
51
  ]
45
52
  }
46
53
  ```
47
54
 
48
- `boundHash` is the inherited artifact's **current** hash from the owning epic — the same hashes
55
+ `inheritedFrom` is the epic that owns the artifact along the parent's line — the latest one that
56
+ re-authored it, which is not always the parent. `boundHash` is the inherited artifact's **current** hash from the owning epic — the same hashes
49
57
  `cli/epic-state.mjs` computes: `contractSurfaceHash` for `architecture`, `storiesHash` for `stories`,
50
- the file bytes otherwise. The gate predicate treats an `inherited` step as satisfied (never re-reviewed)
51
- as long as `boundHash` matches the thread's current hash for that artifact — which it always does,
52
- because the artifact lives in the parent and cannot be edited from the child.
58
+ the file otherwise — every file without its frontmatter `status:` line (`reviewedSha`, shape 9). A
59
+ `boundHash` computed over the whole file by an older release is still accepted. The gate predicate treats an `inherited` step as satisfied (never re-reviewed)
60
+ as long as `boundHash` matches the current hash of that artifact in the owning epic (`inheritedFrom`) —
61
+ which holds unless the owner's copy changes. Compare against the OWNER's copy, never the child's: a
62
+ change-epic writes its own `epic.md` (the change brief), which is not the epic it carries.
53
63
 
54
64
  `approvals.json` provenance record per inherited gate (append-only, NOT an approval that the predicate
55
65
  counts — it just documents where the sign-off lives):
@@ -61,23 +71,23 @@ counts — it just documents where the sign-off lives):
61
71
 
62
72
  ## The pointer-lock (when `architecture` is inherited)
63
73
 
64
- Write `.sdlc/contract-lock.json` with the parent's hash **verbatim**:
74
+ `yad epic new` writes `.sdlc/contract-lock.json` with the owning epic's hash **verbatim**:
65
75
 
66
76
  ```json
67
77
  { "artifact": "contract.md", "hash": "sha256:<parent hash, copied byte-for-byte>", "lockedAt": "<today>",
68
78
  "inheritedFrom": "EP-<genesis>", "ref": "../../EP-<genesis>/.sdlc/contract-lock.json" }
69
79
  ```
70
80
 
71
- Get the parent hash from the owning epic's `.sdlc/contract-lock.json` `hash` field (do NOT recompute — copy
72
- it). `contract-check.sh` reads only `hash`, so a build-half story in the change-epic pins this identical
81
+ The engine copies the `hash` field of the owning epic's `.sdlc/contract-lock.json` (it never recomputes
82
+ it), and refuses when that lock is missing, malformed, or no longer matches the owner's `contract.md`
83
+ surface — a pointer to a stale lock would pass the drift down the thread. `contract-check.sh` reads only `hash`, so a Build story in the change-epic pins this identical
73
84
  hash via its `link.md` and the gate passes unchanged. There is no `contract.md` in the change-epic, so
74
85
  the surface physically cannot drift.
75
86
 
76
87
  **To CHANGE the surface instead:** do not inherit `architecture`. Then `yad-architecture` re-authors
77
88
  `contract.md` in the change-epic between fresh `CONTRACT-SURFACE` markers, computes a **new** hash, and
78
89
  writes a real (non-pointer) `contract-lock.json`. `architecture-review` carries `risk_tags: ["contract"]`
79
- → the escalated domain-owner review. This is the same re-lock-invalidates-approvals behaviour the front
80
- half already has, relocated from "edit the locked file" to "author a contract-surface change-epic".
90
+ → the contract-risk review (full approver count 3, capped at the active people less one and reported, E72; only the base 1 is enforced). This is the same re-lock-invalidates-approvals behaviour Shape already has, relocated from "edit the locked file" to "author a contract-surface change-epic".
81
91
 
82
92
  ## Genesis migration (one-time, per feature)
83
93
 
@@ -86,17 +96,21 @@ to its `epic.md` frontmatter:
86
96
 
87
97
  ```yaml
88
98
  kind: feature
99
+ type: feature
89
100
  thread: <its own id>
90
101
  ```
91
102
 
103
+ Both names, same value: `kind:` is the one every reader still uses, `type:` is the name from shape 5 on.
104
+
92
105
  This is a non-gated, idempotent frontmatter add (no `parent`, since a genesis epic is the thread root).
93
- `epicLineage` already defaults an absent `kind` to `feature`, so an un-migrated genesis still behaves as
106
+ `workItemType` already defaults an absent type to `feature`, so an un-migrated genesis still behaves as
94
107
  a root — migration just makes the `thread` cache explicit and lets `yad thread` group it.
95
108
 
96
109
  ## Concurrent changes on one feature (forward-only resolution)
97
110
 
98
- Two change-epics threaded off the same tip that re-author the same artifact are a **fork**: the resolver
99
- sees two non-inherited owners of one base at the same depth, and `yad doctor` / `yad reconcile` warn.
111
+ Two change-epics threaded off the same tip that re-author the same artifact are a **fork**. Nothing
112
+ detects it today: the current-truth resolver silently picks one owner (the sibling whose epic id sorts last),
113
+ and neither `yad doctor` nor `yad reconcile` warns. Look for two siblings in `yad thread` before threading.
100
114
  Resolution is forward-only — the second to merge **re-parents** onto the first (set `parent` to the new
101
115
  tip) and re-inherits; no artifact is mutated, no lock conflicts. The contract is the natural
102
116
  serialization point: only one re-lock can win, and `contract-check`'s pinned-hash fidelity check fails