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
|
@@ -3,42 +3,211 @@
|
|
|
3
3
|
## Reviewer rule
|
|
4
4
|
Let `A` = the set of `approved` records in `.sdlc/approvals.json` for this step.
|
|
5
5
|
|
|
6
|
-
- `
|
|
7
|
-
- `reviewers = { a in A : a.role == "reviewer" }` (distinct by `approver`)
|
|
8
|
-
- `domainOwners = { a in A : a.role == "domain-owner" }` (grouped by `a.domain`)
|
|
6
|
+
- `approvers = { a.approver : a in A }` (distinct people — two records from one person are one)
|
|
9
7
|
|
|
10
|
-
**
|
|
8
|
+
**Pass (team mode):** `|approvers| >= 1` — the **base**. Missing, the gate reports `1 approval(s)`.
|
|
11
9
|
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
There are no roles. yadflow keeps no list of people, so there is no owner rule, no reviewer rule and no
|
|
11
|
+
domain-owner rule. Older records may still carry `role` and `domain`; they stay on disk and nothing
|
|
12
|
+
reads them. The gate's `rule` label for a team gate is `count` (`solo`, `inherited` and `skipped` are
|
|
13
|
+
unchanged).
|
|
14
|
+
|
|
15
|
+
## The per-step count rule
|
|
16
|
+
|
|
17
|
+
The count is the whole rule. It counts people, not roles:
|
|
18
|
+
|
|
19
|
+
`needed = base + risk step`, where `base` is `1` — one human approval from someone other than the author.
|
|
20
|
+
The engine never compares the approver with the author: GitHub stops you approving your own PR, GitLab
|
|
21
|
+
does only when the project's approval settings say so, and on a local-only ledger nothing does — so do
|
|
22
|
+
not record the author's own approval — and the **risk step** comes from the step's own
|
|
23
|
+
`risk_tags`:
|
|
24
|
+
|
|
25
|
+
| Tags on the step | Risk | Risk step | `needed` |
|
|
26
|
+
|---|---|---|---|
|
|
27
|
+
| none | normal | +0 | 1 |
|
|
28
|
+
| `auth` and/or `payments` | high | +1 | 2 |
|
|
29
|
+
| `contract` | contract surface | +2 | 3 |
|
|
30
|
+
|
|
31
|
+
Several tags take the **highest** step, never the sum — a gate is one decision about the riskiest thing
|
|
32
|
+
it touches. `have` is the number of **distinct approvers**. The tags are read from the step as the epic
|
|
33
|
+
records it in `state.json`, so adding `auth` to a step by hand raises that epic's full count.
|
|
34
|
+
|
|
35
|
+
**What holds the gate: only the base.** The base (1) always holds every team gate, and for now it is the only part that does. The rule is one
|
|
36
|
+
formula: `needed = base + risk step`, **capped** at `active − 1`, with a floor of 1. `active` is the live
|
|
37
|
+
count of people who committed or approved lately (E71); `yad gate status` and `yad gate sync` print it,
|
|
38
|
+
and the predicate carries it as `active`. Since E72 the engine **computes, prints and records** the cap,
|
|
39
|
+
but it does **not enforce** it — the risk step stays advisory, capped or not, and its shortfall is
|
|
40
|
+
reported as `short`, never blocking:
|
|
41
|
+
|
|
42
|
+
| Active people | Contract gate: capped ask (full count 3) |
|
|
43
|
+
|---|---|
|
|
44
|
+
| 0–1 | 1 (never below 1) |
|
|
45
|
+
| 2 | 1 (one seat is left for the author) |
|
|
46
|
+
| 3 | 2 |
|
|
47
|
+
| 4 or more | 3 |
|
|
48
|
+
|
|
49
|
+
The `− 1` is **one seat left for the author**. It is a seat, not a check: the engine does not know who
|
|
50
|
+
the author is. The platform decides whether the author may approve (GitHub never allows it; GitLab only
|
|
51
|
+
when its settings say so; a local ledger checks nothing). The floor of 1 means the cap only ever trims
|
|
52
|
+
the risk step.
|
|
53
|
+
|
|
54
|
+
**Why the cap is not enforced yet.** The count of people errs high in the normal case. A commit is
|
|
55
|
+
counted by its git name, an approval by its platform login, and yadflow never joins the two without
|
|
56
|
+
exact evidence. So an ordinary two-person team can read as **four**. At four the cap lowers nothing,
|
|
57
|
+
and an enforced contract gate would ask three approvals of a team with one person who is not the author
|
|
58
|
+
— a gate it could never pass. A later yadflow change turns the capped count on together with
|
|
59
|
+
`yad gate lower --reason`, the way out of a gate that cannot be met, once the count is accurate.
|
|
60
|
+
|
|
61
|
+
**When a gate may not pass (E73).** `yad gate status` and `yad gate sync` print a warning line under a
|
|
62
|
+
team review gate that has not passed yet, when the count of people suggests the gate may not pass. It is a
|
|
63
|
+
**warning only**: it never holds a gate, never changes whether the gate passed, and writes nothing. There
|
|
64
|
+
are two kinds of line:
|
|
65
|
+
|
|
66
|
+
- `! may not be met: …` is about **today's rule**: the one approval that is always required (the base) may
|
|
67
|
+
have nobody but the author to give it.
|
|
68
|
+
- `! if the risk step were enforced: …` is a **what-if**. Today only the base is enforced, so the gate can
|
|
69
|
+
still pass, and the line ends by saying that nothing beyond the one enforced approval is needed today.
|
|
70
|
+
It says what would happen if a later yadflow change enforced the extra approvals that risk tags add
|
|
71
|
+
(the risk step). "The approvals asked" means the count after the cap. The cap limits the count to the
|
|
72
|
+
active people less one, and never below 1.
|
|
73
|
+
|
|
74
|
+
Every line talks about **the people counted**, and says "may" or "if", because the count can be wrong both
|
|
75
|
+
ways. It is too low when a reviewer has not committed or approved inside the counting window (the same
|
|
76
|
+
window as the `active people:` line), because nothing in that window records them. It is too high when
|
|
77
|
+
one person commits with a work email (recorded as a name) and approves on GitHub (recorded as a login):
|
|
78
|
+
yadflow never joins a name and a login without proof, so that person counts twice.
|
|
79
|
+
|
|
80
|
+
| Kind | When it prints | Example line (a 90-day window) |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| Today | 0 or 1 active person counted, and no approval in the window | ``! may not be met: only 1 active person counted and no approval in the last 90 days, so if nobody but the author can approve, this gate cannot pass. Someone who has not committed or approved in the last 90 days is not counted. Another person's approval settles it, or use the recorded way out, `yad mode solo --reason` `` |
|
|
83
|
+
| Today | Exactly 2 people counted, one not matched to a platform login and one login, and no approval in the window. Example: one developer who commits with a work email and also through GitHub's web editor, which records the login | ``! may not be met: 1 of the 2 people counted is not matched to a platform login and may be the same person as the one login, and there is no approval in the last 90 days, so the team may be one person. Then, if nobody but the author can approve, this gate cannot pass. Another person's approval settles it, or use the recorded way out, `yad mode solo --reason` `` |
|
|
84
|
+
| What-if | The cap lowered the count, and the approvals do not show more people than were counted | `! if the risk step were enforced: with no cap, the full count of 3 is more than 2 active people can give (one seat is left for the author), so if nobody else joins, this gate could not pass. Nothing beyond the one enforced approval is needed today` |
|
|
85
|
+
| What-if | Some people are not matched to a platform login and at least one is a login, and the smallest possible team could not give the approvals asked | `! if the risk step were enforced: 2 of the 4 people counted are not matched to a platform login, and up to 2 of them may be the same people as the logins, so the team may be as small as 2. That leaves room for 1 of the 3 approvals asked, so if the team is that small, this gate could not pass. Nothing beyond the one enforced approval is needed today` |
|
|
86
|
+
|
|
87
|
+
A what-if line is not printed when a today line is. When the cap lowered the count, the last line says
|
|
88
|
+
"approvals asked after the cap", because the full-count line above it quotes the number before the cap.
|
|
89
|
+
|
|
90
|
+
"Not matched to a platform login" means yadflow knows the person only by a name: a git author name, or an
|
|
91
|
+
approval record that carries no login (an older hand-written one, or an engineer-review record, even when
|
|
92
|
+
it holds a login, because nothing in it proves that). Each such name may be a second row for one of the
|
|
93
|
+
logins, so the smallest possible team is the larger of the two groups: the logins, or the names. It is
|
|
94
|
+
never fewer people than the approvals prove.
|
|
95
|
+
|
|
96
|
+
**An approval is evidence.** Any approval in the counting window shows that approvals can be given, so no
|
|
97
|
+
today line prints. It also raises the smallest team a what-if line assumes: at least the approvals on this
|
|
98
|
+
step plus one (the author), and at least two people once anyone has approved anything. That assumes
|
|
99
|
+
authors cannot approve their own work: always true on GitHub, true on GitLab only when its settings say
|
|
100
|
+
so, and never checked on a Product with no platform. Where it is not true, the smallest team is guessed
|
|
101
|
+
too high, so a what-if line may stay quiet when it should speak; it never raises a false alarm because of
|
|
102
|
+
this.
|
|
103
|
+
|
|
104
|
+
A Product with no platform (every record is a name) gets no name line, because there is no login to
|
|
105
|
+
compare against. Two spellings of one name (`bo` and `Bo Chen`) still count as two people; no line can
|
|
106
|
+
see that yet.
|
|
107
|
+
|
|
108
|
+
No line prints in solo mode, under a step that passed, under a waived step (inherited from a parent epic,
|
|
109
|
+
skipped, or deferred), or when the people could not be counted. A line about the whole Product prints
|
|
110
|
+
once per command, under the first step it applies to.
|
|
111
|
+
|
|
112
|
+
Run `yad gate status` yourself while the review is still open: reviewers can still approve then, and a
|
|
113
|
+
merged review PR cannot take approvals. Do not wait for CI to show it. The wired Product workflow runs only
|
|
114
|
+
for merged review PRs (at the merge, and in a scheduled sweep of merged PRs), and it usually cannot count
|
|
115
|
+
people, because the connected repos are not on disk there.
|
|
116
|
+
|
|
117
|
+
**When the people cannot be counted** (`active: null` — a source could not be read), **no cap is
|
|
118
|
+
computed or shown**, and the base holds as always. The engine never guesses a small number from an
|
|
119
|
+
unknown. Product CI is usually this case: it checks out only the hub, so on a Product with connected
|
|
120
|
+
repos the repos are not on disk.
|
|
121
|
+
|
|
122
|
+
**Every cap is recorded.** When a **team** gate passes on its counted approvals while the cap lowered
|
|
123
|
+
its ask, its closing record gets `capped: { needed, to, active }` beside `waived` — the full count, the
|
|
124
|
+
capped ask, and the count of people it read. It records what the gate ASKED, not what held it (the base
|
|
125
|
+
held). It is not written in solo mode, where nothing was counted, nor on a step that passed by its skip
|
|
126
|
+
or inherited shortcut, where nothing was asked. The same rules are listed in
|
|
127
|
+
`../../yad-epic/references/state-schema.md` (Closing records) and `docs/CLI.md`. `yad gate status` prints it as
|
|
128
|
+
`count capped from 3 to 1 (2 active people)`. The generated review-PR body also keeps the count as it
|
|
129
|
+
was when the PR was opened.
|
|
130
|
+
|
|
131
|
+
Why count people: the rule names no person, no role and no step. A stored list of people goes
|
|
132
|
+
stale; repository access decides who can approve.
|
|
133
|
+
|
|
134
|
+
Four surfaces print the count with its cap: `yad gate sync`, `yad gate status`, the generated
|
|
135
|
+
review-PR body and `yad open-pr`. (`checks/risk-route.sh` and `checks/hub-route.sh` print the count
|
|
136
|
+
without the cap — they cannot count people.) `yad gate sync` and `yad gate status` share one suffix; the review-PR body and `yad
|
|
137
|
+
open-pr` word the cap their own way (below). The shared suffix appears only when the step has a risk
|
|
138
|
+
step. It names the cap only when the cap lowered the ask, and always ends by saying what holds:
|
|
139
|
+
- ` — capped to 1: 2 active people, less one seat for the author — base enforced, risk step advisory` — the cap lowered the ask;
|
|
140
|
+
- ` — capped to 1: 1 active person (never below 1) — base enforced, risk step advisory` — at 1 active person (at 0 it reads `0 active people (never below 1)`);
|
|
141
|
+
- ` — base enforced, risk step advisory` — the cap lowered nothing, or the people could not be counted.
|
|
142
|
+
|
|
143
|
+
Where each surface puts it:
|
|
144
|
+
- `yad gate sync`: `1 approved; count: 3 approvers = base 1 + contract risk 2 — capped to 1: 2 active people, less one seat for the author — base enforced, risk step advisory`
|
|
145
|
+
- `yad gate status`: `; count: <sum>` with the same suffix, after the distinct-people count. Above the
|
|
146
|
+
gates it prints the count of people and what it does, for example
|
|
147
|
+
`active people: 2 in the last 90 days — caps each gate's count at 1 approver (one seat is left for the author); reported, only the base is enforced`
|
|
148
|
+
(at 0 or 1 people the bracket reads `(never below 1)`),
|
|
149
|
+
or, when it cannot count them, `active people: NOT COUNTED — <reason> — no cap can be shown, and only the base holds each gate`.
|
|
150
|
+
- the generated review-PR body: `- **Approvals needed:** 1 (enforced) · full count 3 approvers = base 1 + contract risk 2, capped to 1 for 2 active people when this PR was opened (the risk step is advisory)`.
|
|
151
|
+
With no cap: `- **Approvals needed:** 1 (enforced) · full count 3 approvers = base 1 + contract risk 2 (the risk step is advisory)`.
|
|
152
|
+
Its Active-people line: ``- **Active people:** 2 when this PR was opened (the cap is 1 (one seat is left for the author), so this gate's count is 1; `yad gate status` counts it live)``
|
|
153
|
+
(at 0 or 1 people the inner bracket reads `(never below 1)`);
|
|
154
|
+
with no risk step it ends ``… (`yad gate status` counts it live)``.
|
|
155
|
+
- `yad open-pr` (a code-repo task PR, Build half): `this PR asks for 3 approvers = base 1 + contract risk 2, capped to 1 for 2 active people — base enforced, risk step advisory; …`
|
|
156
|
+
(the line goes on to name `checks/risk-route.sh`, which prints the count without the cap). The cap
|
|
157
|
+
appears only when `yad open-pr` is run from the Product; from inside a code repo the people are not counted.
|
|
158
|
+
|
|
159
|
+
`yad gate review` prints JSON, and it carries the rule as an object under `step.gateRule`, and the cap under `step.cap` — an object `{ active, limit, to, capped }`, where `to` is the count asked for after the cap and
|
|
160
|
+
`capped` is true when the cap lowered it — instead of a sentence. The whole `step.cap` field is `null`
|
|
161
|
+
when the people were not counted.
|
|
162
|
+
|
|
163
|
+
Solo mode waives approvals entirely, exactly as before, and reports no shortfall. No `capped` record is
|
|
164
|
+
written in solo mode. The merge and the
|
|
165
|
+
resolved threads still advance the step, and the review step's closing record carries `waived: "solo"`
|
|
166
|
+
(E10). Switch with `yad mode solo --reason "<why>"` / `yad mode team`.
|
|
167
|
+
In solo mode, `yad gate status`, `yad mode`, `yad next` and `yad doctor` count the active people and print
|
|
168
|
+
`! solo mode is on, but …` when more than one person may work on the Product (E74): 2 or more platform
|
|
169
|
+
logins, or 2 or more names not matched to a login, or an approval by someone with a platform login in the
|
|
170
|
+
counting window with more than one person counted. It only suggests `yad mode team`; nothing switches by
|
|
171
|
+
itself, and it never suggests solo mode. It says "may", because one person with two accounts, two
|
|
172
|
+
spellings of their name, or a bot that commits or auto-approves also reads as two. When the people could
|
|
173
|
+
not be counted, nothing is suggested: `yad mode` and `yad doctor` (a warning) say so; `gate status` and
|
|
174
|
+
`yad next` do not repeat it.
|
|
14
175
|
|
|
15
176
|
**Engagement (the Review Companion).** Each approval carries `engagement: verified | none` —
|
|
16
177
|
`verified` when it was recorded through the companion (a real trailer/cards/chat session), `none` for a
|
|
17
178
|
bare UI click. By **default (soft)** both count: a bare approve still passes the gate but is recorded
|
|
18
179
|
`none` and draws a friendly public @-mention nudge, so review *quality* is visible without blocking
|
|
19
180
|
anyone. When `hub.review.requireEngagement: true`, only `verified` approvals are counted toward the
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
181
|
+
approvers above; when that leaves the gate short, it adds a line saying how many approvals did not count (a determined faker
|
|
182
|
+
can still run an empty session — the signal is **gameable by design**; it raises the cost of a
|
|
183
|
+
rubber-stamp and makes laziness visible, it does not prove a human read the artifact). Philosophy:
|
|
184
|
+
*visible, not impossible.*
|
|
185
|
+
|
|
186
|
+
**Touched domains** are resolved from files, not hardcoded. They name and label a review (`domain:<repo>`
|
|
187
|
+
labels on the review PR); they add no approvals:
|
|
188
|
+
- A step with a risk tag (e.g. architecture+contract review): the epic's `repos`.
|
|
189
|
+
- Stories review: the **union of every story's `repos`** under `stories/`.
|
|
190
|
+
|
|
191
|
+
So one gate, one shape — only the full count changes with the tags:
|
|
192
|
+
- Epic / UI / stories / test-cases reviews (no risk tags): the count asks for 1.
|
|
193
|
+
- Architecture+contract review (`risk_tags: ["contract"]`): the full count asks for **3 distinct
|
|
194
|
+
approvers** (base 1 + contract 2). The base (1) holds the gate; a shortfall against 3 is reported
|
|
195
|
+
without blocking.
|
|
196
|
+
- `stories-review` is an ordinary count gate. It no longer routes per repo.
|
|
36
197
|
|
|
37
198
|
## Staleness
|
|
38
199
|
An approval round is invalidated if the authored artifact was edited after the newest `approved`
|
|
39
200
|
record's date/round. When that happens, drop back to `comment` — reviewers must re-approve the new
|
|
40
201
|
content. This prevents "approve, then quietly change it" (build plan §5 spirit).
|
|
41
202
|
|
|
203
|
+
The engine checks this by content, not by date. A bridge approval records `artifactHash`, the
|
|
204
|
+
fingerprint of what was approved. When the artifact's fingerprint no longer matches, the approval is
|
|
205
|
+
counted as revoked: it stays on disk, it no longer counts, and the gate reports `N approval(s) revoked —
|
|
206
|
+
artifact changed; re-approve`. An approval recorded with no `artifactHash` is never treated as stale.
|
|
207
|
+
A hand-written approval (the `approve` action, on a Product with no platform) carries no fingerprint and
|
|
208
|
+
no platform evidence, so an edit to the artifact does **not** revoke it: after a real change, remove the
|
|
209
|
+
old approval and record it again once the reviewer has seen the new content.
|
|
210
|
+
|
|
42
211
|
For the architecture+contract review there is a second, content-based staleness check: recompute the
|
|
43
212
|
SHA-256 of the contract-surface block and compare it to `.sdlc/contract-lock.json`. A mismatch means
|
|
44
213
|
the locked surface changed even if the file's mtime looks fine — approvals are stale, re-lock and
|
|
@@ -48,24 +217,25 @@ drifted from its lock — run it rather than recomputing by hand.
|
|
|
48
217
|
|
|
49
218
|
## Worked example — epic gate
|
|
50
219
|
|
|
51
|
-
1. `action: open` → `
|
|
52
|
-
|
|
53
|
-
2. Reviewer *bob* leaves comments → captured in the comments file; owner *alice* (pm-assisted)
|
|
220
|
+
1. `action: open` → step `epic-review` set `in_review`; `currentStep = epic-review`.
|
|
221
|
+
2. Reviewer *bob* leaves comments → captured in the comments file; the author *alice* (pm-assisted)
|
|
54
222
|
edits `epic.md`.
|
|
55
|
-
3.
|
|
56
|
-
|
|
57
|
-
4. `action: approve` approver *bob*
|
|
58
|
-
|
|
223
|
+
3. Predicate before any approval: `|approvers|=0` → **fails**. Gate reports "missing: 1 approval(s)".
|
|
224
|
+
*alice* wrote the artifact, so her own approval is not recorded.
|
|
225
|
+
4. `action: approve` approver *bob* → ledger entry added. Predicate: `|approvers|=1` → **base pass**
|
|
226
|
+
(`epic-review` has no risk tags, so the full count is also 1 and nothing is short).
|
|
59
227
|
5. `action: advance` → `epic-review.status=done`, `architecture.status=in_progress`,
|
|
60
228
|
`currentStep=architecture`. Gate reports the advance. The paired authoring step (`epic`) is closed
|
|
61
|
-
too, if it was not already — a gate cannot have passed on an unauthored artifact.
|
|
229
|
+
too, if it was not already — a gate cannot have passed on an unauthored artifact. Each step it closes
|
|
230
|
+
gets a `closed` record: `via: "approved"` on the review step, `via: "review-passed"` on the author step (E18). In solo mode the
|
|
231
|
+
review step's record also carries `waived: "solo"` (E10). `doctor` reports
|
|
62
232
|
any surviving violation as `YAD-STATE-005`; `yad gate repair <epic>` heals it.
|
|
63
233
|
|
|
64
234
|
## Participation record (comments.json)
|
|
65
235
|
`approvals.json` answers "who approved"; `.sdlc/comments.json` answers "who reviewed/commented". The
|
|
66
236
|
gate appends a record per commenter per round on every `comment` action (the machine-readable
|
|
67
237
|
counterpart to the `reviews/*--comments.md` markdown). It does **not** feed the predicate — approvals
|
|
68
|
-
alone decide the gate — but it makes the `approved.md`
|
|
238
|
+
alone decide the gate — but it makes the `approved.md` record's "Reviewed / commented by" section
|
|
69
239
|
attributable, and it is the same shape a future service or the platform bridge can write.
|
|
70
240
|
|
|
71
241
|
## Non-blocking companion comments (`<!-- yad:noblock -->`)
|
|
@@ -77,33 +247,39 @@ marker, and the gate **excludes marked threads** from the unresolved-thread bloc
|
|
|
77
247
|
not "resolve to pass" — it ignores them). A reviewer's *genuine* concern is posted **without** the
|
|
78
248
|
marker and blocks normally, exactly as a `CHANGES_REQUESTED` or any unresolved human thread does.
|
|
79
249
|
|
|
80
|
-
## Platform-backed input (the
|
|
81
|
-
When the
|
|
82
|
-
on a real PR/MR instead of (or as well as) the skill recording it directly.
|
|
83
|
-
|
|
250
|
+
## Platform-backed input (the verified ledger)
|
|
251
|
+
When the Product has a platform (`.sdlc/hub.json`), reviewers can approve/comment
|
|
252
|
+
on a real PR/MR instead of (or as well as) the skill recording it directly. The sync reads that platform
|
|
253
|
+
state with the local user's own `gh`/`glab` and writes the **same**
|
|
84
254
|
`approvals.json` / `comments.json` / `reviews/*.md` records the manual path writes — bridge approvals
|
|
85
|
-
tagged `"source": "bridge"`.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
- On PR/MR open the assignee is the committer and reviewers are the scope's `reviewer` + `domain-owner`
|
|
94
|
-
members (minus the committer); the owner/author is recorded, not requested. See
|
|
255
|
+
tagged `"source": "bridge"`. With a local ledger `yad gate sync` writes them; with a verified ledger
|
|
256
|
+
`yad gate ci` writes them at merge, and a local `yad gate sync` only prints. **The predicate above is
|
|
257
|
+
unchanged**: it counts distinct approvers regardless of how they were recorded.
|
|
258
|
+
|
|
259
|
+
- The `approver` / `commenter` is the platform login that reviewed. There is no lookup and no role.
|
|
260
|
+
- On PR/MR open the assignee is whoever opens it: `@me` on GitHub, and the login `glab` reports on GitLab.
|
|
261
|
+
**No reviewers are requested** — the command prints `no reviewers were requested — ask them on the
|
|
262
|
+
PR itself`. `domain:<repo>` labels for the touched domains are still applied. See
|
|
95
263
|
`../yad-hub-bridge/references/login-roster.md`.
|
|
96
|
-
- `sync` is idempotent (upsert by `(step, approver
|
|
97
|
-
never touches **manual** approvals.
|
|
264
|
+
- `sync` is idempotent (upsert by `(step, approver)`, one record per person; comment records by
|
|
265
|
+
`(step, commenter, round)`, an unchanged round rewritten in place) and never touches **manual** approvals. An older bridge record that carries `role`/`domain` is
|
|
266
|
+
recognised as `../yad-hub-bridge/references/login-roster.md` → "Older records" describes, and replaced
|
|
267
|
+
by one login-named record that keeps its fingerprint. Every sync write (`yad gate sync`, `yad gate ci`)
|
|
268
|
+
also records the login on the older records the roster can place for certain; only those it cannot
|
|
269
|
+
place still need the roster on a later sync. A bridge approval records the
|
|
270
|
+
platform's evidence (`approvedAt`, and on GitHub `commit`, `url`, `reviewId`) — for the record only. A revoked approval is superseded **while the step is open**; once
|
|
98
271
|
the step is `done` its approvals are kept as the record of why it passed, and a re-sync only re-binds
|
|
99
272
|
new ones (see `../yad-hub-bridge/references/bridge.md` → "Idempotent re-sync").
|
|
100
|
-
- The architecture+contract staleness rule applies to bridge approvals too: a re-lock
|
|
101
|
-
approvals
|
|
102
|
-
- No platform / no CLI → the gate runs
|
|
273
|
+
- The architecture+contract staleness rule applies to bridge approvals too: a re-lock changes the
|
|
274
|
+
surface fingerprint, so bridge approvals of the old surface stop counting (they stay on disk as revoked).
|
|
275
|
+
- No platform / no CLI → the gate runs local with no error. Detail: `../yad-hub-bridge/references/bridge.md`.
|
|
103
276
|
|
|
104
277
|
## Why this shape
|
|
105
|
-
-
|
|
106
|
-
a second pair of eyes (priority 1, code quality / production safety).
|
|
107
|
-
-
|
|
108
|
-
|
|
278
|
+
- One approver who is not the author keeps review load low on a small team (design priority 2) while
|
|
279
|
+
still requiring a second pair of eyes (priority 1, code quality / production safety).
|
|
280
|
+
- The risk step asks for more people only where a change can break a shared surface
|
|
281
|
+
(contract/auth/payments). It is advisory for now: the capacity cap (E72) is reported. A later change will
|
|
282
|
+
enforce it together with `yad gate lower --reason`, so that a gate asking for more people than the team
|
|
283
|
+
has will always have a way out. Until then E73 only warns when a gate may not pass.
|
|
284
|
+
- No stored list of people: it goes stale, and repository access already decides who can approve.
|
|
109
285
|
- Everything is a file, so a future service can drive the same gate by writing the same records.
|
package/skills/yad-run/SKILL.md
CHANGED
|
@@ -1,26 +1,25 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: yad-run
|
|
3
|
-
description: 'Phase 4 (automation) — the orchestrator that makes the second dial real. Drives a story''s
|
|
3
|
+
description: 'Phase 4 (automation) — the orchestrator that makes the second dial real. Drives a story''s Build loop (spec → tasks → implement → checks) in one code repo, reading each step''s advance dial from build-state: on `auto` it advances on its own, on `human` it stops for a human. Records every run in the trust log, the run record `yad dial` shows as advice. With `checks` on auto, a clean gate pass advances to engineer-review; any failure, scope overrun, or contract-surface touch HALTS and pulls in a human. Also sets a step''s dial (`yad dial`, nothing to earn since E34) and flips the kill switch (`yad kill` / `yad unkill`). Never advances a Shape step or the engineer review. Use when the user says "run Build", "advance story <id>", "set the checks dial", or "kill switch".'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# SDLC — Run (Phase 4 orchestrator)
|
|
7
7
|
|
|
8
|
-
**Goal:** Be the **engine** that the
|
|
9
|
-
config; this skill reads it and acts. For ONE story in ONE code repo, walk the
|
|
8
|
+
**Goal:** Be the **engine** that the advance dial finally drives. Until Phase 4 the dial was inert
|
|
9
|
+
config; this skill reads it and acts. For ONE story in ONE code repo, walk the Build steps —
|
|
10
10
|
`spec → tasks → implement → checks → engineer-review` — and at each step either **advance on its own**
|
|
11
|
-
(dial `
|
|
12
|
-
condition). Every run is recorded in the **trust log
|
|
11
|
+
(dial `advance: auto`, step succeeded) or **stop for a human** (dial `advance: human`, or any halt
|
|
12
|
+
condition). Every run is recorded in the **trust log** — the run record `yad dial` shows the team, as
|
|
13
|
+
advice, when it sets a dial.
|
|
13
14
|
|
|
14
15
|
This is the most dangerous skill in the system, so it is built to **halt-and-escalate over guess**:
|
|
15
16
|
a failing check, ambiguity, a scope overrun, or a contract-surface touch stops the loop and pulls in a
|
|
16
|
-
human regardless of any dial.
|
|
17
|
-
|
|
17
|
+
human regardless of any dial. This skill **drives Build only**: it never drives a Shape step, and the
|
|
18
|
+
**engineer review is a gate** — always a person.
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
hooks but stay `human_approve` until their own evidence clears the threshold — there is no historical
|
|
23
|
-
signal to seed them from, so they are earned only on real runs.
|
|
20
|
+
**Nothing has to be earned (E34).** Automation used to be unlocked per step once its trust log cleared a
|
|
21
|
+
threshold. Now the team sets each lane step's dial with `yad dial`, which prints that step's run record
|
|
22
|
+
beside it as advice and never refuses on it. The kill switch (`yad kill`) holds every step at `human`.
|
|
24
23
|
|
|
25
24
|
## Conventions
|
|
26
25
|
|
|
@@ -28,16 +27,17 @@ signal to seed them from, so they are earned only on real runs.
|
|
|
28
27
|
truth: it holds the story, the build-state, and the trust log).
|
|
29
28
|
- Code repos are separate git repos under `{project-root}/demo-repos/<repo>/`
|
|
30
29
|
(`config.yaml` `build.code_repos_root`). Operate inside them with absolute paths.
|
|
31
|
-
- Automation config is
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
- Automation config is `.sdlc/config.yaml` → `automation:` (`back_steps`, `default`). The kill
|
|
31
|
+
switch and the Shape dials live in `.sdlc/automation.json`, and are read and written **only through the
|
|
32
|
+
engine** — `yad dial … --json`, `yad kill`, `yad unkill`. Never read or edit either file for a dial.
|
|
33
|
+
- Per-story Build state: `epics/<epic>/.sdlc/build-state/<story-id>.json` (per repo).
|
|
34
34
|
- Trust ledger: **shard-then-fold** — each run is its own shard file
|
|
35
35
|
`epics/<epic>/.sdlc/trust-log/<story>-<repo>-<step>-<uid>.json` (a fresh `uid` per run, so concurrent
|
|
36
36
|
writers never conflict); readers UNION the folded `trust-log.json` with every loose shard, and
|
|
37
37
|
`yad tidy up` folds finished shards back into `trust-log.json`. Schemas:
|
|
38
38
|
`../yad-epic/references/state-schema.md`.
|
|
39
|
-
- These machine-written
|
|
40
|
-
committed by **`yad checkpoint`** — the
|
|
39
|
+
- These machine-written Build files (`build-state/<story>.json`, the `trust-log/` shards) are
|
|
40
|
+
committed by **`yad checkpoint`** — the Build analogue of the Shape `yad gate` sync; the loop
|
|
41
41
|
calls it each iteration so the state is durable and shared without a human commit. (`yad checkpoint`
|
|
42
42
|
stages the shard dirs; `yad tidy up` later folds finished shards — loose objects + `git gc`.)
|
|
43
43
|
- The orchestrator **calls the existing step skills unchanged** — `yad-spec` (A), `yad-implement`
|
|
@@ -52,78 +52,108 @@ signal to seed them from, so they are earned only on real runs.
|
|
|
52
52
|
- `action` — `run` (default) | `set-dial` | `kill` | `unkill`.
|
|
53
53
|
- For `run`: optional `from` (the step id to start at; default the repo's `currentStep` in
|
|
54
54
|
build-state) and `task` (the atomic task id for the `implement`/`checks` legs).
|
|
55
|
-
- For `set-dial`: `step` (a `back_steps` id), `to`
|
|
55
|
+
- For `set-dial`: `step` (a `back_steps` id), `to` — `human` | `auto` (the older `human_approve` |
|
|
56
|
+
`machine_advance` mean the same two things).
|
|
57
|
+
- For `kill`: `reason` (required). For `unkill`: an optional `reason`.
|
|
56
58
|
|
|
57
59
|
## On Activation
|
|
58
60
|
|
|
59
61
|
### Step 0 — Load state
|
|
60
|
-
Read `config.yaml` `automation
|
|
61
|
-
`back_steps` defaults if absent — all `human_approve
|
|
62
|
-
|
|
62
|
+
Read `config.yaml` `automation` (for `back_steps`), the story's `build-state/<story>.json` (create it from the
|
|
63
|
+
`back_steps` defaults if absent — all `advance: human` (`automation: human_approve`), `engineer-review` `locked:true`, and
|
|
64
|
+
**write it to disk before the loop starts**, because `yad next` reads that file to know this lane
|
|
65
|
+
exists), and `trust-log.json` (treat missing as `[]`). Resolve the code repo.
|
|
66
|
+
|
|
67
|
+
If the file exists but has **no entry for this `repo`**, add that repo's entry from the same `back_steps`
|
|
68
|
+
defaults — the file may already hold another repo, or a skipped lane — and never remove an entry you did
|
|
69
|
+
not add. If this repo's entry is **`status: skipped`** (E39: the whole lane was set aside with
|
|
70
|
+
`yad skip <epic> <story> --repo <repo>`, because the story needs no change in this repo), **do not drive
|
|
71
|
+
it**: stop, report the recorded `record.reason`, and point at `yad unskip <epic> <story> --repo <repo>` if
|
|
72
|
+
the lane is owed after all. **Never write a Build step as `skipped` or `deferred`**: a lane is skipped
|
|
73
|
+
whole or not at all, and `yad doctor` reports a single step set aside.
|
|
74
|
+
|
|
75
|
+
Also read **which skill runs each step** — `yad skill list --json`, whose `steps[]` gives each step id
|
|
76
|
+
its `skills` array. That is the project's choice (`.sdlc/skills.json`, E6) and the engine's default
|
|
77
|
+
when it has made none. Read it once here; it does not change during a run.
|
|
63
78
|
|
|
64
79
|
### `action: run` — drive the loop
|
|
65
80
|
Walk the steps for `repo` starting at `from`/`currentStep`. For each step:
|
|
66
81
|
|
|
67
|
-
1. **Run the step's skill
|
|
68
|
-
`
|
|
82
|
+
1. **Run the step's skill — use the list from Step 0, never a name written here.** Look the step id
|
|
83
|
+
up in `yad skill list --json` and run every entry of its `skills` **in order**, each one seeing what
|
|
84
|
+
the one before it produced; the last output is the result. (`yad next <epic> --json` gives the same
|
|
85
|
+
answer per lane once `build-state/<story>.json` is on disk, if you already have it open.) Which
|
|
86
|
+
skill runs a step is the project's setting, so a hardcoded name here would make this loop do one
|
|
87
|
+
thing while `yad next` tells the user another — on the same project, with the automation half
|
|
88
|
+
winning. Unbound, the engine answers `spec`→`yad-spec`, `tasks`→ the tasks leg of `yad-spec`,
|
|
89
|
+
`implement`→`yad-implement`, `checks`→`yad-checks`, which is what this step used to say. Pass
|
|
90
|
+
`action: run` to `yad-checks`. Capture the result.
|
|
69
91
|
2. **Derive trust signals & write a trust-log shard** — write the entry to its own file
|
|
70
92
|
`trust-log/<story>-<repo>-<step>-<uid>.json` (a fresh `uid` per run; never append to a shared file),
|
|
71
93
|
with `ranBy: machine` if this advance was automated, else `human` — see `references/run-loop.md` for
|
|
72
94
|
the derivation. Do this for *every* step run, pass or fail; the log is the evidence base.
|
|
73
|
-
3. **
|
|
74
|
-
|
|
75
|
-
|
|
95
|
+
3. **Ask the engine for the effective dial:** `yad dial <epic> <story> --repo <repo> <step> --json`, and use
|
|
96
|
+
its `advance`. It is `human` whenever the step is a gate (`why: gate` — a read never refuses one), the kill switch
|
|
97
|
+
is on, or `.sdlc/automation.json` cannot be read (`automationError` says so) — so never work the dial out from the files yourself.
|
|
76
98
|
4. **Decide:**
|
|
77
99
|
- **HALT** if the step failed — any check FAIL, a scope overrun (`yad-implement` stopped on the
|
|
78
|
-
file-boundary rule), a contract-surface touch, or any ambiguity. Set the step `status: blocked
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
100
|
+
file-boundary rule), a contract-surface touch, or any ambiguity. Set the step `status: blocked`
|
|
101
|
+
**with a `record`** — `{ "reason": "<the halt cause>", "by": "<login or null>", "date":
|
|
102
|
+
"<YYYY-MM-DD>" }` — write the `rejected` trust entry, **stop the loop**, and report what a human
|
|
103
|
+
must resolve. The record is not decoration: since shape 7 a `blocked` step with nothing recorded
|
|
104
|
+
on it reads as `todo` ("not started"), so a halt written without one is indistinguishable from a
|
|
105
|
+
lane nobody has begun. `yad doctor` reports the difference as `step:no-record`.
|
|
106
|
+
- else if effective dial is **`advance: auto`** → set the step `done` **with a `closed`** —
|
|
107
|
+
`{ "by": "<login or null>", "date": "<YYYY-MM-DD>", "via": "auto", "run": "<the trust-log uid>" }` —
|
|
108
|
+
advance `currentStep` to the next step, and **continue the loop** (this is the Step B auto-advance
|
|
109
|
+
for `checks`).
|
|
110
|
+
- else (**`advance: human`**) → set the step `done`/`in_review`, and give a `done` step a `closed` —
|
|
111
|
+
`{ "by": "<login or null>", "date": "<YYYY-MM-DD>", "via": "human", "run": "<the trust-log uid>" }` —
|
|
112
|
+
then **stop** and report "waiting for a human at `<next-step>`".
|
|
113
|
+
- `closed` is the step's **closing record** (E18): how the lane moved past it, and which run did it.
|
|
114
|
+
Never write one over a `closed` already on the step. See `references/run-loop.md`.
|
|
84
115
|
5. **Always stop at `engineer-review`** (it is `locked`): hand off to `yad-engineer-review` for the human merge
|
|
85
116
|
gate, which finalizes the trust verdict (confirm/override the provisional one).
|
|
86
117
|
|
|
87
118
|
**Commit the machine-written state.** After each iteration's writes (the trust-log shard in 2 and the
|
|
88
119
|
build-state change in 4), run `yad checkpoint --push` from `{project-root}`. It commits *only* the
|
|
89
|
-
`trust-log/` shards + `build-state/<story>.json` (never a
|
|
120
|
+
`trust-log/` shards + `build-state/<story>.json` (never a Shape gate file) as one `chore(hub): …`
|
|
90
121
|
audit-trail commit, and only ever on the default branch. It is a safe no-op when nothing changed, so
|
|
91
122
|
call it every iteration — teammates don't review these machine writes, but CI and `yad status` on
|
|
92
123
|
other machines must see current trust evidence. Never run it off the default branch (it will refuse):
|
|
93
|
-
an unpushed or branch-stranded trust log
|
|
94
|
-
|
|
95
|
-
### `action: set-dial` —
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
evidence.
|
|
105
|
-
- `to: human_approve` is **always allowed** (reverting automation is one move, never gated).
|
|
124
|
+
an unpushed or branch-stranded trust log leaves the run record `yad dial` shows out of date.
|
|
125
|
+
|
|
126
|
+
### `action: set-dial` — set (or revert) a step's advance dial
|
|
127
|
+
Run `yad dial <epic> <story> --repo <repo> <step> --to <human|auto>` (map `human_approve` → `human` and
|
|
128
|
+
`machine_advance` → `auto` first). The engine writes both dial names on the lane step, refuses a gate and
|
|
129
|
+
a lane step `yad-run` has not written yet, and prints the step's run record in that repo **as advice**.
|
|
130
|
+
Relay its output. There is **no threshold** (E34): the team decides, and "it seems fine" is theirs to
|
|
131
|
+
judge. `--to human` is always accepted. Never edit `build-state` for a dial yourself.
|
|
132
|
+
|
|
133
|
+
For a Shape author step the team wants on auto, point at `yad dial <step> --to auto` — project-wide, and
|
|
134
|
+
recorded only: this loop never drives a Shape step.
|
|
106
135
|
|
|
107
136
|
### `action: kill` / `action: unkill` — the kill switch
|
|
108
|
-
|
|
109
|
-
**every** step's effective dial is `
|
|
110
|
-
|
|
137
|
+
Run `yad kill --reason "<why>"` or `yad unkill [--reason "<why>"]`. The engine records who, when and why
|
|
138
|
+
in `.sdlc/automation.json`; while the switch is on, **every** step's effective dial is `human`. Tell the
|
|
139
|
+
user to commit that file so every machine and CI sees it. Never set a `kill_switch` in `config.yaml`:
|
|
140
|
+
nothing reads it, and `yad doctor` fails on one left `true`.
|
|
111
141
|
|
|
112
142
|
## Hard rules (phase-4-build-plan.md)
|
|
113
143
|
|
|
114
|
-
- **
|
|
115
|
-
|
|
116
|
-
- **Reversible in one move.** `
|
|
117
|
-
|
|
144
|
+
- **The dial is the team's (E34).** `yad dial` sets it and shows the run record as advice; nothing is
|
|
145
|
+
earned, and nothing is refused on evidence. A gate is never auto.
|
|
146
|
+
- **Reversible in one move.** `yad dial … --to human` is never refused; `yad kill --reason "<why>"` holds
|
|
147
|
+
every step at human, with no code change.
|
|
118
148
|
- **Halt-and-escalate beats guess.** A failing check, ambiguity, scope overrun, or contract-surface
|
|
119
149
|
touch halts the loop and pulls in a human, regardless of the dial.
|
|
120
|
-
- **
|
|
121
|
-
|
|
150
|
+
- **This skill never drives a Shape step, and never passes the engineer review.** The review is a gate,
|
|
151
|
+
and the kill switch holds every step at human.
|
|
122
152
|
- **The orchestrator never changes what a step does** — it calls the existing skills and owns only the
|
|
123
153
|
advance decision and the trust record.
|
|
124
154
|
|
|
125
155
|
## Reference
|
|
126
|
-
- The loop
|
|
156
|
+
- The loop and the trust-verdict derivation: `references/run-loop.md`.
|
|
127
157
|
- State/trust schemas: `../yad-epic/references/state-schema.md`.
|
|
128
158
|
- The steps it drives: `../yad-spec/`, `../yad-implement/`, `../yad-checks/`; the human gate it
|
|
129
159
|
hands off to: `../yad-engineer-review/`.
|