mandrel 2.9.0 → 2.11.0
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/.agents/agents/.markdownlint.json +4 -0
- package/.agents/agents/acceptance-critic.md +30 -5
- package/.agents/agents/auditor.md +36 -19
- package/.agents/agents/plan-critic.md +31 -5
- package/.agents/agents/story-worker.md +91 -100
- package/.agents/docs/configuration.md +39 -25
- package/.agents/docs/execution-reference.md +13 -0
- package/.agents/docs/workflows.md +1 -1
- package/.agents/instructions.md +131 -265
- package/.agents/rules/git-conventions.md +47 -83
- package/.agents/rules/orchestration-error-handling.md +28 -0
- package/.agents/schemas/agentrc.schema.json +36 -9
- package/.agents/schemas/validation-evidence.schema.json +3 -1
- package/.agents/scripts/acceptance-eval.js +10 -6
- package/.agents/scripts/apply-quality-bootstrap.js +1 -1
- package/.agents/scripts/check-test-temp-hygiene.js +438 -0
- package/.agents/scripts/deliver-recover.js +23 -6
- package/.agents/scripts/lib/audit-suite/index.js +5 -0
- package/.agents/scripts/lib/audit-suite/lens-diff-floor.js +179 -0
- package/.agents/scripts/lib/audit-suite/selector.js +1 -1
- package/.agents/scripts/lib/baselines/env-overrides.js +33 -0
- package/.agents/scripts/lib/baselines/git-base.js +0 -0
- package/.agents/scripts/lib/baselines/preview-gates.js +5 -0
- package/.agents/scripts/lib/config/gates/maintainability.schema.js +10 -1
- package/.agents/scripts/lib/config/quality.js +13 -0
- package/.agents/scripts/lib/config/temp-paths.js +121 -1
- package/.agents/scripts/lib/config-settings-schema-delivery.js +30 -0
- package/.agents/scripts/lib/config-settings-schema.js +12 -16
- package/.agents/scripts/lib/observability/metrics-ledger.js +217 -0
- package/.agents/scripts/lib/observability/runtime-friction.js +7 -0
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +45 -0
- package/.agents/scripts/lib/orchestration/check-baselines/phases/evaluate.js +97 -4
- package/.agents/scripts/lib/orchestration/check-baselines/phases/parse-args.js +7 -0
- package/.agents/scripts/lib/orchestration/complexity-gate.js +533 -93
- package/.agents/scripts/lib/orchestration/deliver-recover.js +137 -10
- package/.agents/scripts/lib/orchestration/merge-block-class.js +36 -15
- package/.agents/scripts/lib/orchestration/merge-poll.js +213 -0
- package/.agents/scripts/lib/orchestration/plan-context.js +119 -3
- package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +182 -9
- package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +29 -2
- package/.agents/scripts/lib/orchestration/plan-metrics.js +31 -82
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +153 -2
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +221 -14
- package/.agents/scripts/lib/orchestration/resolve-stories.js +12 -1
- package/.agents/scripts/lib/orchestration/review-depth.js +9 -4
- package/.agents/scripts/lib/orchestration/review-providers/native.js +34 -16
- package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +8 -3
- package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +230 -79
- package/.agents/scripts/lib/orchestration/spec-budget.js +78 -0
- package/.agents/scripts/lib/orchestration/story-body-gate.js +72 -0
- package/.agents/scripts/lib/orchestration/story-close/phases/local-lens-review.js +89 -1
- package/.agents/scripts/lib/orchestration/story-close/phases/review-core.js +73 -0
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +6 -0
- package/.agents/scripts/lib/orchestration/ticket-validator.js +18 -62
- package/.agents/scripts/lib/templates/decomposer-prompts.js +13 -6
- package/.agents/scripts/lib/test-env.js +65 -0
- package/.agents/scripts/plan-context.js +84 -9
- package/.agents/scripts/plan-critics.js +115 -3
- package/.agents/scripts/plan-persist.js +11 -1
- package/.agents/scripts/plan-run-epilogue.js +1 -1
- package/.agents/scripts/resolve-stories.js +2 -0
- package/.agents/scripts/single-story-confirm-merge.js +65 -5
- package/.agents/scripts/stories-wave-tick.js +1 -1
- package/.agents/workflows/deliver.md +88 -230
- package/.agents/workflows/helpers/acceptance-self-eval.md +16 -5
- package/.agents/workflows/helpers/deliver-reference.md +171 -0
- package/.agents/workflows/helpers/deliver-story-reference.md +223 -0
- package/.agents/workflows/helpers/deliver-story.md +115 -432
- package/.agents/workflows/helpers/plan-reference.md +246 -0
- package/.agents/workflows/plan.md +108 -304
- package/docs/CHANGELOG.md +46 -0
- package/lib/cli/registry.js +31 -14
- package/lib/migrations/index.js +2 -0
- package/lib/migrations/steps/2.11.0-retire-max-seed-words.js +92 -0
- package/package.json +1 -1
|
@@ -7,20 +7,26 @@ description:
|
|
|
7
7
|
|
|
8
8
|
# /deliver <storyId...>
|
|
9
9
|
|
|
10
|
+
> **Lean spine.** Happy path + gate list. Sequencing edge cases, dispatch
|
|
11
|
+
> mechanics, lite-route inline execution, checklist threading, ceremony, and
|
|
12
|
+
> the per-run epilogue live in the on-demand
|
|
13
|
+
> [`helpers/deliver-reference.md`](helpers/deliver-reference.md).
|
|
14
|
+
|
|
10
15
|
## Role
|
|
11
16
|
|
|
12
|
-
Single delivery path
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
merges.
|
|
17
|
+
Single delivery path, single input shape: **a list of Story ids**. `/deliver`
|
|
18
|
+
owns input resolution and sequencing only — every Story runs through
|
|
19
|
+
[`helpers/deliver-story.md`](helpers/deliver-story.md). No Epic wave loop, no
|
|
20
|
+
`epic/<id>` integration branch, no `--no-ff` wave merges.
|
|
17
21
|
|
|
18
22
|
The dependency graph is **discovered, not declared**: `resolve-stories.js`
|
|
19
|
-
reads it from live state (body edges ∪ native GitHub `blocked_by` edges,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
reads it from live state (body edges ∪ native GitHub `blocked_by` edges, every
|
|
24
|
+
blocker resolved against its real issue state). You never hand it a graph, and
|
|
25
|
+
there is no batch label — which is what lets you deliver Stories **across plan
|
|
26
|
+
runs and over time**. The `plan-run::<id>` grouping label is filter metadata
|
|
27
|
+
only — never a resolution input (there is no `--run` or `--dep` axis).
|
|
28
|
+
Per-Story routes are **body-derived** too (#4722); `route::lite` is a hint
|
|
29
|
+
only.
|
|
24
30
|
|
|
25
31
|
## Inputs
|
|
26
32
|
|
|
@@ -29,55 +35,39 @@ weeks ago in a different run is simply ready.
|
|
|
29
35
|
| `/deliver <storyId>` | Deliver one Story via `helpers/deliver-story.md`. |
|
|
30
36
|
| `/deliver <storyId> <storyId> ...` | Resolve the set with `resolve-stories.js`, then sequence by the discovered graph via `stories-wave-tick.js`. Default concurrency is **3**. |
|
|
31
37
|
|
|
32
|
-
Any named ticket that is not `type::story`, or
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
silently dropping the offending id and under-delivering.
|
|
36
|
-
|
|
37
|
-
> **No batch identity (Story #4540).** There is no `--run`, `plan-run::<id>`,
|
|
38
|
-
> or `--dep` axis: ordering lives in the dependency edges, so delivering the
|
|
39
|
-
> ids resolves the graph itself.
|
|
38
|
+
Any named ticket that is not `type::story`, or still carrying an `Epic: #N`
|
|
39
|
+
footer, is a **hard error** naming the id and the fix (close or re-plan as a v2
|
|
40
|
+
Story). Resolution refuses the whole set rather than silently under-delivering.
|
|
40
41
|
|
|
41
42
|
## Flags
|
|
42
43
|
|
|
43
44
|
| Flag | Meaning |
|
|
44
45
|
| --- | --- |
|
|
45
|
-
| `--concurrency <n>` | **Optional** per-run override of the
|
|
46
|
+
| `--concurrency <n>` | **Optional** per-run override of the fan-out cap. Omit it to honor `delivery.deliverRunner.concurrencyCap` (config default **3**, including any `.agentrc.local.json` override); pass it **only** for a one-run cap. `1` = sequential. |
|
|
46
47
|
| `--yes` | Suppress the multi-Story confirmation gate. |
|
|
47
48
|
| `--steal` | Forwarded to `single-story-init.js` / lease steal. |
|
|
48
|
-
| `--wait-merge` | Force close-and-land (the default; `delivery.routing.closeAndLand
|
|
49
|
-
| `--no-wait-merge` | Opt out
|
|
49
|
+
| `--wait-merge` | Force close-and-land (the default; `delivery.routing.closeAndLand`). |
|
|
50
|
+
| `--no-wait-merge` | Opt out; stop at `agent::closing` for a human land. |
|
|
50
51
|
|
|
51
52
|
**Operator-merge implies no-wait.** `--no-auto-merge` and
|
|
52
|
-
`delivery.ci.autoMerge: "strict"`
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
blocks, because that is a fault to report rather than an operator decision
|
|
58
|
-
to respect.
|
|
53
|
+
`delivery.ci.autoMerge: "strict"` leave the PR un-armed: the Story rests at
|
|
54
|
+
`agent::closing` for the human merge and is **not** flipped to `agent::blocked`
|
|
55
|
+
(`--wait-merge` does not override this). A genuine *arm failure* differs — it
|
|
56
|
+
still waits and still blocks, because that is a fault to report, not an operator
|
|
57
|
+
decision to respect.
|
|
59
58
|
|
|
60
59
|
## Procedure
|
|
61
60
|
|
|
62
61
|
1. **Resolve the set.** One command, for one Story or many:
|
|
62
|
+
`node .agents/scripts/resolve-stories.js --ids <id,id,...>`. It validates
|
|
63
|
+
the set and shows what will run: read `stories[]`, `dag[]`, and `done[]` to
|
|
64
|
+
present the order in step 2. You do **not** thread them into step 3 — the
|
|
65
|
+
tick re-resolves the graph itself every beat. Resolution hard-errors
|
|
66
|
+
(exit 1) on a named id that is not a Story, carries an `Epic: #N` footer, or
|
|
67
|
+
whose native edges cannot be read — a missing gate would co-dispatch a Story
|
|
68
|
+
against an unlanded blocker.
|
|
63
69
|
|
|
64
|
-
|
|
65
|
-
node .agents/scripts/resolve-stories.js --ids <id,id,...>
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
This validates the set and shows the operator what will run: read
|
|
69
|
-
`stories[]`, `dag[]`, and `done[]` to present the order in step 2. You do
|
|
70
|
-
**not** thread them into step 3 — the tick re-resolves the graph itself
|
|
71
|
-
from the same machinery, every beat. Do **not** rebuild the graph by hand;
|
|
72
|
-
it is discovered from live state, including edges a body does not spell
|
|
73
|
-
out and blockers outside the delivered set.
|
|
74
|
-
|
|
75
|
-
Resolution hard-errors (exit 1) on a named id that is not a Story, still
|
|
76
|
-
carries an `Epic: #N` footer, or whose native dependency edges cannot be
|
|
77
|
-
read. A failed edge read is fatal by design: a missing gate would
|
|
78
|
-
co-dispatch a Story against an unlanded blocker.
|
|
79
|
-
|
|
80
|
-
2. **Confirm (N>1).** Present the order and wait unless `--yes`.
|
|
70
|
+
2. **Confirm (N>1).** Present the order; wait unless `--yes`.
|
|
81
71
|
|
|
82
72
|
3. **Sequence.** Loop until the tick reports `epilogueDue: true`:
|
|
83
73
|
|
|
@@ -88,218 +78,86 @@ to respect.
|
|
|
88
78
|
```
|
|
89
79
|
|
|
90
80
|
**Do not add `--concurrency` unless the operator explicitly asked for a
|
|
91
|
-
per-run cap.** Omitting it
|
|
81
|
+
per-run cap.** Omitting it lets the tick resolve the cap from
|
|
92
82
|
`delivery.deliverRunner.concurrencyCap` — including a `.agentrc.local.json`
|
|
93
|
-
override. An explicit `--concurrency <n>` wins over config for that run
|
|
94
|
-
(`
|
|
95
|
-
filling in a literal — e.g. the documented default `3` — silently defeats
|
|
96
|
-
the operator's configured override. Thread `--concurrency` through here
|
|
97
|
-
only when it was passed to `/deliver`.
|
|
98
|
-
|
|
99
|
-
Each beat re-probes live state: it re-resolves the graph, classifies done
|
|
100
|
-
(`agent::done` or a closed issue — including foreign blockers that landed
|
|
101
|
-
in another run), and derives in-flight from live `agent::executing` /
|
|
102
|
-
`agent::closing` labels. You never compute `done` or `in-flight` — that
|
|
103
|
-
accounting is read from reality every beat (Story #4594).
|
|
83
|
+
override. An explicit `--concurrency <n>` wins over config for that run, so a
|
|
84
|
+
filled-in literal (e.g. `3`) silently defeats the operator's override.
|
|
104
85
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
it back and a second sub-agent would join the first on the same branch and
|
|
113
|
-
worktree, interleaving commits. `--dispatched` closes that residual
|
|
114
|
-
same-run window.
|
|
115
|
-
|
|
116
|
-
**Cross-run de-confliction is automatic (Story #4620).** A Story another
|
|
117
|
-
operator is delivering is withheld without any bookkeeping from you: the
|
|
118
|
-
probe reads the Story's assignee lease and, when it belongs to a different
|
|
119
|
-
operator, withholds the Story and reports it in the envelope's
|
|
120
|
-
`foreignHeld: [{ id, holder }]` (with `foreignHeldReason`). That is not a
|
|
121
|
-
failure or a wedge — the holder's run owns the branch, and this run picks
|
|
122
|
-
the Story up automatically once their lease clears. Init is the backstop:
|
|
123
|
-
it refuses a Story already labelled `agent::executing`, or one whose lease a
|
|
124
|
-
different operator holds, unless you pass `--steal`. Assignee-based
|
|
125
|
-
withholding needs `github.operatorHandle` set (in `.agentrc.local.json`);
|
|
126
|
-
without it the probe logs a warning and leans on init's lease refusal alone.
|
|
127
|
-
|
|
128
|
-
The rule is **append-only: add each id as you dispatch it and never remove
|
|
129
|
-
one.** The flag is additive, not authoritative — the probe unions it into
|
|
130
|
-
the label-derived set and then filters it against live state, so an id that
|
|
131
|
-
has since gone `agent::done` is dropped for you. Re-listing an id costs
|
|
132
|
-
nothing and cannot double-count a slot; *omitting* one is the only way to
|
|
133
|
-
get this wrong. This is why `--dispatched` is not the `--done` bookkeeping
|
|
134
|
-
#4594 retired, and why `--in-flight` remains rejected under `--probe-live`.
|
|
86
|
+
Each beat re-probes live state to derive done / in-flight itself; you never
|
|
87
|
+
compute them (Story #4594). `--dispatched` is the one thing you must supply —
|
|
88
|
+
the append-only list of every id you spawned this run — and cross-run
|
|
89
|
+
de-confliction via the assignee lease is automatic
|
|
90
|
+
([`helpers/deliver-reference.md` § Sequencing edge cases](helpers/deliver-reference.md);
|
|
91
|
+
[§ Dispatch mechanics](helpers/deliver-reference.md) covers role-scoped
|
|
92
|
+
spawn, lite-route execution, and `checklistPath`).
|
|
135
93
|
|
|
136
94
|
Branch on the exit code:
|
|
137
|
-
- **0** — dispatch each `ready` id (
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
(
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
A blocked Story outranks a wedge (its blockers are moot while a human
|
|
161
|
-
owes a decision) but not a cycle (exit 2 — fix the graph first).
|
|
162
|
-
|
|
163
|
-
**Dispatch each `ready` Story (role-scoped by default).** When
|
|
164
|
-
`delivery.routing.roleScopedAgents` is enabled (the **default**) and the
|
|
165
|
-
host exposes agent dispatch, spawn each ready Story as its own
|
|
166
|
-
`subagent_type: story-worker` sub-agent — it boots on the role-scoped
|
|
167
|
-
[`story-worker`](../agents/story-worker.md) context (its own system prompt,
|
|
168
|
-
no `CLAUDE.md` @-closure) carrying the load-bearing delivery MUSTs
|
|
169
|
-
standalone. The sub-agent executes
|
|
170
|
-
[`helpers/deliver-story.md`](helpers/deliver-story.md) end to end
|
|
171
|
-
(init → implement → acceptance self-eval → close-and-land). Thread into its
|
|
172
|
-
prompt:
|
|
173
|
-
- `storyId` — the id to deliver.
|
|
174
|
-
- `docsDigestPath` — the per-run docs digest (digest-first reading,
|
|
175
|
-
[`instructions.md` § 3](../instructions.md)); null when
|
|
176
|
-
`project.docsContextFiles` is unset.
|
|
177
|
-
- `checklistPath` — the footprint-matched write-time audit checklist,
|
|
178
|
-
produced at dispatch (below).
|
|
179
|
-
- the **change-set discipline** — the worker computes the change set once
|
|
180
|
-
with `computeChangeSet` and hands that one list to every acceptance critic
|
|
181
|
-
(Story #4593); it never lets a critic re-derive the diff.
|
|
182
|
-
|
|
183
|
-
**Produce `checklistPath` before the spawn (Story #4627).** Compute the
|
|
184
|
-
payload from the Story's predicted footprint (its `changes[]` /
|
|
185
|
-
`references[]` path entries) with `buildDispatchChecklist` and write it to
|
|
186
|
-
the run temp dir, then thread the resulting path (empty when nothing
|
|
187
|
-
matched):
|
|
188
|
-
|
|
189
|
-
```bash
|
|
190
|
-
node --input-type=module -e '
|
|
191
|
-
import { buildDispatchChecklist } from "<main-repo>/.agents/scripts/lib/audit-suite/index.js";
|
|
192
|
-
import { parse } from "<main-repo>/.agents/scripts/lib/story-body/story-body.js";
|
|
193
|
-
// storyBody is the fetched Story issue body.
|
|
194
|
-
const { changes, references } = parse(process.env.STORY_BODY);
|
|
195
|
-
const { checklistPath } = buildDispatchChecklist({
|
|
196
|
-
storyId: <storyId>, changes, references, runTempDir: "temp/run-<id>",
|
|
197
|
-
});
|
|
198
|
-
console.log(checklistPath ?? "");
|
|
199
|
-
'
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
`buildDispatchChecklist` (`lib/audit-suite/dispatch-checklist.js`) is a pure
|
|
203
|
-
function of the footprint and the on-disk checklists; an empty match prints
|
|
204
|
-
nothing and the worker runs with no write-time checklist — the maker-blind
|
|
205
|
-
close-scope pass still covers it.
|
|
206
|
-
|
|
207
|
-
**Inline fallback (`roleScopedAgents: false` / no-nesting harness).** When
|
|
208
|
-
the kill-switch is off, or the host cannot spawn a sub-agent at this nesting
|
|
209
|
-
depth, do **not** stall: read
|
|
210
|
-
[`helpers/deliver-story.md`](helpers/deliver-story.md) **in full** and
|
|
211
|
-
execute it directly, in this turn, threading the same `docsDigestPath` /
|
|
212
|
-
`checklistPath` / change-set discipline. Under `--yes` / injected helper
|
|
213
|
-
content, execute directly without a re-read turn. The engine, gates, and
|
|
214
|
-
terminal envelope are identical either way — only the isolation differs.
|
|
215
|
-
|
|
216
|
-
4. **Per-run epilogue (N>1).** Once step 3 reports `epilogueDue: true`
|
|
217
|
-
(every Story done), keyed on the delivered id set:
|
|
218
|
-
|
|
219
|
-
```bash
|
|
220
|
-
node .agents/scripts/plan-run-epilogue.js --stories 101,102
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
This executes, in order:
|
|
224
|
-
- `audit-roster` — selects cross-Story audit lenses over the combined
|
|
225
|
-
landed tip and posts `plan-run-audit-roster` on the primary Story;
|
|
226
|
-
the host MUST walk each listed lens against the combined diff
|
|
227
|
-
- `follow-up-rollup` — friction follow-ups across every Story in the
|
|
228
|
-
run (files issues when auto-file is on; posts `follow-ups`)
|
|
229
|
-
- `sibling-coherence` — Spec/Acceptance coherence check across sibling
|
|
230
|
-
bodies (`plan-run-sibling-coherence`)
|
|
231
|
-
|
|
232
|
-
A single-Story run skips the epilogue — follow-ups are captured on
|
|
233
|
-
merge confirm instead (`captureStoryFollowUps`).
|
|
95
|
+
- **0** — dispatch each `ready` id (already capped and overlap-free). Empty
|
|
96
|
+
`ready` with work in flight means "waiting"; keep looping.
|
|
97
|
+
`epilogueDue: true` means every Story is done — go to step 4.
|
|
98
|
+
- **2** — `cycleError`: the graph is self-referential. Fix the `depends_on`
|
|
99
|
+
declarations; do not retry.
|
|
100
|
+
- **3** — `wedged`: nothing dispatchable, nothing in flight, undone Stories
|
|
101
|
+
waiting on blockers that are not done. The envelope names the stuck ids and
|
|
102
|
+
unmet blockers. Land the blocker or include it in `--ids`; do not retry
|
|
103
|
+
unchanged.
|
|
104
|
+
- **4** — `blocked`: a Story carries `agent::blocked`, named in `blocked[]`
|
|
105
|
+
with `blockedReason` — the protocol's HITL pause
|
|
106
|
+
([`instructions.md` § 1.J](../instructions.md)). **Stop the loop and
|
|
107
|
+
surface it; do not poll.** Read the friction comment
|
|
108
|
+
(`gh issue view <id> --comments`) and resume only once the operator
|
|
109
|
+
unblocks it (`update-ticket-state.js --ticket <id> --state agent::ready`).
|
|
110
|
+
A blocked Story outranks a wedge but not a cycle (fix the graph first).
|
|
111
|
+
|
|
112
|
+
4. **Per-run epilogue (N>1).** Once step 3 reports `epilogueDue: true`, run
|
|
113
|
+
`node .agents/scripts/plan-run-epilogue.js --stories 101,102` — audit
|
|
114
|
+
roster, follow-up roll-up, sibling coherence. A single-Story run skips it.
|
|
115
|
+
Detail:
|
|
116
|
+
[`helpers/deliver-reference.md` § Per-run epilogue](helpers/deliver-reference.md).
|
|
234
117
|
|
|
235
118
|
## Branch model (authoritative)
|
|
236
119
|
|
|
237
|
-
Every Story:
|
|
238
|
-
|
|
239
120
|
```text
|
|
240
121
|
story-<id> → PR → main (squash + required checks)
|
|
241
122
|
```
|
|
242
123
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
Ceremony depth is selected by `delivery.routing.ceremonyProfile`
|
|
250
|
-
(`minimal` | `standard` | `strict`, default `standard`) and the **change
|
|
251
|
-
level derived from the Story's own diff** — the changed files' intersection
|
|
252
|
-
with the sensitive-path classes in `audit-rules.json`
|
|
253
|
-
(`review-depth.js#deriveChangeLevel`), not a planner-authored verdict
|
|
254
|
-
(Story #4542):
|
|
255
|
-
|
|
256
|
-
| Profile | Acceptance critic | When to use |
|
|
257
|
-
| --- | --- | --- |
|
|
258
|
-
| `minimal` | Always inline | Tiny trusted N=1 Stories |
|
|
259
|
-
| `standard` | Derived-level routed (+ sampling floor) | Default |
|
|
260
|
-
| `strict` | Always fresh-context | High-assurance / regulated surfaces |
|
|
261
|
-
|
|
262
|
-
| Scope | What runs | Mechanism |
|
|
263
|
-
| --- | --- | --- |
|
|
264
|
-
| **Per-Story (always)** | Gates, branch discipline, close-and-land | `deliver-story` / `single-story-close` |
|
|
265
|
-
| **Per-Story (profile + derived level)** | Acceptance critic mode; review depth | `ceremony-routing.js` + `review-depth.js` + `code-review.js` |
|
|
266
|
-
| **Per-run (N>1)** | Audit roster · follow-up roll-up · sibling coherence | `plan-run-epilogue.js` once at run end |
|
|
267
|
-
| **Per-Story land tail** | Follow-up capture · status resync · ref cleanup · base fast-forward | `single-story-close/phases/post-land.js` (in-process, per-step reported) |
|
|
124
|
+
No `epic/<id>` integration branch and no `--no-ff` wave merge. Dependent
|
|
125
|
+
Stories land sequentially so each builds on the previous merge to `main`.
|
|
126
|
+
Ceremony depth (profiles + derived level via `ceremony-routing.js`,
|
|
127
|
+
review depth reading the same level) and the mechanism table:
|
|
128
|
+
[`helpers/deliver-reference.md` § Ceremony](helpers/deliver-reference.md).
|
|
268
129
|
|
|
269
130
|
## Reading a Story's outcome
|
|
270
131
|
|
|
271
132
|
Each Story's delivery ends in exactly one schema-validated terminal envelope
|
|
272
133
|
([`story-deliver-terminal.schema.json`](../schemas/story-deliver-terminal.schema.json),
|
|
273
|
-
Story #4543) — `landed` | `pending` | `blocked` | `failed
|
|
274
|
-
|
|
134
|
+
Story #4543) — `landed` | `pending` | `blocked` | `failed`, the SSOT for the
|
|
135
|
+
shape; this workflow does not restate its fields.
|
|
275
136
|
|
|
276
137
|
`pending` is **not** a failure: the bounded merge wait expired with the PR
|
|
277
|
-
healthy
|
|
278
|
-
|
|
279
|
-
|
|
138
|
+
healthy (or a human owns the merge), nothing was mutated, and the
|
|
139
|
+
`nextCommand` resumes it — run that rather than re-dispatching. The slow-CI
|
|
140
|
+
`async` mode (Story #4698) returns `pending` by design — launch its
|
|
141
|
+
`nextCommand` as a background invocation (reference appendix).
|
|
280
142
|
|
|
281
143
|
For a Story in an unclear state — including the merged-but-label-stale one a
|
|
282
|
-
`/deliver` re-run refuses outright — probe it read-only
|
|
283
|
-
|
|
284
|
-
```bash
|
|
285
|
-
node .agents/scripts/deliver-recover.js --story <storyId>
|
|
286
|
-
```
|
|
144
|
+
`/deliver` re-run refuses outright — probe it read-only with
|
|
145
|
+
`node .agents/scripts/deliver-recover.js --story <storyId>`.
|
|
287
146
|
|
|
288
147
|
## Constraints
|
|
289
148
|
|
|
290
149
|
- **Land or block — never a silent local build.** Worktrees, `story-<id>`
|
|
291
|
-
branches, close-validation, and PR-to-`main` are the only sanctioned
|
|
292
|
-
|
|
293
|
-
(`delivery.routing.closeAndLand: true`); use `--no-wait-merge` only when
|
|
294
|
-
|
|
295
|
-
- `/deliver` never plans — tickets come from [`/plan`](plan.md).
|
|
296
|
-
|
|
297
|
-
script invocation per Story.
|
|
150
|
+
branches, close-validation, and PR-to-`main` are the only sanctioned delivery
|
|
151
|
+
mechanism. Attended delivers default to close-and-land
|
|
152
|
+
(`delivery.routing.closeAndLand: true`); use `--no-wait-merge` only when a
|
|
153
|
+
human lands the PR.
|
|
154
|
+
- `/deliver` never plans — tickets come from [`/plan`](plan.md). The router
|
|
155
|
+
performs no git/label mutations; `deliver-story` owns every script.
|
|
298
156
|
|
|
299
157
|
## See also
|
|
300
158
|
|
|
301
159
|
- [`/plan`](plan.md) — unified planning entry point.
|
|
302
160
|
- [`helpers/deliver-story.md`](helpers/deliver-story.md) — the one Story
|
|
303
161
|
delivery engine.
|
|
304
|
-
-
|
|
305
|
-
|
|
162
|
+
- [`helpers/deliver-reference.md`](helpers/deliver-reference.md) — sequencing,
|
|
163
|
+
dispatch, ceremony, and epilogue detail.
|
|
@@ -30,10 +30,19 @@ mid-delivery, and evaluates the actual work product.
|
|
|
30
30
|
|
|
31
31
|
## Per round
|
|
32
32
|
|
|
33
|
-
1. **Eval pass
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
1. **Eval pass — one verdict-owner per cluster (Story #4723).** Exactly
|
|
34
|
+
**one** pass authors each cluster's verdict: the **fresh-context critic**
|
|
35
|
+
when the ceremony routing below resolves `fresh` (a sub-agent via the
|
|
36
|
+
`Agent` tool, *not* a continuation of your implementing turn — the
|
|
37
|
+
evaluator does not grade its own homework), or the **inline self-eval**
|
|
38
|
+
when it resolves `inline`. The resolved decision names the owner
|
|
39
|
+
explicitly (`verdictOwner: 'fresh-critic' | 'inline-self-eval'` from
|
|
40
|
+
`resolveCeremonyForRisk`). **Never run both**, and never run a
|
|
41
|
+
preliminary self-assessment pass before dispatching the fresh critic —
|
|
42
|
+
the redundant pre-pass buys no measurable quality and roughly triples
|
|
43
|
+
the acceptance-block cost. Step 2's gate is the deterministic **scorer**
|
|
44
|
+
of the one authored verdict, not a second (or third) pass over the
|
|
45
|
+
criteria.
|
|
37
46
|
|
|
38
47
|
> **Sub-agent type + derived-level ceremony (Epic #4478, M7-B).** When
|
|
39
48
|
> `delivery.routing.roleScopedAgents` is enabled (the **default**), dispatch
|
|
@@ -129,7 +138,9 @@ mid-delivery, and evaluates the actual work product.
|
|
|
129
138
|
one `{ index, criterion, verdict: met|partial|unmet, evidence,
|
|
130
139
|
verifyEvidence[] }` record per acceptance item.
|
|
131
140
|
2. **Decide.** Run the gate against the verdict (the caller's Step 1a names the
|
|
132
|
-
exact invocation — omit `--epic`)
|
|
141
|
+
exact invocation — omit `--epic`). The gate **scores the single verdict
|
|
142
|
+
the round's owner authored** — schema validation, round cap, decision —
|
|
143
|
+
and never re-scores the criteria itself (Story #4723):
|
|
133
144
|
|
|
134
145
|
```bash
|
|
135
146
|
node <main-repo>/.agents/scripts/acceptance-eval.js \
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
---
|
|
2
|
+
description:
|
|
3
|
+
On-demand reference appendix for /deliver — the sequencing edge cases,
|
|
4
|
+
role-scoped dispatch mechanics, lite-route inline execution, checklist
|
|
5
|
+
threading, and the per-run epilogue. Read it when the matching lever is in
|
|
6
|
+
play; the lean spine in deliver.md links here.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# /deliver — reference appendix (on-demand)
|
|
10
|
+
|
|
11
|
+
Reference-only detail split out of [`deliver.md`](../deliver.md) so the
|
|
12
|
+
always-resident spine stays lean (Story #4708). Nothing here is a new MUST —
|
|
13
|
+
it is the mechanics an operator consults when the matching lever is engaged.
|
|
14
|
+
|
|
15
|
+
## Sequencing edge cases (`stories-wave-tick.js`)
|
|
16
|
+
|
|
17
|
+
Each beat re-probes live state: it re-resolves the graph, classifies **done**
|
|
18
|
+
(`agent::done` or a closed issue — including foreign blockers that landed in
|
|
19
|
+
another run), and derives **in-flight** from live `agent::executing` /
|
|
20
|
+
`agent::closing` labels. You never compute `done` or `in-flight` — that
|
|
21
|
+
accounting is read from reality every beat (Story #4594).
|
|
22
|
+
|
|
23
|
+
**`--dispatched` is the one thing you must tell it (Story #4601).** List every
|
|
24
|
+
Story id you have spawned this run. Live state cannot instantly report a Story
|
|
25
|
+
you dispatched moments ago: `single-story-init.js` publishes `agent::executing`
|
|
26
|
+
before the worktree install (Story #4620 moved it ahead of the multi-minute
|
|
27
|
+
install, so the window is now short rather than minutes-long), but it is not
|
|
28
|
+
zero — until the label lands the Story still reads `agent::ready` and, without
|
|
29
|
+
`--dispatched`, the next beat would hand it back and a second sub-agent would
|
|
30
|
+
join the first on the same branch and worktree, interleaving commits.
|
|
31
|
+
`--dispatched` closes that residual same-run window. The rule is
|
|
32
|
+
**append-only: add each id as you dispatch it and never remove one.** The flag
|
|
33
|
+
is additive, not authoritative — the probe unions it into the label-derived set
|
|
34
|
+
and then filters it against live state, so an id that has since gone
|
|
35
|
+
`agent::done` is dropped for you. Re-listing an id costs nothing and cannot
|
|
36
|
+
double-count a slot; *omitting* one is the only way to get this wrong. This is
|
|
37
|
+
why `--dispatched` is not the `--done` bookkeeping #4594 retired, and why
|
|
38
|
+
`--in-flight` remains rejected under `--probe-live`.
|
|
39
|
+
|
|
40
|
+
**Cross-run de-confliction is automatic (Story #4620).** A Story another
|
|
41
|
+
operator is delivering is withheld without any bookkeeping from you: the probe
|
|
42
|
+
reads the Story's assignee lease and, when it belongs to a different operator,
|
|
43
|
+
withholds the Story and reports it in the envelope's
|
|
44
|
+
`foreignHeld: [{ id, holder }]` (with `foreignHeldReason`). That is not a
|
|
45
|
+
failure or a wedge — the holder's run owns the branch, and this run picks the
|
|
46
|
+
Story up automatically once their lease clears. Init is the backstop: it
|
|
47
|
+
refuses a Story already labelled `agent::executing`, or one whose lease a
|
|
48
|
+
different operator holds, unless you pass `--steal`. Assignee-based withholding
|
|
49
|
+
needs `github.operatorHandle` set (in `.agentrc.local.json`); without it the
|
|
50
|
+
probe logs a warning and leans on init's lease refusal alone.
|
|
51
|
+
|
|
52
|
+
## Dispatch mechanics (role-scoped by default)
|
|
53
|
+
|
|
54
|
+
**Lite-shaped Stories execute inline (Story #4722).** Before spawning anything,
|
|
55
|
+
read the Story's `dispatchMode` from the resolver envelope
|
|
56
|
+
(`stories[].dispatchMode`, derived by `resolveStoryDispatchMode` in
|
|
57
|
+
`lib/orchestration/complexity-gate.js` **from the fetched Story body's own
|
|
58
|
+
shape** — `changes[]` count, acceptance count, creates-vs-refactors mix, and
|
|
59
|
+
sensitive-path classes; the `route::lite` label is a human-visible hint only,
|
|
60
|
+
never the control signal, so a lost or never-written label cannot misroute
|
|
61
|
+
delivery): a Story with `dispatchMode: "inline"` executes
|
|
62
|
+
[`deliver-story.md`](deliver-story.md) **inline in this session** — no
|
|
63
|
+
`story-worker` sub-agent boot and no fresh acceptance-critic sub-agents
|
|
64
|
+
(sub-agent boots are the dominant deliver-phase token cost at trivial scope) —
|
|
65
|
+
threading the same `docsDigestPath` / `checklistPath` / change-set discipline
|
|
66
|
+
as a spawned worker. Inline removes model-side fan-out only: every
|
|
67
|
+
`single-story-close.js` gate, the PR to `main`, and the terminal envelope are
|
|
68
|
+
identical. Everything else — a full-shaped body, a missing/unparseable body,
|
|
69
|
+
or a footprint intersecting a sensitive-path class (sensitivity wins and
|
|
70
|
+
keeps the fresh acceptance critic) — takes the standard sub-agent path.
|
|
71
|
+
|
|
72
|
+
**Dispatch each `ready` Story (role-scoped by default).** When
|
|
73
|
+
`delivery.routing.roleScopedAgents` is enabled (the **default**) and the host
|
|
74
|
+
exposes agent dispatch, spawn each ready Story as its own
|
|
75
|
+
`subagent_type: story-worker` sub-agent — it boots on the role-scoped
|
|
76
|
+
[`story-worker`](../../agents/story-worker.md) context (its own system prompt, no
|
|
77
|
+
`CLAUDE.md` @-closure) carrying the load-bearing delivery MUSTs standalone. The
|
|
78
|
+
sub-agent executes [`deliver-story.md`](deliver-story.md) end to end
|
|
79
|
+
(init → implement → acceptance self-eval → close-and-land). Thread into its
|
|
80
|
+
prompt: `storyId`; `docsDigestPath` (the per-run docs digest, null when
|
|
81
|
+
`project.docsContextFiles` is unset); `checklistPath` (the footprint-matched
|
|
82
|
+
write-time audit checklist, produced at dispatch, below); and the
|
|
83
|
+
**change-set discipline** — the worker computes the change set once with
|
|
84
|
+
`computeChangeSet` and hands that one list to every acceptance critic (Story #4593); it never lets a critic re-derive the diff.
|
|
85
|
+
|
|
86
|
+
**Produce `checklistPath` before the spawn (Story #4627).** Compute the payload
|
|
87
|
+
from the Story's predicted footprint (its `changes[]` / `references[]` path
|
|
88
|
+
entries) with `buildDispatchChecklist` and write it to the run temp dir, then
|
|
89
|
+
thread the resulting path (empty when nothing matched):
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
node --input-type=module -e '
|
|
93
|
+
import { buildDispatchChecklist } from "<main-repo>/.agents/scripts/lib/audit-suite/index.js";
|
|
94
|
+
import { parse } from "<main-repo>/.agents/scripts/lib/story-body/story-body.js";
|
|
95
|
+
// storyBody is the fetched Story issue body.
|
|
96
|
+
const { changes, references } = parse(process.env.STORY_BODY);
|
|
97
|
+
const { checklistPath } = buildDispatchChecklist({
|
|
98
|
+
storyId: <storyId>, changes, references, runTempDir: "temp/run-<id>",
|
|
99
|
+
});
|
|
100
|
+
console.log(checklistPath ?? "");
|
|
101
|
+
'
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`buildDispatchChecklist` (`lib/audit-suite/dispatch-checklist.js`) is a pure
|
|
105
|
+
function of the footprint and the on-disk checklists; an empty match prints
|
|
106
|
+
nothing and the worker runs with no write-time checklist — the maker-blind
|
|
107
|
+
close-scope pass still covers it.
|
|
108
|
+
|
|
109
|
+
**Inline fallback (`roleScopedAgents: false` / no-nesting harness).** When the
|
|
110
|
+
kill-switch is off, or the host cannot spawn a sub-agent at this nesting depth,
|
|
111
|
+
do **not** stall: read [`deliver-story.md`](deliver-story.md) **in full** and
|
|
112
|
+
execute it directly, in this turn, threading the same `docsDigestPath` /
|
|
113
|
+
`checklistPath` / change-set discipline. Under `--yes` / injected helper
|
|
114
|
+
content, execute directly without a re-read turn. The engine, gates, and
|
|
115
|
+
terminal envelope are identical either way — only the isolation differs.
|
|
116
|
+
|
|
117
|
+
## Per-run epilogue (N>1)
|
|
118
|
+
|
|
119
|
+
Once the sequence reports `epilogueDue: true` (every Story done), keyed on the
|
|
120
|
+
delivered id set:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
node .agents/scripts/plan-run-epilogue.js --stories 101,102
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
This executes, in order:
|
|
127
|
+
|
|
128
|
+
- `audit-roster` — selects cross-Story audit lenses over the combined landed
|
|
129
|
+
tip and posts `plan-run-audit-roster` on the primary Story; the host MUST
|
|
130
|
+
walk each listed lens against the combined diff.
|
|
131
|
+
- `follow-up-rollup` — friction follow-ups across every Story in the run
|
|
132
|
+
(files issues when auto-file is on; posts `follow-ups`).
|
|
133
|
+
- `sibling-coherence` — Spec/Acceptance coherence check across sibling bodies
|
|
134
|
+
(`plan-run-sibling-coherence`).
|
|
135
|
+
|
|
136
|
+
A single-Story run skips the epilogue — follow-ups are captured on merge
|
|
137
|
+
confirm instead (`captureStoryFollowUps`).
|
|
138
|
+
|
|
139
|
+
## Ceremony (profiles + two scopes)
|
|
140
|
+
|
|
141
|
+
Ceremony depth is selected by `delivery.routing.ceremonyProfile`
|
|
142
|
+
(`minimal` | `standard` | `strict`, default `standard`) and the **change level
|
|
143
|
+
derived from the Story's own diff** — the changed files' intersection with the
|
|
144
|
+
sensitive-path classes in `audit-rules.json`
|
|
145
|
+
(`review-depth.js#deriveChangeLevel`), not a planner-authored verdict
|
|
146
|
+
(Story #4542):
|
|
147
|
+
|
|
148
|
+
| Profile | Acceptance critic | When to use |
|
|
149
|
+
| --- | --- | --- |
|
|
150
|
+
| `minimal` | Always inline | Tiny trusted N=1 Stories |
|
|
151
|
+
| `standard` | Derived-level routed (+ sampling floor) | Default |
|
|
152
|
+
| `strict` | Always fresh-context | High-assurance / regulated surfaces |
|
|
153
|
+
|
|
154
|
+
| Scope | What runs | Mechanism |
|
|
155
|
+
| --- | --- | --- |
|
|
156
|
+
| **Per-Story (always)** | Gates, branch discipline, close-and-land | `deliver-story` / `single-story-close` |
|
|
157
|
+
| **Per-Story (profile + derived level)** | Acceptance critic mode; review depth | `ceremony-routing.js` + `review-depth.js` + `code-review.js` |
|
|
158
|
+
| **Per-run (N>1)** | Audit roster · follow-up roll-up · sibling coherence | `plan-run-epilogue.js` once at run end |
|
|
159
|
+
| **Per-Story land tail** | Follow-up capture · status resync · ref cleanup · base fast-forward | `single-story-close/phases/post-land.js` (in-process, per-step reported) |
|
|
160
|
+
|
|
161
|
+
## Async merge-confirm mode (`delivery.mergeWatch.mode: "async"`, Story #4698)
|
|
162
|
+
|
|
163
|
+
A slow-CI consumer can opt the close into `"async"` mode so the merge wait
|
|
164
|
+
probes once for ~60s (catching an instant merge or an instantly-red required
|
|
165
|
+
check) and then returns `pending` instead of burning ~5 minutes of the host
|
|
166
|
+
tool slot polling a merge that lands after the wait would have expired anyway.
|
|
167
|
+
When a worker returns that `pending` envelope, launch its `nextCommand` as a
|
|
168
|
+
**background** invocation (host background Bash — its completion re-invokes the
|
|
169
|
+
agent) and move on to the next Story; `single-story-confirm-merge.js` is
|
|
170
|
+
idempotent and owns the whole tail. Do not foreground-poll the merge. The
|
|
171
|
+
default `"sync"` behaviour is unchanged.
|