yadflow 3.19.0-next.2 → 4.0.0-next.3
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 +371 -0
- package/README.md +160 -22
- package/bin/commands.mjs +46 -0
- package/bin/yad.mjs +506 -117
- package/cli/artifact-status.mjs +30 -11
- package/cli/capture.mjs +417 -0
- package/cli/checkpoint.mjs +47 -27
- package/cli/claims.mjs +165 -0
- package/cli/codeowners-command.mjs +170 -0
- package/cli/codeowners.mjs +397 -0
- package/cli/commit.mjs +13 -9
- package/cli/dial.mjs +183 -0
- package/cli/docs.mjs +88 -32
- package/cli/doctor.mjs +1682 -90
- package/cli/epic-state.mjs +3460 -211
- package/cli/epic.mjs +506 -0
- package/cli/errors.mjs +3 -0
- package/cli/fold.mjs +247 -0
- package/cli/gate-local.mjs +296 -0
- package/cli/gate.mjs +977 -183
- package/cli/history.mjs +556 -0
- package/cli/hook.mjs +264 -53
- package/cli/hubcommit.mjs +9 -19
- package/cli/index-command.mjs +87 -0
- package/cli/ledger.mjs +54 -4
- package/cli/lib.mjs +161 -9
- package/cli/manifest.mjs +376 -38
- package/cli/migrate.mjs +683 -52
- package/cli/mode.mjs +170 -0
- package/cli/next.mjs +365 -86
- package/cli/openpr.mjs +193 -34
- package/cli/owners.mjs +266 -0
- package/cli/people.mjs +657 -0
- package/cli/plan.mjs +466 -125
- package/cli/platform.mjs +109 -128
- package/cli/product-index.mjs +287 -0
- package/cli/protection.mjs +706 -0
- package/cli/reconcile.mjs +40 -12
- package/cli/repo-publish.mjs +24 -26
- package/cli/repo.mjs +23 -14
- package/cli/report.mjs +24 -14
- package/cli/review.mjs +19 -22
- package/cli/riskmap-command.mjs +289 -0
- package/cli/riskmap.mjs +373 -0
- package/cli/setup.mjs +127 -279
- package/cli/ship.mjs +6 -5
- package/cli/skill.mjs +180 -0
- package/cli/skip.mjs +211 -30
- package/cli/thread.mjs +41 -16
- package/cli/tidy.mjs +18 -18
- package/cli/update-commit.mjs +21 -21
- package/cli/usage.mjs +115 -109
- package/package.json +3 -3
- package/skills/sdlc/config.yaml +149 -70
- package/skills/sdlc/module-help.csv +24 -24
- package/skills/yad-analysis/SKILL.md +121 -59
- package/skills/yad-architecture/SKILL.md +30 -18
- package/skills/yad-architecture/references/contract-format.md +8 -5
- package/skills/yad-backfill/SKILL.md +10 -4
- package/skills/yad-backfill/references/backfill.md +8 -1
- package/skills/yad-backfill/templates/checks/backfill-check.sh +10 -2
- package/skills/yad-change/SKILL.md +119 -42
- package/skills/yad-change/references/triage.md +42 -27
- package/skills/yad-checks/SKILL.md +68 -35
- package/skills/yad-checks/references/check-gates.md +310 -85
- package/skills/yad-checks/templates/checks/commit-message.sh +17 -3
- package/skills/yad-checks/templates/checks/contract-check.sh +130 -9
- package/skills/yad-checks/templates/checks/epic-open.sh +2 -2
- package/skills/yad-checks/templates/checks/ledger-guard.sh +55 -8
- package/skills/yad-checks/templates/checks/lineage-check.sh +23 -9
- package/skills/yad-checks/templates/checks/reconcile-debt-check.sh +1 -1
- 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 +13 -2
- package/skills/yad-checks/templates/github/yad-hub-checks.yml +5 -5
- package/skills/yad-checks/templates/github/yad-update-guard.yml +7 -5
- package/skills/yad-checks/templates/github/yad-verified-commits.yml +4 -4
- package/skills/yad-checks/templates/gitlab/yad-checks.gitlab-ci.yml +10 -1
- 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 +3 -3
- package/skills/yad-checks/templates/hooks/ledger-guard-cursor.sh +91 -0
- package/skills/yad-checks/templates/hooks/ledger-guard.sh +37 -6
- package/skills/yad-checks/templates/hooks/yad-capture.sh +53 -0
- package/skills/yad-commit/SKILL.md +4 -4
- package/skills/yad-connect-design/SKILL.md +5 -5
- 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 +11 -11
- 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 +66 -45
- package/skills/yad-connect-repos/references/code-context.md +4 -4
- package/skills/yad-connect-repos/references/hub-config.md +47 -51
- package/skills/yad-connect-repos/references/repos-registry.md +8 -7
- package/skills/yad-connect-repos/references/risk-map.md +81 -0
- package/skills/yad-connect-testing/SKILL.md +5 -5
- 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 +7 -7
- package/skills/yad-discovery/SKILL.md +131 -94
- package/skills/yad-discovery/references/discovery-schema.md +22 -6
- package/skills/yad-discovery/references/foundation-schema.md +374 -0
- package/skills/yad-docs/SKILL.md +13 -8
- 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 +30 -15
- package/skills/yad-docs-overview/references/pipeline-model.md +33 -20
- 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 +81 -17
- package/skills/yad-engineer-review/references/ship-and-record.md +17 -8
- package/skills/yad-epic/SKILL.md +173 -92
- package/skills/yad-epic/references/state-schema.md +610 -94
- package/skills/yad-hub-bridge/SKILL.md +57 -38
- package/skills/yad-hub-bridge/references/bridge.md +94 -67
- 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 +21 -11
- package/skills/yad-implement/SKILL.md +27 -13
- 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 +61 -26
- package/skills/yad-pair-review/SKILL.md +7 -5
- package/skills/yad-pair-review/references/session-state.md +1 -1
- package/skills/yad-pr-template/SKILL.md +47 -26
- package/skills/yad-pr-template/references/risk-routing.md +97 -24
- package/skills/yad-pr-template/templates/checks/pr-template.sh +34 -11
- package/skills/yad-pr-template/templates/checks/pr-title.sh +32 -9
- 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 +14 -13
- package/skills/yad-pr-template/templates/hub/gitlab/merge_request_templates/Default.md +14 -12
- package/skills/yad-reconcile/SKILL.md +3 -3
- package/skills/yad-report/SKILL.md +5 -5
- package/skills/yad-review-companion/SKILL.md +6 -3
- package/skills/yad-review-gate/SKILL.md +203 -93
- package/skills/yad-review-gate/references/gating.md +232 -54
- package/skills/yad-run/SKILL.md +81 -51
- package/skills/yad-run/references/run-loop.md +61 -39
- package/skills/yad-ship/SKILL.md +15 -11
- package/skills/yad-spec/SKILL.md +21 -6
- package/skills/yad-spec/references/spec-handoff.md +17 -5
- package/skills/yad-status/SKILL.md +115 -50
- package/skills/yad-stories/SKILL.md +37 -21
- package/skills/yad-stories/references/story-schema.md +7 -6
- package/skills/yad-stub/SKILL.md +58 -47
- package/skills/yad-sync-repos/SKILL.md +2 -2
- package/skills/yad-test-cases/SKILL.md +32 -23
- package/skills/yad-test-cases/references/test-cases-schema.md +7 -4
- package/skills/yad-timeline/SKILL.md +8 -7
- package/skills/yad-ui/SKILL.md +41 -18
- package/cli/roster.mjs +0 -164
- package/skills/sdlc/install.sh +0 -68
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
# `yad-run` — the loop
|
|
1
|
+
# `yad-run` — the loop and the trust verdict
|
|
2
2
|
|
|
3
3
|
This is the detail behind `SKILL.md`. It restates the orchestration so the skill is self-contained,
|
|
4
|
-
and pins down the
|
|
5
|
-
|
|
4
|
+
and pins down the judgment the skill makes: **what trust verdict to record**. Whether a step advances on
|
|
5
|
+
its own is not a judgment any more — the team sets the dial with `yad dial` (E34), and the engine answers
|
|
6
|
+
what applies.
|
|
6
7
|
|
|
7
8
|
## The Build steps
|
|
8
9
|
|
|
@@ -12,39 +13,66 @@ From `config.yaml` `automation.back_steps` plus the human merge gate:
|
|
|
12
13
|
spec → tasks → implement → checks → engineer-review(locked)
|
|
13
14
|
```
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
`
|
|
16
|
+
**These names are the engine's DEFAULTS, not a fixed wiring.** Which skill runs a step is the
|
|
17
|
+
project's setting in `.sdlc/skills.json`, so the loop reads `yad skill list --json` once at Step 0 and
|
|
18
|
+
runs every entry of that step's `skills`, in order, when it is bound to more than one. Unbound: `spec` and `tasks` are the two legs of `yad-spec` (the heavy ceremony, then the atomic
|
|
19
|
+
`tasks.md`), `implement` is one atomic task via `yad-implement`, and `checks` is
|
|
20
|
+
`yad-checks (action: run)`. `engineer-review` is the human gate at `yad-engineer-review` — always a
|
|
21
|
+
stop, never automated, and bindable like any other step.
|
|
18
22
|
|
|
19
23
|
## The loop (pseudocode)
|
|
20
24
|
|
|
21
25
|
```
|
|
22
26
|
cfg = config.yaml.automation
|
|
23
|
-
bs = build-state/<story>.json.repos[<repo>] # create from defaults if absent
|
|
27
|
+
bs = build-state/<story>.json.repos[<repo>] # create the FILE from defaults if absent; if the file
|
|
28
|
+
# exists but has no entry for <repo>, add one from the
|
|
29
|
+
# defaults — never remove an entry you did not add
|
|
30
|
+
if bs.status == "skipped": # E39: the whole lane was set aside with
|
|
31
|
+
STOP — report bs.record.reason # `yad skip <epic> <story> --repo <repo>`; do not drive it.
|
|
32
|
+
# `yad unskip <epic> <story> --repo <repo>` puts it back.
|
|
33
|
+
# Never write a single Build step as skipped or deferred.
|
|
24
34
|
step = from or bs.currentStep
|
|
25
35
|
|
|
26
36
|
while step is a Build step (not engineer-review):
|
|
27
|
-
result = run_step_skill(step) #
|
|
37
|
+
result = run_step_skill(step) # the skill(s) `yad next --json` names for this lane
|
|
28
38
|
|
|
29
39
|
signals = derive_signals(step, result) # see "Deriving signals"
|
|
30
40
|
verdict = derive_verdict(signals) # rejected | approved-with-edits | approved-unchanged
|
|
31
41
|
uid = fresh_short_token() # e.g. `openssl rand -hex 4` — unique per run,
|
|
32
42
|
# never reuse; a collision would fuse two runs
|
|
33
43
|
write trust-log/<story>-<repo>-<step>-<uid>.json # ONE shard file = ONE entry (never append to a shared file)
|
|
34
|
-
|
|
44
|
+
# `automation` is recorded in the OLD vocabulary (this is a history record, and `yad migrate`
|
|
45
|
+
# deliberately leaves it alone). Read the step's dial from EITHER spelling.
|
|
46
|
+
= { story, repo, step, uid, automation: bs.step.automation ?? old_name_for(bs.step.advance), verdict, signals, ranBy, date }
|
|
35
47
|
|
|
36
48
|
eff = effective_dial(step, bs, cfg) # see "Effective dial"
|
|
37
49
|
|
|
38
50
|
if result is a HALT (failed / scope overrun / contract touch / ambiguous):
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
bs.step.status = "
|
|
42
|
-
|
|
43
|
-
|
|
51
|
+
# `blocked` needs its `record` (E38) — that is what separates a halted lane from one that has
|
|
52
|
+
# simply not started. `reason` is the halt cause you already have in hand.
|
|
53
|
+
bs.step.status = "blocked"
|
|
54
|
+
bs.step.record = { reason: "<check FAIL | scope overrun | contract touch | ambiguous>",
|
|
55
|
+
by: "<platform login, else git user.name, or null>", date: "<YYYY-MM-DD>" }
|
|
56
|
+
persist; checkpoint; STOP and report the human action needed
|
|
57
|
+
elif eff == "auto":
|
|
58
|
+
bs.step.status = "done"
|
|
59
|
+
bs.step.closed = { by: "<platform login, else git user.name, or null>", date: "<YYYY-MM-DD>", via: "auto", run: uid }
|
|
60
|
+
advance bs.currentStep to next; persist; checkpoint; continue # Step B advance
|
|
61
|
+
else: # "human"
|
|
62
|
+
bs.step.status = "done"
|
|
63
|
+
bs.step.closed = { by: "<platform login, else git user.name, or null>", date: "<YYYY-MM-DD>", via: "human", run: uid }
|
|
64
|
+
persist; checkpoint; STOP and report "waiting for human at <next>"
|
|
44
65
|
|
|
45
66
|
# reached engineer-review: always stop, hand to yad-engineer-review (human gate, finalizes the verdict)
|
|
46
67
|
```
|
|
47
68
|
|
|
69
|
+
`closed` is the step's **closing record** (E18): who wrote it, when, and how the lane moved past the
|
|
70
|
+
step. `via: "auto"` means the dial let the run go on by itself; `via: "human"` means it stopped for a
|
|
71
|
+
person. `run` is the `uid` of the trust-log shard this run wrote, so the record points at the evidence.
|
|
72
|
+
`by` is the login `gh api user` / `glab api user` reports, else git `user.name`, else `null`
|
|
73
|
+
(`YAD_PLATFORM_LOGIN=0` turns the lookup off).
|
|
74
|
+
Write it only when you write `done`, and never over a `closed` already on the step.
|
|
75
|
+
|
|
48
76
|
`ranBy` is `machine` when the *previous* step's effective dial caused this step to run without a human
|
|
49
77
|
nudge; otherwise `human`. Persist build-state after every transition so a halt leaves an accurate,
|
|
50
78
|
resumable record.
|
|
@@ -56,24 +84,22 @@ way `git gc` folds loose objects later (`yad tidy up` folds finished shards into
|
|
|
56
84
|
`trust-log.json` / `build-log.json`) — plus any story `status:` flip (→ in-build/shipped) once that
|
|
57
85
|
story has a build-log ship (#112) — as one
|
|
58
86
|
`chore(hub): sync Build state — <epic>/<story> by @<login> [skip ci]`
|
|
59
|
-
audit-trail commit, on the default branch only,
|
|
87
|
+
audit-trail commit (`@<login>` when the platform login is known, else the git `user.name`, else `unknown`), on the default branch only,
|
|
60
88
|
staging *only* those files by an explicit allowlist (never a Shape gate file — so `ledger-guard`
|
|
61
89
|
never trips). It is idempotent (a no-op when nothing changed), so calling it after every transition —
|
|
62
90
|
including a halt — is safe and keeps the shared trust evidence current for CI, teammates, and
|
|
63
91
|
`yad status` on other machines. It refuses to run off the default branch (an unsigned `[skip ci]`
|
|
64
92
|
commit inside a future PR range would fail `verified-commits` and strand the PR).
|
|
65
93
|
|
|
66
|
-
## Effective dial (kill switch
|
|
94
|
+
## Effective dial (the kill switch and gates always win)
|
|
67
95
|
|
|
68
96
|
```
|
|
69
|
-
eff =
|
|
70
|
-
if cfg.kill_switch == true: eff = "human_approve"
|
|
71
|
-
if bs.step.locked == true: eff = "human_approve"
|
|
72
|
-
if step in cfg.locked_steps: eff = "human_approve"
|
|
97
|
+
eff = (yad dial <epic> <story> --repo <repo> <step> --json).advance # "human" | "auto"
|
|
73
98
|
```
|
|
74
99
|
|
|
75
|
-
|
|
76
|
-
|
|
100
|
+
The engine reads the lane step's dial under either spelling (the OLD name wins), and answers `human`
|
|
101
|
+
when the step is a gate (`engineer-review`) or the kill switch in `.sdlc/automation.json` is on — whatever
|
|
102
|
+
the dial says. Its `why` says which: `lane`, `gate`, `kill` or `unknown` (a step id this release does not know), and `automationError` is present when the file cannot be read.
|
|
77
103
|
|
|
78
104
|
## Deriving signals & the provisional verdict
|
|
79
105
|
|
|
@@ -106,35 +132,31 @@ may also override it to `rejected` — e.g. the human discards/regenerates the s
|
|
|
106
132
|
rejects the diff at the engineer review for a reason no signal captured. A human override always wins.
|
|
107
133
|
|
|
108
134
|
Rationale: the trust log should count an output as fully trustworthy (`approved-unchanged`) only when
|
|
109
|
-
the machine's work was accepted as-is. Any human correction is `approved-with-edits` (useful
|
|
110
|
-
|
|
135
|
+
the machine's work was accepted as-is. Any human correction is `approved-with-edits` (useful, and a sign
|
|
136
|
+
the step still wants a person); any failure or boundary breach is `rejected`.
|
|
111
137
|
|
|
112
|
-
## The
|
|
138
|
+
## The run record as advice (E34)
|
|
113
139
|
|
|
114
|
-
|
|
115
|
-
`config.yaml` `automation.trust_threshold`. `set-dial` enforces this predicate before flipping a step
|
|
116
|
-
to `machine_advance`:
|
|
140
|
+
`yad dial` prints a step's record when someone sets its dial, and whenever it is asked with no `--to`:
|
|
117
141
|
|
|
118
142
|
```
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
all = trust-log.json.runs + [read each file in trust-log/]
|
|
122
|
-
slice = entries in `all` for this step (this story's repo; widen to the project if you track it there)
|
|
143
|
+
all = trust-log.json.runs + [read each file in trust-log/] # UNION; every shard is a distinct run
|
|
144
|
+
slice = entries in `all` for this step in this repo
|
|
123
145
|
runs = len(slice)
|
|
124
146
|
unchanged = count(e.verdict == "approved-unchanged" in slice)
|
|
125
|
-
earned = runs >= trust_threshold.min_runs
|
|
126
|
-
AND (unchanged / runs) >= trust_threshold.min_approved_unchanged
|
|
127
147
|
```
|
|
128
148
|
|
|
129
|
-
|
|
130
|
-
|
|
149
|
+
It is **advice, never a rule.** There is no threshold and no refusal: the team decides whether a step
|
|
150
|
+
runs on auto, and the record is what they read to decide. A step with no runs says so — no evidence either
|
|
151
|
+
way is not evidence against.
|
|
131
152
|
|
|
132
|
-
Reverting (
|
|
153
|
+
Reverting (`--to human`) and the kill switch are always one move.
|
|
133
154
|
|
|
134
155
|
## What stays human, always
|
|
135
156
|
|
|
136
157
|
- `engineer-review` — the merge gate. `yad-run` always stops here and hands to `yad-engineer-review`.
|
|
137
|
-
- The
|
|
138
|
-
`
|
|
158
|
+
- The Shape steps — this loop never drives them. A Shape author step's dial may be set to auto for the
|
|
159
|
+
project (`yad dial <step> --to auto`), and is only recorded until the engine runs agents (E26); every
|
|
160
|
+
Shape review gate is a person.
|
|
139
161
|
- Any contract-surface change — halts the loop and routes back to the architecture gate, regardless of
|
|
140
162
|
the dial.
|
package/skills/yad-ship/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: yad-ship
|
|
3
|
-
description: 'Build helper of the gated SDLC — commit AND open the task PR/MR in one step. A thin orchestration over yad-commit then yad-open-pr: commit the staged atomic change by the conventions (Conventional-Commits subject, Task → Contract-Change trailers, an OPTIONAL Co-Authored-By footer that is OFF by default and added only when --ai <id> is explicitly passed, ≤3-file atomic guard), then push the branch and open the PR/MR from the committed template
|
|
3
|
+
description: 'Build helper of the gated SDLC — commit AND open the task PR/MR in one step. A thin orchestration over yad-commit then yad-open-pr: commit the staged atomic change by the conventions (Conventional-Commits subject, Task → Contract-Change trailers, an OPTIONAL Co-Authored-By footer that is OFF by default and added only when --ai <id> is explicitly passed, ≤3-file atomic guard), then push the branch and open the PR/MR from the committed template, assigned to the logged-in gh/glab login with no reviewers requested. The PR step runs ONLY if the commit lands (a failed commit, tripped guard, or --dry-run stops before pushing). Drives the `yad ship` CLI; never merges. Use when the user says "ship this task", "commit and open the PR", or "commit and raise the MR". (For the engineer review + merge, use yad-engineer-review.)'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# SDLC — Commit + Open PR/MR (Build helper)
|
|
@@ -19,12 +19,12 @@ its own and **never merges**. The engineer review + merge are Step E (`yad-engin
|
|
|
19
19
|
`Task → Contract-Change → Co-Authored-By` (the footer **off by default** — added only via an explicit
|
|
20
20
|
`--ai`), the ≤3-file atomic guard (`../yad-commit/SKILL.md`).
|
|
21
21
|
- PR/MR: pushed branch, the committed template prefilled, title defaulting to the commit subject,
|
|
22
|
-
|
|
22
|
+
the assignee set and no reviewers requested, risk routing (`../yad-open-pr/SKILL.md`).
|
|
23
23
|
- **Order matters:** the PR/MR is opened **only if the commit lands**. A failed commit, a tripped
|
|
24
24
|
atomic guard, or `--dry-run` stops the step before anything is pushed.
|
|
25
|
-
- **Stage-aware on the
|
|
25
|
+
- **Stage-aware on the Product** (via `yad-open-pr`): on a `review/EP-*` branch `ship` opens the
|
|
26
26
|
Shape **artifact-review** PR (delegating to `yad gate open` — `--title` is ignored); on any
|
|
27
|
-
other
|
|
27
|
+
other Product branch it opens the **code-task** PR from the bundled code-task template. In a code repo
|
|
28
28
|
it is unchanged.
|
|
29
29
|
|
|
30
30
|
## Inputs
|
|
@@ -33,11 +33,11 @@ its own and **never merges**. The engineer review + merge are Step E (`yad-engin
|
|
|
33
33
|
- `ai` — co-author footer: `claude|copilot|cursor|coderabbit|none` (default `none` = **no
|
|
34
34
|
footer**; the `Co-Authored-By` trailer appears only when this flag names a tool).
|
|
35
35
|
- `task` — Task trailer (optional; derived from the branch when omitted).
|
|
36
|
-
- `contractChange` — flag; marks the contract surface touched (commit trailer + PR
|
|
36
|
+
- `contractChange` — flag; marks the contract surface touched (commit trailer + a raised approver count on the PR).
|
|
37
37
|
- `repo` / `risk` / `platform` / `title` — PR/MR options (see `yad-open-pr`).
|
|
38
38
|
- `base` — override the PR/MR base. The default is the repo's own default branch, resolved
|
|
39
39
|
by the full chain in **Base branch** (`yad-open-pr`) — the canonical description, including the
|
|
40
|
-
|
|
40
|
+
Product rung — never a hardcoded `main`. A non-default base loses the AI first pass; `ship` warns and
|
|
41
41
|
still opens.
|
|
42
42
|
|
|
43
43
|
## On Activation
|
|
@@ -53,12 +53,16 @@ yad ship --type <type> -m "<subject>" [--ai <id>] [--task <id>] [--contract-chan
|
|
|
53
53
|
[--repo <name>] [--risk <level>] [--title "<subject>"]
|
|
54
54
|
```
|
|
55
55
|
The CLI runs `yad commit` and, only if it succeeds, `yad open-pr` — committing the change, pushing the
|
|
56
|
-
branch, and opening the PR/MR with the template prefilled and reviewers
|
|
56
|
+
branch, and opening the PR/MR with the template prefilled and the assignee set. No reviewers are
|
|
57
|
+
requested: it prints `no reviewers were requested — ask them on the PR itself`.
|
|
57
58
|
|
|
58
59
|
### Step 3 — Route + stop (no merge)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
the
|
|
60
|
+
The open step prints how many approvers the PR asks for — the body's level and the base branch's risk
|
|
61
|
+
map, the larger step winning (E66). When it asks for more than one, `bash checks/risk-route.sh "<pr body>"`
|
|
62
|
+
lists the touched domains to ask; ask reviewers on the PR/MR itself. It also prints a reviewer
|
|
63
|
+
**suggestion** (E68): who has committed in the touched folders in the last 30 days, and what CODEOWNERS
|
|
64
|
+
lists — a hint about who may know the code, never a request and never a list of owners. Report the commit + the PR/MR URL. The PR now
|
|
65
|
+
runs the check gates (Step C); the engineer review and merge are Step E (`yad-engineer-review`).
|
|
62
66
|
|
|
63
67
|
## Hard rules
|
|
64
68
|
|
|
@@ -67,7 +71,7 @@ the engineer review and merge are Step E (`yad-engineer-review`).
|
|
|
67
71
|
- **No AI footer by default.** The wrapped commit writes a `Co-Authored-By` trailer ONLY when `--ai <id>`
|
|
68
72
|
is explicitly passed; never add it on the AI's own initiative.
|
|
69
73
|
- **No PR without a landed commit.** A failed/`--dry-run` commit stops the step before pushing.
|
|
70
|
-
- **High risk
|
|
74
|
+
- **High risk raises the approver count** — the same count as the gate.
|
|
71
75
|
- **Shipping here never merges.** The human owns the merge in `yad-engineer-review`.
|
|
72
76
|
|
|
73
77
|
## Reference
|
package/skills/yad-spec/SKILL.md
CHANGED
|
@@ -30,7 +30,7 @@ is the same graceful-degradation pattern `yad-ui` uses for Impeccable.
|
|
|
30
30
|
`specs/<story-id>/`.
|
|
31
31
|
- Spec Kit output layout (RESEARCH-NOTES §2): `specs/<feature-id>/spec.md` (+ `research.md`,
|
|
32
32
|
`data-model.md`, `contracts/`), `plan.md`, `tasks.md`; constitution at `.specify/memory/constitution.md`.
|
|
33
|
-
- Speak in the
|
|
33
|
+
- Speak in the `communication_language` set in `{project-root}/.sdlc/config.yaml`; write documents in `document_output_language`.
|
|
34
34
|
|
|
35
35
|
## Inputs
|
|
36
36
|
|
|
@@ -59,7 +59,20 @@ Read `epics/<epic>/contract.md` and `epics/<epic>/.sdlc/contract-lock.json`. The
|
|
|
59
59
|
inputs (the API shapes, data-model entities, and events the story touches) are **quoted from the
|
|
60
60
|
locked surface**, not re-authored. The spec MUST stay within that surface. If the story needs surface
|
|
61
61
|
the contract does not define, STOP and route back to the **architecture gate** — never extend the
|
|
62
|
-
contract here.
|
|
62
|
+
contract here. (On a route that HAS one; the short lanes do not — see below.)
|
|
63
|
+
|
|
64
|
+
- **Short-lane-safe:** read `epics/<epic>/.sdlc/state.json`'s `profile` first (an epic seeded before
|
|
65
|
+
file shape 6 has no such key — fall back to what its `steps[]` contains). On the `chore` and
|
|
66
|
+
`spike` routes there is no architecture step, so `contract.md` and `contract-lock.json` **never
|
|
67
|
+
exist** — that is the route's design, not a missing file to chase. Write the spec from the story and
|
|
68
|
+
`epic.md` alone, record `contract-lock: none` in Step 6's frontmatter, and say in the spec that this
|
|
69
|
+
epic defines no contract surface.
|
|
70
|
+
- A short lane has no architecture gate to route back to. So the rule above becomes stricter, not
|
|
71
|
+
looser: if the story turns out to need the shared cross-repo surface to CHANGE, **STOP and escalate
|
|
72
|
+
to a human** — the work belongs to a new epic on `classic`. Do not write the change into a spec on a
|
|
73
|
+
short lane. Do not create `specs/<story>/contracts/` at all on one — a short-lane story has no
|
|
74
|
+
surface to quote, and a slice folder under a lockless epic is what the CI gate refuses. (If one
|
|
75
|
+
already exists, a commit that only DELETES it is allowed through, which is the way back out.)
|
|
63
76
|
|
|
64
77
|
### Step 4 — Detect Spec Kit
|
|
65
78
|
Check for `/speckit.*` slash-commands and/or `demo-repos/<repo>/.specify/`. Record the result for
|
|
@@ -83,7 +96,9 @@ to read. See `references/spec-handoff.md` for the exact file map and per-file de
|
|
|
83
96
|
Write `demo-repos/<repo>/specs/<story>/link.md` (template in `references/spec-handoff.md`) with
|
|
84
97
|
frontmatter linking the spec back to the product repo: `story`, `epic`, `repo`, `feature-id`,
|
|
85
98
|
`product-repo` (path), `contract-lock` (the hash **copied** from `contract-lock.json`, NOT recomputed
|
|
86
|
-
in the code repo
|
|
99
|
+
in the code repo — or the literal `none` on a short-lane epic, which has no lock; never a made-up or
|
|
100
|
+
empty hash, which the CI gate reads as a broken lock and fails), `speckit`
|
|
101
|
+
(`installed | not-installed`), `generated` (date). This `link.md` plus the
|
|
87
102
|
spec folder is the authoritative record that this story's spec exists.
|
|
88
103
|
|
|
89
104
|
### Step 7 — Stop (Shape state untouched)
|
|
@@ -93,7 +108,7 @@ Report: the spec folder path, the files written, whether Spec Kit was used, the
|
|
|
93
108
|
|
|
94
109
|
### Step 8 — Record the `spec` trust signal (Phase 4b)
|
|
95
110
|
When this step runs under the orchestrator (`yad-run`), the generated spec is a Build run that the
|
|
96
|
-
trust log measures (
|
|
111
|
+
trust log measures (part of the run record `yad dial` shows beside the `spec` dial). The
|
|
97
112
|
verdict is **anchored to the human who accepts the spec**, never self-graded:
|
|
98
113
|
- the human approves the generated `specs/<story>/` untouched → `approved-unchanged`;
|
|
99
114
|
- the human edits the spec/plan/tasks before accepting → `approved-with-edits` (signal
|
|
@@ -104,8 +119,8 @@ pattern as the engineer review finalizing `implement` at `yad-engineer-review`).
|
|
|
104
119
|
entry in its own shard `epics/<epic>/.sdlc/trust-log/<story>-<repo>-spec-<uid>.json` (a fresh `uid` per
|
|
105
120
|
run; readers union the folded `trust-log.json` + the loose shards). Schema:
|
|
106
121
|
`../yad-epic/references/state-schema.md`. **Run standalone, no trust entry is written** — the
|
|
107
|
-
log measures orchestrated runs.
|
|
108
|
-
|
|
122
|
+
log measures orchestrated runs. The record is advice: the `spec` dial is
|
|
123
|
+
whatever the team sets with `yad dial`, and this step never changes it.
|
|
109
124
|
|
|
110
125
|
## Hard rules (build plan §A, Cross-cutting)
|
|
111
126
|
|
|
@@ -33,6 +33,7 @@ demo-repos/<repo>/specs/<story-id>/
|
|
|
33
33
|
research.md # decisions/unknowns resolved during clarify
|
|
34
34
|
data-model.md # entities/fields THIS repo implements (quoting the shared ones from the contract)
|
|
35
35
|
contracts/ # the API/event surface this repo implements (quoted from the locked contract)
|
|
36
|
+
# OMITTED ENTIRELY on a short-lane epic — see below
|
|
36
37
|
plan.md # technical approach for this repo
|
|
37
38
|
tasks.md # numbered atomic tasks (T01…), each scoped to the files it may touch
|
|
38
39
|
link.md # back-pointer to the story in the product repo (Step A adds this; not a Spec Kit file)
|
|
@@ -54,6 +55,11 @@ comes from the story's acceptance criteria and the **locked contract surface**
|
|
|
54
55
|
- **`contracts/`** — the slice of the API/event surface this repo implements, **quoted from the locked
|
|
55
56
|
`contract.md`** (e.g. `POST /orders` request/response). Add a one-line note that this is a quote of
|
|
56
57
|
the locked surface, not a new definition.
|
|
58
|
+
**Do NOT create this folder on a short-lane epic** (`profile: chore` or `spike`). Those routes have
|
|
59
|
+
no architecture step, so there is no locked `contract.md` to quote and no lock for CI to check the
|
|
60
|
+
quote against — the gate refuses a slice under a lockless epic. A short-lane story consumes the
|
|
61
|
+
shared surface through the code it calls; it never restates it. If one already exists, a commit
|
|
62
|
+
that only DELETES it is allowed through, which is the way back out.
|
|
57
63
|
- **`plan.md`** — the technical approach for this repo at story altitude (components, sequence,
|
|
58
64
|
test approach). No new cross-repo surface.
|
|
59
65
|
- **`tasks.md`** — numbered atomic tasks. Each task: an ID (`T01`, `T02`, …), a one-line goal, and an
|
|
@@ -69,7 +75,9 @@ epic: EP-<slug>
|
|
|
69
75
|
repo: <repo>
|
|
70
76
|
feature-id: EP-<slug>-S0N
|
|
71
77
|
product-repo: <path to the product repo — absolute, or relative to THIS file's dir (specs/<story>/)>
|
|
72
|
-
contract-lock: sha256:<hex copied from epics/EP-<slug>/.sdlc/contract-lock.json>
|
|
78
|
+
contract-lock: sha256:<hex copied from epics/EP-<slug>/.sdlc/contract-lock.json> # or the literal
|
|
79
|
+
# `none` on a short-lane epic, which has no lock. Never an empty or invented hash:
|
|
80
|
+
# CI reads an unparseable value as a broken lock and fails.
|
|
73
81
|
speckit: installed | not-installed
|
|
74
82
|
generated: <YYYY-MM-DD>
|
|
75
83
|
---
|
|
@@ -89,12 +97,16 @@ back to the architecture gate in the product repo — it is never widened from t
|
|
|
89
97
|
`product-repo` is the one field CI resolves on disk (contract-check, lineage-check, epic-open,
|
|
90
98
|
reconcile-debt all read it). Every gate resolves it the SAME way: an **absolute** path is used as-is; a
|
|
91
99
|
**relative** path is joined to this `link.md`'s own directory, `specs/<story>/` — `../../` climbs out
|
|
92
|
-
of the story dir and out of `specs/` to the code-repo root, so a
|
|
93
|
-
is `../../../<
|
|
100
|
+
of the story dir and out of `specs/` to the code-repo root, so a Product checked out beside the code repo
|
|
101
|
+
is `../../../<product-dir>`. A relative value that only resolves from the **repo root** still works too
|
|
94
102
|
(what contract-check historically did), so older `link.md` files keep gating. Write it as an
|
|
95
103
|
**unquoted scalar** — `"..."` or `'...'` is taken literally, and a leading `~`/`$VAR` is never
|
|
96
|
-
expanded. An unreachable path degrades the
|
|
97
|
-
them, and each one now says so in its output.
|
|
104
|
+
expanded. An unreachable path degrades the Product-reading gates to a PASS-with-note rather than failing
|
|
105
|
+
them, and each one now says so in its output. **Reachable-but-lockless is a different case and does
|
|
106
|
+
NOT degrade:** when the Product resolves and `epics/<epic>/.sdlc/` is there with no
|
|
107
|
+
`contract-lock.json` in it, a claimed `Contract-Change: yes` FAILS, because nothing exists for the
|
|
108
|
+
claim to be true of. That is the ordinary state of a short-lane epic, of a stub, and of a classic epic
|
|
109
|
+
that has not reached its architecture gate yet.
|
|
98
110
|
|
|
99
111
|
## Do not re-invent the contract
|
|
100
112
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: yad-status
|
|
3
|
-
description: 'Read-only view of an SDLC epic: prints the current step, each step''s dials (
|
|
3
|
+
description: 'Read-only view of an SDLC epic: prints the current step, each step''s dials (driver/advance, under either spelling) and status, and which approvals are still required at the active gate. For stories in Build it also prints each Build step''s automation dial, status, and run record (runs / % approved-unchanged, as advice), plus the kill switch and the Shape author dials from .sdlc/automation.json — so the team can see WHY a step is automated and reverse it. Also prints the cross-cutting personal skills-log roll-up from the LOCAL-ONLY learning ledger (gitignored, never committed/pushed — the local learner''s own learning, by stage). Across multiple epics, prints a fleet roll-up (scale of read). Use when the user says "yad status", "where is epic EP-...", "what is blocking the gate", "show the trust record", "team skills", or "fleet status".'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# SDLC — Status (read-only)
|
|
@@ -10,7 +10,8 @@ reads `.sdlc/` and `reviews/` and reports.
|
|
|
10
10
|
|
|
11
11
|
## Conventions
|
|
12
12
|
- `{project-root}` resolves from the project working directory.
|
|
13
|
-
- Operate on one epic: `{project-root}/epics/EP-<slug>/`.
|
|
13
|
+
- Operate on one epic: `{project-root}/epics/EP-<slug>/`. The Product level is the exception:
|
|
14
|
+
`EP-foundation` lives at `{project-root}/foundation/` (its `.sdlc/` and `reviews/` are there).
|
|
14
15
|
|
|
15
16
|
## On Activation
|
|
16
17
|
|
|
@@ -27,7 +28,9 @@ distinct run; never dedup by story/repo/step — but DO skip a shard whose full
|
|
|
27
28
|
`(story,repo,step,uid)` already appears in the folded `runs`, i.e. a half-applied `yad tidy up`). All are committed by `yad checkpoint` (so a fresh clone
|
|
28
29
|
or another machine sees current evidence; `yad tidy up` folds finished shards into `trust-log.json`).
|
|
29
30
|
Also read the `automation` block of
|
|
30
|
-
|
|
31
|
+
`.sdlc/config.yaml` (`back_steps`), and `.sdlc/automation.json` if present — the kill switch and the
|
|
32
|
+
Shape author steps set to auto (absent = the switch off, every Shape step human). The effective dial of any
|
|
33
|
+
step is `yad dial … --json`, which applies the kill switch and gates for you. For the
|
|
31
34
|
cross-cutting learning layer, also read — if present — the **local-only** `.sdlc/learning-records.json`
|
|
32
35
|
(the per-epic learning ledger, gitignored) and the project-wide `{project-root}/.sdlc/learning-records.json`.
|
|
33
36
|
Do not modify any of them.
|
|
@@ -35,45 +38,115 @@ Do not modify any of them.
|
|
|
35
38
|
### Step 3 — Report
|
|
36
39
|
Print, in this order:
|
|
37
40
|
|
|
38
|
-
1. **Header:** render the
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
1. **Header:** render the type noun from the `epic.md` frontmatter work-item type. Two names carry it,
|
|
42
|
+
`kind:` and `type:`; read `kind:` first, then `type:`. **Change request** (`change`),
|
|
43
|
+
**Defect** (`defect`), **Hotfix** (`hotfix`), **Chore** (`chore`), or **Epic** (`feature`, and the default when neither is
|
|
44
|
+
present) — followed by `epicId`, then `status` from `epic.md` frontmatter, `currentStep`, and `repos`
|
|
41
45
|
(the touched domains). Example: `Defect EP-checkout-queue-filter — draft @ stories`. A bug is a defect
|
|
42
|
-
(`
|
|
43
|
-
2. **Steps table** — for every Shape step in `steps[]` order
|
|
44
|
-
|
|
45
|
-
`currentStep` with `→`. The
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
(type `defect`) — there is no separate noun. This is presentation only; the artifact is still an epic.
|
|
47
|
+
2. **Steps table** — for every Shape step in `steps[]` order, however many there are: `id`, `type`,
|
|
48
|
+
`status`, the two dials (`driver`/`assistance` and `advance`/`automation` — read whichever the step carries, the OLD name wins), `locked`, and `risk_tags`. Mark the
|
|
49
|
+
`currentStep` with `→`. **The chain length is a fact about the epic's ROUTE, never a constant** —
|
|
50
|
+
read `profile` from `state.json`: `classic` is 10 steps, `analysis-first` 12, and the short lanes
|
|
51
|
+
`chore` 4 and `spike` 6 (E40). On `classic` the gating chain is `[analysis → analysis-review →]
|
|
52
|
+
epic → epic-review → architecture → architecture-review → ui-design → ui-design-review → stories →
|
|
53
|
+
stories-review` → **`ready-for-build`** (the bracketed `analysis` prefix is not on `classic`: it is
|
|
54
|
+
what makes `analysis-first`, the route `yad-analysis` seeds). A short lane runs `epic → epic-review → stories → stories-review` →
|
|
55
|
+
**`ready-for-build`**, with `analysis` in front on `spike`, and has no architecture, UI-design or
|
|
56
|
+
test-case rows at all — render what `steps[]` holds and never a step it lacks.
|
|
48
57
|
`test-cases → test-cases-review` is a **parallel, non-blocking track**: it opens when `stories-review`
|
|
49
58
|
passes and runs alongside Build, so when `currentStep` is `ready-for-build` the `test-cases`
|
|
50
59
|
step may still be `in_progress`/`in_review` — show its status, and note "parallel" so it is clear it
|
|
51
60
|
does not gate the build. Always render exactly the steps present in `steps[]`.
|
|
52
|
-
- **Skipped (N/A) steps:** the optional `ui-design
|
|
53
|
-
|
|
54
|
-
in `steps[]`, each
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
- **Skipped (N/A) steps:** a step the epic's route marks optional — `ui-design`, on the routes that
|
|
62
|
+
ship — may be marked N/A with `yad skip`. **Both** the author step **and** its `-review` gate stay
|
|
63
|
+
in `steps[]`, each with `status: "skipped"` and a `record` (`{ reason, by, date }`). A file from
|
|
64
|
+
before shape 7 spells the same thing `status: "done"` with `skipped: true` and a `skipReason`
|
|
65
|
+
beside it; read either. Render **each** as `<id> — SKIPPED (N/A: <reason>)` (with who and when if
|
|
66
|
+
present) instead of the plain status, so the deliberate skip and its reason are visible and the
|
|
67
|
+
gate does not appear to vanish. The chain then reads `… architecture-review → ui-design (N/A) → ui-design-review (N/A) →
|
|
58
68
|
stories → …`; the review gate never needs approvals.
|
|
69
|
+
- **Deferred steps:** `yad defer` sets an optional step aside to come back to later. Both the author
|
|
70
|
+
step and its `-review` gate carry `status: "deferred"` and a `record`. Render each as
|
|
71
|
+
`<id> — DEFERRED (later: <reason>)`, with who and when if present. A deferred step is still owed:
|
|
72
|
+
never render it as done or as N/A.
|
|
73
|
+
- **Debt:** a step carrying `"debt": true` (written by `yad defer … --debt`) is owed back and must be
|
|
74
|
+
reminded until paid. Add `· DEBT` to its line, and list every debt in a short **Owed** block under
|
|
75
|
+
the chain with the command that pays it: `yad undefer <epic> <id>` while it is still `deferred` (on a verified Product,
|
|
76
|
+
say that verb is refused once the ledger is on the default branch), or
|
|
77
|
+
"being paid back — clears when `<id>-review` passes" once it is back in progress. The flag is
|
|
78
|
+
removed only when that review passes. `yad doctor` reports the same list as `step:debt`.
|
|
79
|
+
- **Closed steps:** a `done` step may carry `closed` (E18) — `{ by, date, via, pr?, … }`. Add
|
|
80
|
+
`(closed <date>, via <via>, PR #<pr>)` to its line when it is there, and nothing when it is not: a
|
|
81
|
+
step closed before this release has none, and that is not a fault.
|
|
82
|
+
- **Re-opened steps:** an unfinished step with a later step `done` (not its own `-review` gate) was
|
|
83
|
+
put back after the chain built past it — a late `yad undefer`. Render it as
|
|
84
|
+
`<id> — RE-OPENED (beside finished work)`: it does not block the finished steps after it, and
|
|
85
|
+
`currentStep` stays where it is. `yad next <epic>` prints it as a `re-opened lane`.
|
|
86
|
+
- **Blocked steps:** a step with `status: "blocked"` **and** a `record` is waiting on someone outside
|
|
87
|
+
the workflow. Render it as `<id> — BLOCKED (<reason>)`, with who recorded it and when, and say it is
|
|
88
|
+
cleared with `yad unblock <epic> <id>` once the wait is over — on a verified Product that verb refuses
|
|
89
|
+
once the ledger is on the default branch, so say so. A `blocked` with no record is the older
|
|
90
|
+
word for `todo`: render it as not started.
|
|
91
|
+
- **Owned steps:** `yad assign` (E47) gives an authoring step to one person, in
|
|
92
|
+
`<epic>/.sdlc/owners/<step>.json` (`{ step, owner, name, assignedBy, date }`). Add `· owner: <name>`
|
|
93
|
+
to the author step's line **and** its `-review` line while it is live — the author step, or its
|
|
94
|
+
review, not yet passed (`done`, `skipped`, `deferred`, `satisfied`). A passed step's file is a record:
|
|
95
|
+
say nothing. It is advice, not a lock, and never changes what the gate needs. A file that is not valid
|
|
96
|
+
JSON, or whose `step` is not its own file name, is not an owner: name it and point to `yad owners`.
|
|
97
|
+
`yad owners [<epic>] --json` gives the same list ready-made.
|
|
59
98
|
3. **Active gate** — for the `currentStep` (if it is a `review+approve` step), compute and show:
|
|
60
|
-
- the
|
|
61
|
-
|
|
62
|
-
- approvals **recorded so far** (from `approvals.json
|
|
63
|
-
- approvals **still required** to pass the gate (
|
|
99
|
+
- the count in force — **distinct approvers** recorded, against the **base** (1, enforced) and the
|
|
100
|
+
**full count** from the step's risk tags (advisory),
|
|
101
|
+
- approvals **recorded so far** (from `approvals.json`, one line per approver), and
|
|
102
|
+
- approvals **still required** to pass the gate (`1 approval(s)`, or none).
|
|
64
103
|
Do not advance — just state whether the gate would pass right now.
|
|
65
104
|
|
|
66
105
|
Apply the same predicate `yad-review-gate` uses (restated here so this skill is
|
|
67
106
|
self-contained). From the `approved` records in `approvals.json` for the current step:
|
|
68
|
-
- `
|
|
69
|
-
`
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
-
|
|
75
|
-
|
|
76
|
-
|
|
107
|
+
- `approvers` = the distinct, non-empty `approver` values of the approvals that are not stale (below).
|
|
108
|
+
A record that names nobody counts as nobody. When `requireEngagement` is on in the Product config,
|
|
109
|
+
only approvals with `engagement: verified` count. There are no roles; a `role` or `domain` an older
|
|
110
|
+
record carries is legacy data, and it counts for nothing.
|
|
111
|
+
- **Pass (team mode):** `|approvers| >= 1` — the base — plus, with a platform, resolved review threads and a merged
|
|
112
|
+
review PR/MR. Solo mode waives approvals entirely.
|
|
113
|
+
- **The full count, reported on every step that is not inherited, skipped or deferred:** `needed = base 1 + risk step` — plus `2` when the
|
|
114
|
+
step's `risk_tags` carry `contract`, or `1` when they carry `auth`/`payments` (the highest tag,
|
|
115
|
+
never the sum). The engine **caps** that ask at `active − 1` (floor 1) and prints it (E72) — for
|
|
116
|
+
example a contract gate asks 3, and with 2 active people the capped ask is 1. The risk step is
|
|
117
|
+
ADVISORY, capped or not — it never decides whether the gate would pass, because the count
|
|
118
|
+
of people can read high (a git name and a platform login are two people until proven one). When the
|
|
119
|
+
people could not be counted, no cap is shown. `yad gate status` prints the sum and the shortfall
|
|
120
|
+
(`; count: 3 approvers = base 1 + contract risk 2 — capped to 1: 2 active people, less one seat
|
|
121
|
+
for the author — base enforced, risk step advisory — 1 short`, or without the `capped to …` part
|
|
122
|
+
when the cap lowered nothing) — read it from there rather than recomputing it, and say "short N"
|
|
123
|
+
rather than "blocked" when it is short. A closed team gate whose ask was lowered says
|
|
124
|
+
`count capped from 3 to 1 (2 active people)`. In solo mode it prints no shortfall.
|
|
125
|
+
- **A gate that may not pass (E73):** under a team gate that has not passed, `yad gate status` can
|
|
126
|
+
print a warning line. Relay it as stated, as a warning and never as "blocked": it holds nothing.
|
|
127
|
+
A line starting `! may not be met:` means there is no approval in the counting window and the one
|
|
128
|
+
required approval may have nobody but the author to give it; another person's approval settles it.
|
|
129
|
+
A line starting `! if the risk step were enforced:` is a what-if: the extra approvals that risk
|
|
130
|
+
tags add could not all be given by the people counted. The gate can still pass today, and the line
|
|
131
|
+
says so.
|
|
132
|
+
- **How many people there are to ask (E71), printed once per epic and not per step:** `active people:
|
|
133
|
+
4 in the last 90 days — caps each gate's count at 3 approvers (one seat is left for the author);
|
|
134
|
+
reported, only the base is enforced`, with the window's basis on the line under it. It is
|
|
135
|
+
the count the cap (E72) uses, so never report a step as blocked by it. A source that could not be
|
|
136
|
+
read prints `active people: NOT COUNTED — <which source> — no cap can be shown, and only the base
|
|
137
|
+
holds each gate`: report that wording as it stands and NEVER as a number or as "no people" — an
|
|
138
|
+
unreadable input is not a small team.
|
|
139
|
+
- **Solo mode may be wrong (E74):** in solo mode, under the `active people:` line, `yad gate status`
|
|
140
|
+
can print `! solo mode is on, but … so more than one person may work on this Product`. Relay it as
|
|
141
|
+
a suggestion to run `yad mode team`, never as a fault and never as switched: nothing changes until
|
|
142
|
+
a person runs it. It never suggests solo mode.
|
|
143
|
+
- Touched domains (`epic.repos` for a step with a risk tag; the union of every story's `repos` for
|
|
144
|
+
`stories-review`) only label the review. They add no approvals.
|
|
145
|
+
- An approval is **stale** (it no longer counts) when the `artifactHash` it recorded no longer
|
|
146
|
+
matches the artifact's current fingerprint — the content changed after that person approved. For
|
|
147
|
+
`architecture-review` the fingerprint is the contract surface in `contract.md`. An approval with no
|
|
148
|
+
`artifactHash` is never stale. `yad gate status` prints the stale count. For `architecture-review`,
|
|
149
|
+
also flag when the contract-surface hash no longer matches `.sdlc/contract-lock.json`.
|
|
77
150
|
4. **Contract lock** — if `.sdlc/contract-lock.json` exists, show the locked hash and `lockedAt`
|
|
78
151
|
(and, when at/after `architecture-review`, whether the current surface still matches it).
|
|
79
152
|
5. **Stories** — if `stories/` has files, list each story `id` and its `repos` tags.
|
|
@@ -81,26 +154,18 @@ Print, in this order:
|
|
|
81
154
|
7. **Build (per story, per repo)** — if any `.sdlc/build-state/<story-id>.json` exists, then for
|
|
82
155
|
each such story and each of its repos print the Build chain
|
|
83
156
|
`spec → tasks → implement → checks → engineer-review`, marking each step's `status`, its
|
|
84
|
-
`
|
|
157
|
+
`advance` dial (or `automation`, whichever the step carries), and `locked`. Mark that repo's `currentStep` with `→`. A repo whose lane is `status: skipped` (a whole lane set aside with `yad skip <epic> <story> --repo <repo>`, E39) prints `skipped (N/A)` and its `record.reason` instead of the chain. This shows, at a glance,
|
|
85
158
|
which Build steps are automated and where a run is waiting. (For the single *next* build sub-step to
|
|
86
159
|
take per story/repo — rather than this full status view — point the user at `yad next <epic>`, which
|
|
87
160
|
reads the same `build-state` files.)
|
|
88
|
-
8. **Automation &
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
Never recommend flipping a locked step or a Shape step — those can never be `machine_advance`.
|
|
97
|
-
|
|
98
|
-
**Nudge-cost signal (Phase 5 instrumentation).** For each Build step that is **earned but its dial
|
|
99
|
-
is still `human_approve`** (and it is not locked / not a Shape step), flag it:
|
|
100
|
-
`⚠ earned but manual — could be machine_advance`. This is the *nudge cost* the Phase 5 trigger
|
|
101
|
-
watches: automation that is proven safe but still hand-started. It is a read-only observation, not a
|
|
102
|
-
recommendation to flip — earning the evidence and flipping the dial stay deliberate human acts
|
|
103
|
-
(`yad-run action: set-dial`). See `docs/phase-5-build-plan.md` §"What to instrument now".
|
|
161
|
+
8. **Automation & run record** — print the **kill switch** from `.sdlc/automation.json` (when on, who
|
|
162
|
+
turned it on, when and why, and that every step is held at `advance: human`), and the Shape author
|
|
163
|
+
steps the project set to `auto` — noting that a Shape `auto` is recorded only: nothing drives a Shape
|
|
164
|
+
step on its own yet. Then, for each Build step that has entries in the trust ledger — the **union** of
|
|
165
|
+
the folded `.sdlc/trust-log.json` `runs` plus every loose `.sdlc/trust-log/` shard — print its **run
|
|
166
|
+
record**: number of runs and the fraction with `verdict == "approved-unchanged"`. It is **advice**:
|
|
167
|
+
there is no threshold and nothing is "earned" (E34). Do not recommend flipping a dial; the team sets
|
|
168
|
+
dials with `yad dial`. A review gate is never auto.
|
|
104
169
|
|
|
105
170
|
9. **My skills (the learning layer — local-only).** If `.sdlc/learning-records.json` exists for the epic
|
|
106
171
|
(or the project-wide ledger does), print the **personal skills-log** roll-up from it — read-only. These
|
|
@@ -118,9 +183,9 @@ Print, in this order:
|
|
|
118
183
|
10. **Fleet roll-up (overview only).** When the user asked for an overview, or more than one epic exists
|
|
119
184
|
under `{project-root}/epics/`, print a one-line-per-epic roll-up across the fleet: each epic's
|
|
120
185
|
`currentStep` (Shape gate) and, for stories in Build, a count of Build steps **waiting
|
|
121
|
-
at a human gate
|
|
186
|
+
at a human gate**, plus a **local skills-log** count (records
|
|
122
187
|
in the local-only `learning-records.json`: learned / in-progress). Close with fleet totals (epics at
|
|
123
|
-
each Shape gate; total
|
|
188
|
+
each Shape gate; total concepts learned locally across the fleet).
|
|
124
189
|
This is the
|
|
125
190
|
*scale-of-read* signal the Phase 5 trigger watches — when this roll-up stops fitting in one glance,
|
|
126
191
|
that is the measured bottleneck. Still strictly read-only; it only scans the per-epic files.
|