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.
- package/CHANGELOG.md +355 -0
- package/README.md +79 -26
- package/bin/commands.mjs +41 -0
- package/bin/yad.mjs +437 -124
- package/cli/artifact-status.mjs +34 -15
- package/cli/checkpoint.mjs +69 -49
- package/cli/codeowners-command.mjs +170 -0
- package/cli/codeowners.mjs +397 -0
- package/cli/commit.mjs +13 -9
- package/cli/companion.mjs +2 -2
- package/cli/dial.mjs +183 -0
- package/cli/docs.mjs +88 -32
- package/cli/doctor.mjs +1472 -97
- package/cli/epic-state.mjs +3478 -232
- package/cli/epic.mjs +506 -0
- package/cli/errors.mjs +4 -1
- package/cli/gate.mjs +1002 -209
- package/cli/history.mjs +556 -0
- package/cli/hook.mjs +266 -55
- package/cli/hubcommit.mjs +6 -17
- package/cli/index-command.mjs +87 -0
- package/cli/ledger.mjs +57 -7
- package/cli/lib.mjs +184 -18
- package/cli/manifest.mjs +367 -56
- package/cli/migrate.mjs +726 -53
- package/cli/mode.mjs +170 -0
- package/cli/next.mjs +349 -90
- package/cli/openpr.mjs +191 -39
- package/cli/people.mjs +654 -0
- package/cli/plan.mjs +417 -132
- package/cli/platform.mjs +110 -129
- package/cli/product-index.mjs +287 -0
- package/cli/protection.mjs +706 -0
- package/cli/reconcile.mjs +38 -12
- package/cli/repo-publish.mjs +24 -26
- package/cli/repo.mjs +23 -14
- package/cli/report.mjs +21 -15
- package/cli/review.mjs +24 -27
- package/cli/riskmap-command.mjs +289 -0
- package/cli/riskmap.mjs +373 -0
- package/cli/setup.mjs +139 -287
- package/cli/ship.mjs +7 -6
- package/cli/skill.mjs +180 -0
- package/cli/skip.mjs +211 -30
- package/cli/thread.mjs +42 -17
- package/cli/tidy.mjs +20 -20
- package/cli/update-commit.mjs +22 -22
- package/cli/usage.mjs +115 -109
- package/package.json +3 -3
- package/skills/sdlc/config.yaml +166 -87
- package/skills/sdlc/module-help.csv +35 -35
- package/skills/yad-analysis/SKILL.md +125 -65
- package/skills/yad-architecture/SKILL.md +34 -23
- package/skills/yad-architecture/references/contract-format.md +10 -8
- package/skills/yad-backfill/SKILL.md +14 -8
- package/skills/yad-backfill/references/backfill.md +1 -1
- package/skills/yad-change/SKILL.md +127 -52
- package/skills/yad-change/references/triage.md +42 -28
- package/skills/yad-checks/SKILL.md +89 -45
- package/skills/yad-checks/references/check-gates.md +315 -92
- package/skills/yad-checks/templates/checks/build-test-lint.sh +25 -7
- package/skills/yad-checks/templates/checks/commit-message.sh +17 -3
- package/skills/yad-checks/templates/checks/contract-check.sh +58 -2
- package/skills/yad-checks/templates/checks/epic-open.sh +3 -3
- package/skills/yad-checks/templates/checks/install-deps.sh +46 -0
- package/skills/yad-checks/templates/checks/ledger-guard.sh +94 -18
- package/skills/yad-checks/templates/checks/lineage-check.sh +23 -9
- package/skills/yad-checks/templates/checks/package-manager.sh +140 -0
- package/skills/yad-checks/templates/checks/reconcile-debt-check.sh +4 -4
- package/skills/yad-checks/templates/checks/risk-map-check.sh +438 -0
- package/skills/yad-checks/templates/checks/verified-commits.sh +20 -46
- package/skills/yad-checks/templates/github/yad-checks.yml +37 -5
- package/skills/yad-checks/templates/github/yad-hub-checks.yml +5 -5
- package/skills/yad-checks/templates/github/yad-update-guard.yml +3 -4
- package/skills/yad-checks/templates/github/yad-verified-commits.yml +4 -4
- package/skills/yad-checks/templates/gitlab/.gitlab-ci.yml +7 -1
- package/skills/yad-checks/templates/gitlab/yad-checks.gitlab-ci.yml +22 -4
- package/skills/yad-checks/templates/gitlab/yad-hub-checks.gitlab-ci.yml +5 -5
- package/skills/yad-checks/templates/gitlab/yad-verified-commits.gitlab-ci.yml +4 -4
- package/skills/yad-checks/templates/hooks/ledger-guard-cursor.sh +91 -0
- package/skills/yad-checks/templates/hooks/ledger-guard.sh +38 -7
- package/skills/yad-commit/SKILL.md +6 -6
- package/skills/yad-connect-design/SKILL.md +6 -6
- package/skills/yad-connect-design/references/design-context.md +1 -1
- package/skills/yad-connect-design/references/design-registry.md +2 -2
- package/skills/yad-connect-docs/SKILL.md +12 -12
- package/skills/yad-connect-docs/references/docs-registry.md +1 -1
- package/skills/yad-connect-learning/SKILL.md +5 -5
- package/skills/yad-connect-learning/references/learning-registry.md +2 -2
- package/skills/yad-connect-repos/SKILL.md +92 -54
- package/skills/yad-connect-repos/references/code-context.md +6 -6
- package/skills/yad-connect-repos/references/hub-config.md +68 -58
- package/skills/yad-connect-repos/references/repos-registry.md +10 -9
- package/skills/yad-connect-repos/references/risk-map.md +81 -0
- package/skills/yad-connect-testing/SKILL.md +6 -6
- package/skills/yad-connect-testing/references/testing-context.md +3 -4
- package/skills/yad-connect-testing/references/testing-registry.md +2 -2
- package/skills/yad-defects/SKILL.md +8 -8
- package/skills/yad-discovery/SKILL.md +130 -94
- package/skills/yad-discovery/references/discovery-schema.md +23 -7
- package/skills/yad-discovery/references/foundation-schema.md +374 -0
- package/skills/yad-docs/SKILL.md +16 -11
- package/skills/yad-docs/references/data-mapping.md +9 -7
- package/skills/yad-docs/templates/app/package-lock.json +3 -3
- package/skills/yad-docs-overview/SKILL.md +32 -17
- package/skills/yad-docs-overview/references/pipeline-model.md +47 -28
- package/skills/yad-docs-sync/SKILL.md +10 -5
- package/skills/yad-docs-sync/references/staleness.md +8 -7
- package/skills/yad-engineer-review/SKILL.md +88 -24
- package/skills/yad-engineer-review/references/ship-and-record.md +25 -16
- package/skills/yad-epic/SKILL.md +178 -100
- package/skills/yad-epic/references/state-schema.md +626 -117
- package/skills/yad-hub-bridge/SKILL.md +66 -48
- package/skills/yad-hub-bridge/references/bridge.md +110 -83
- package/skills/yad-hub-bridge/references/login-roster.md +163 -70
- package/skills/yad-hub-bridge/templates/checks/hub-route.sh +22 -19
- package/skills/yad-hub-bridge/templates/github/yad-gate-sync.yml +34 -14
- package/skills/yad-hub-bridge/templates/gitlab/gitlab-ci.include-root.yml +2 -2
- package/skills/yad-hub-bridge/templates/gitlab/yad-gate-sync.gitlab-ci.yml +22 -12
- package/skills/yad-implement/SKILL.md +29 -15
- package/skills/yad-implement/references/implement-conventions.md +2 -2
- package/skills/yad-learn/SKILL.md +9 -9
- package/skills/yad-learn/references/learning-state.md +2 -2
- package/skills/yad-open-pr/SKILL.md +64 -29
- package/skills/yad-pair-review/SKILL.md +18 -16
- package/skills/yad-pair-review/references/session-state.md +4 -4
- package/skills/yad-pr-template/SKILL.md +48 -27
- package/skills/yad-pr-template/references/risk-routing.md +97 -24
- package/skills/yad-pr-template/templates/checks/pr-template.sh +37 -15
- package/skills/yad-pr-template/templates/checks/pr-title.sh +27 -13
- package/skills/yad-pr-template/templates/checks/risk-route.sh +107 -14
- package/skills/yad-pr-template/templates/github/pull_request_template.md +7 -5
- package/skills/yad-pr-template/templates/gitlab/merge_request_templates/Default.md +7 -5
- package/skills/yad-pr-template/templates/hub/github/pull_request_template.md +15 -14
- package/skills/yad-pr-template/templates/hub/gitlab/merge_request_templates/Default.md +15 -13
- package/skills/yad-reconcile/SKILL.md +3 -3
- package/skills/yad-report/SKILL.md +5 -5
- package/skills/yad-review-companion/SKILL.md +12 -9
- package/skills/yad-review-gate/SKILL.md +198 -79
- package/skills/yad-review-gate/references/gating.md +230 -54
- package/skills/yad-run/SKILL.md +86 -56
- package/skills/yad-run/references/run-loop.md +67 -45
- package/skills/yad-ship/SKILL.md +18 -14
- package/skills/yad-spec/SKILL.md +31 -17
- package/skills/yad-spec/references/spec-handoff.md +17 -5
- package/skills/yad-status/SKILL.md +114 -56
- package/skills/yad-stories/SKILL.md +42 -27
- package/skills/yad-stories/references/story-schema.md +10 -9
- package/skills/yad-stub/SKILL.md +59 -48
- package/skills/yad-sync-repos/SKILL.md +3 -3
- package/skills/yad-test-cases/SKILL.md +37 -30
- package/skills/yad-test-cases/references/test-cases-schema.md +8 -5
- package/skills/yad-timeline/SKILL.md +8 -7
- package/skills/yad-ui/SKILL.md +46 -25
- package/cli/roster.mjs +0 -164
- 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
|
|
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
|
|
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 **
|
|
13
|
+
skills + `yad-review-gate`. It is a **Shape step**: human-confirmed, **never auto-advances**.
|
|
14
14
|
|
|
15
|
-
This is the answer to "the
|
|
16
|
-
no longer ship through
|
|
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
|
|
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
|
|
24
|
-
`yad next`, and the
|
|
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
|
|
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
|
-
|
|
39
|
-
|
|
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
|
|
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 `
|
|
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
|
|
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
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
|
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.
|
|
151
|
-
|
|
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
|
|
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
|
|
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 `
|
|
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 (
|
|
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
|
|
178
|
-
`yad-
|
|
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.**
|
|
186
|
-
`boundHash`; the pointer-lock carries the
|
|
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
|
|
189
|
-
the same mechanism as the
|
|
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,
|
|
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
|
-
##
|
|
24
|
+
## What `yad epic new --parent` writes (worked shape)
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
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":
|
|
32
|
-
"epicId": "EP-<slug>", "createdAt": "<today>", "
|
|
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": "
|
|
35
|
-
{ "id": "epic-review", "type": "review+approve", "artifact": "epic.md", "assistance": "review", "automation": "human_approve", "locked": true, "status": "
|
|
36
|
-
{ "id": "architecture", "type": "author", "artifact": "architecture.md", "assistance": "review", "automation": "human_approve", "locked": true, "status": "
|
|
37
|
-
{ "id": "architecture-review", "type": "review+approve", "artifact": "architecture.md", "assistance": "review", "automation": "human_approve", "locked": true, "status": "
|
|
38
|
-
{ "id": "ui-design", "type": "author", "artifact": "ui-design.md", "assistance": "review", "automation": "human_approve", "locked": true, "status": "
|
|
39
|
-
{ "id": "ui-design-review", "type": "review+approve", "artifact": "ui-design.md", "assistance": "review", "automation": "human_approve", "locked": true, "status": "
|
|
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": "
|
|
42
|
-
{ "id": "test-cases", "type": "author", "artifact": "test-cases.md", "assistance": "review", "automation": "human_approve", "locked": true, "status": "
|
|
43
|
-
{ "id": "test-cases-review", "type": "review+approve", "artifact": "test-cases.md", "assistance": "review", "automation": "human_approve", "locked": true, "status": "
|
|
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
|
-
`
|
|
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
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
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
|
-
|
|
72
|
-
it)
|
|
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
|
|
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
|
-
`
|
|
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
|
|
99
|
-
|
|
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
|