mandrel 2.24.0 → 2.26.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.
Files changed (189) hide show
  1. package/.agents/agents/acceptance-critic.md +19 -21
  2. package/.agents/agents/auditor.md +16 -19
  3. package/.agents/agents/story-worker.md +41 -54
  4. package/.agents/audit-checklists/architecture.md +5 -1
  5. package/.agents/audit-checklists/baselines.md +21 -0
  6. package/.agents/audit-checklists/quality.md +3 -0
  7. package/.agents/docs/agentrc-reference.json +2 -1
  8. package/.agents/docs/configuration.md +2 -1
  9. package/.agents/docs/quality-gates.md +80 -18
  10. package/.agents/docs/workflows.md +4 -2
  11. package/.agents/instructions.md +1 -1
  12. package/.agents/rules/ci-remediation.md +68 -3
  13. package/.agents/schemas/agentrc.schema.json +6 -1
  14. package/.agents/schemas/audit-rules.json +15 -0
  15. package/.agents/schemas/baselines/audit-baselines-envelope.schema.json +242 -0
  16. package/.agents/schemas/baselines/baseline-envelope.schema.json +8 -0
  17. package/.agents/schemas/baselines/crap.schema.json +13 -1
  18. package/.agents/schemas/crap-report.schema.json +37 -0
  19. package/.agents/schemas/model-attribution.schema.json +4 -0
  20. package/.agents/scripts/acceptance-eval.js +124 -15
  21. package/.agents/scripts/audit-baselines.js +136 -0
  22. package/.agents/scripts/audit-labels-bootstrap.js +16 -95
  23. package/.agents/scripts/audit-to-stories.js +33 -3
  24. package/.agents/scripts/check-arch-cycles.js +12 -93
  25. package/.agents/scripts/check-baseline-drift.js +16 -3
  26. package/.agents/scripts/check-baselines.js +19 -3
  27. package/.agents/scripts/check-context-budget.js +95 -14
  28. package/.agents/scripts/check-cyclomatic.js +214 -0
  29. package/.agents/scripts/check-schema-references.js +392 -0
  30. package/.agents/scripts/check-test-temp-hygiene.js +121 -1
  31. package/.agents/scripts/check-workflow-timeouts.js +291 -0
  32. package/.agents/scripts/cleanup-repo-test-temp.js +7 -5
  33. package/.agents/scripts/deliver-light.js +3 -4
  34. package/.agents/scripts/deliver-recover.js +13 -0
  35. package/.agents/scripts/diagnose-friction.js +85 -19
  36. package/.agents/scripts/lib/audit-baselines/engine.js +177 -0
  37. package/.agents/scripts/lib/audit-baselines/gate-surface.js +63 -0
  38. package/.agents/scripts/lib/audit-baselines/headroom.js +72 -0
  39. package/.agents/scripts/lib/audit-baselines/hotspots.js +69 -0
  40. package/.agents/scripts/lib/audit-baselines/kinds.js +313 -0
  41. package/.agents/scripts/lib/audit-baselines/outliers.js +100 -0
  42. package/.agents/scripts/lib/audit-baselines/read.js +87 -0
  43. package/.agents/scripts/lib/audit-baselines/staleness.js +123 -0
  44. package/.agents/scripts/lib/audit-baselines/surface-entry.js +106 -0
  45. package/.agents/scripts/lib/audit-baselines/trend.js +125 -0
  46. package/.agents/scripts/lib/audit-baselines/weights.js +193 -0
  47. package/.agents/scripts/lib/audit-suite/index.js +0 -5
  48. package/.agents/scripts/lib/audit-suite/selector.js +9 -62
  49. package/.agents/scripts/lib/audit-to-stories/audit-label-taxonomy.js +177 -0
  50. package/.agents/scripts/lib/audit-to-stories/audit-lenses.js +1 -0
  51. package/.agents/scripts/lib/audit-to-stories/build-story-body.js +35 -3
  52. package/.agents/scripts/lib/audit-to-stories/group-findings.js +20 -11
  53. package/.agents/scripts/lib/audit-to-stories/parse-audit-md.js +23 -9
  54. package/.agents/scripts/lib/audit-to-stories/seed-from-findings.js +9 -2
  55. package/.agents/scripts/lib/baseline-loader.js +0 -0
  56. package/.agents/scripts/lib/baseline-schema-registry.js +13 -1
  57. package/.agents/scripts/lib/baselines/diff-scope-cli.js +22 -160
  58. package/.agents/scripts/lib/baselines/duplication-scanner.js +27 -0
  59. package/.agents/scripts/lib/baselines/git-base.js +0 -0
  60. package/.agents/scripts/lib/baselines/kinds/crap.js +499 -57
  61. package/.agents/scripts/lib/baselines/preview-gates.js +58 -15
  62. package/.agents/scripts/lib/baselines/reader.js +10 -0
  63. package/.agents/scripts/lib/baselines/refresh-service.js +69 -11
  64. package/.agents/scripts/lib/baselines/scope.js +39 -90
  65. package/.agents/scripts/lib/baselines/writer.js +16 -7
  66. package/.agents/scripts/lib/changed-files.js +8 -1
  67. package/.agents/scripts/lib/cli-args.js +115 -1
  68. package/.agents/scripts/lib/close-validation/runner.js +70 -25
  69. package/.agents/scripts/lib/config-settings-schema-delivery.js +5 -0
  70. package/.agents/scripts/lib/crap-engine.js +124 -27
  71. package/.agents/scripts/lib/crap-method-identity.js +153 -0
  72. package/.agents/scripts/lib/crap-utils.js +86 -13
  73. package/.agents/scripts/lib/cyclomatic-ceiling.js +265 -0
  74. package/.agents/scripts/lib/env-loader.js +46 -16
  75. package/.agents/scripts/lib/feedback-loop/audit-results-graduator.js +0 -2
  76. package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +0 -2
  77. package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +0 -2
  78. package/.agents/scripts/lib/findings/route-finding.js +123 -17
  79. package/.agents/scripts/lib/findings/severity.js +80 -2
  80. package/.agents/scripts/lib/git/cached-fetch.js +0 -0
  81. package/.agents/scripts/lib/git-utils.js +136 -80
  82. package/.agents/scripts/lib/import-graph.js +156 -0
  83. package/.agents/scripts/lib/label-constants.js +17 -0
  84. package/.agents/scripts/lib/observability/runtime-friction.js +17 -2
  85. package/.agents/scripts/lib/observability/source-classifier.js +175 -2
  86. package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +110 -17
  87. package/.agents/scripts/lib/orchestration/ceremony-routing.js +17 -12
  88. package/.agents/scripts/lib/orchestration/check-baselines/phases/compare.js +36 -6
  89. package/.agents/scripts/lib/orchestration/check-baselines/phases/evaluate.js +5 -0
  90. package/.agents/scripts/lib/orchestration/check-baselines/phases/floors.js +12 -1
  91. package/.agents/scripts/lib/orchestration/check-baselines/phases/report.js +8 -1
  92. package/.agents/scripts/lib/orchestration/ci-rerun-guard.js +548 -0
  93. package/.agents/scripts/lib/orchestration/deliver-recover.js +2 -2
  94. package/.agents/scripts/lib/orchestration/git-cleanup/phases/phase-drivers.js +10 -5
  95. package/.agents/scripts/lib/orchestration/git-cleanup/phases/render.js +39 -3
  96. package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +71 -20
  97. package/.agents/scripts/lib/orchestration/light-backstop.js +20 -3
  98. package/.agents/scripts/lib/orchestration/light-escalation.js +82 -3
  99. package/.agents/scripts/lib/orchestration/light-suitability.js +103 -5
  100. package/.agents/scripts/lib/orchestration/plan-context.js +119 -66
  101. package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +31 -5
  102. package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +216 -109
  103. package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +61 -13
  104. package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +79 -22
  105. package/.agents/scripts/lib/orchestration/plan-text-hygiene.js +51 -20
  106. package/.agents/scripts/lib/orchestration/planning/authoring-context.js +70 -74
  107. package/.agents/scripts/lib/orchestration/planning/memory-pool-advisory.js +231 -0
  108. package/.agents/scripts/lib/orchestration/resolve-stories.js +18 -17
  109. package/.agents/scripts/lib/orchestration/retro-proposals.js +0 -0
  110. package/.agents/scripts/lib/orchestration/run-epilogue.js +12 -0
  111. package/.agents/scripts/lib/orchestration/run-scoped-config.js +276 -0
  112. package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +41 -10
  113. package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +125 -37
  114. package/.agents/scripts/lib/orchestration/single-story-close/phases/normalize-pr-title.js +6 -6
  115. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +42 -38
  116. package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +129 -21
  117. package/.agents/scripts/lib/orchestration/single-story-close/phases/push.js +6 -1
  118. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +267 -125
  119. package/.agents/scripts/lib/orchestration/spec-budget.js +16 -5
  120. package/.agents/scripts/lib/orchestration/story-follow-ups.js +182 -95
  121. package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +22 -0
  122. package/.agents/scripts/lib/orchestration/ticket-validator.js +5 -11
  123. package/.agents/scripts/lib/orchestration/ticketing/reads.js +4 -4
  124. package/.agents/scripts/lib/reserved-test-ids.js +77 -0
  125. package/.agents/scripts/lib/story-adjacency.js +3 -3
  126. package/.agents/scripts/lib/story-plan.js +137 -42
  127. package/.agents/scripts/lib/templates/decomposer-prompts.js +4 -4
  128. package/.agents/scripts/lib/test-runner-contract.js +134 -0
  129. package/.agents/scripts/lib/test-tiers.js +11 -2
  130. package/.agents/scripts/lib/ticket-body-sections.js +1 -1
  131. package/.agents/scripts/lib/util/concurrent-map.js +17 -0
  132. package/.agents/scripts/lib/util/parse-id-list.js +103 -0
  133. package/.agents/scripts/lib/util/poll-loop.js +18 -2
  134. package/.agents/scripts/lib/wave-runner/live-probe.js +29 -13
  135. package/.agents/scripts/lib/wave-runner/ready-set.js +254 -43
  136. package/.agents/scripts/lib/workers/combined-mi-crap-worker.js +10 -13
  137. package/.agents/scripts/lib/workers/crap-worker.js +8 -13
  138. package/.agents/scripts/lib/workers/maintainability-report-worker.js +4 -10
  139. package/.agents/scripts/lib/workers/maintainability-worker.js +4 -10
  140. package/.agents/scripts/lib/workers/serve-worker-messages.js +35 -0
  141. package/.agents/scripts/lib/worktree/git-hooks.js +206 -0
  142. package/.agents/scripts/lib/worktree/lifecycle/creation.js +6 -0
  143. package/.agents/scripts/lib/worktree-manager.js +14 -0
  144. package/.agents/scripts/plan-run-epilogue.js +17 -5
  145. package/.agents/scripts/pr-watch-with-update.js +568 -213
  146. package/.agents/scripts/providers/github/tickets.js +33 -10
  147. package/.agents/scripts/provision-git-hooks.js +85 -0
  148. package/.agents/scripts/quality-preview.js +137 -28
  149. package/.agents/scripts/resolve-stories.js +4 -1
  150. package/.agents/scripts/run-coverage.js +86 -35
  151. package/.agents/scripts/run-lint.js +20 -0
  152. package/.agents/scripts/run-tests.js +36 -36
  153. package/.agents/scripts/single-story-close.js +34 -2
  154. package/.agents/scripts/single-story-confirm-merge.js +22 -6
  155. package/.agents/scripts/single-story-init.js +7 -0
  156. package/.agents/scripts/stories-wave-tick.js +308 -47
  157. package/.agents/scripts/story-plan.js +65 -9
  158. package/.agents/scripts/update-coverage-baseline.js +34 -4
  159. package/.agents/scripts/update-crap-baseline.js +42 -4
  160. package/.agents/scripts/update-duplication-baseline.js +209 -83
  161. package/.agents/scripts/update-maintainability-baseline.js +31 -4
  162. package/.agents/scripts/validate-docs-freshness.js +1 -0
  163. package/.agents/skills/core/diagnose-friction/SKILL.md +4 -1
  164. package/.agents/skills/core/gates-and-baselines/SKILL.md +17 -11
  165. package/.agents/skills/skills.index.json +2 -2
  166. package/.agents/templates/single-story-body.md +16 -8
  167. package/.agents/workflows/audit-architecture.md +69 -13
  168. package/.agents/workflows/audit-baselines.md +289 -0
  169. package/.agents/workflows/audit-navigability.md +5 -4
  170. package/.agents/workflows/audit-quality.md +26 -0
  171. package/.agents/workflows/audit-to-stories.md +30 -4
  172. package/.agents/workflows/deliver.md +92 -97
  173. package/.agents/workflows/helpers/acceptance-self-eval.md +47 -10
  174. package/.agents/workflows/helpers/audit-lens-core.md +25 -4
  175. package/.agents/workflows/helpers/code-quality-guardrails.md +9 -2
  176. package/.agents/workflows/helpers/deliver-digest.md +41 -21
  177. package/.agents/workflows/helpers/deliver-reference.md +95 -4
  178. package/.agents/workflows/helpers/deliver-story-reference.md +174 -52
  179. package/.agents/workflows/helpers/deliver-story.md +104 -119
  180. package/.agents/workflows/helpers/plan-reference.md +49 -5
  181. package/.agents/workflows/memory-consolidate.md +116 -0
  182. package/.agents/workflows/plan.md +62 -75
  183. package/README.md +13 -6
  184. package/docs/CHANGELOG.md +93 -0
  185. package/lib/cli/update.js +14 -11
  186. package/lib/cli/version-check.js +9 -1
  187. package/package.json +9 -4
  188. package/.agents/schemas/friction-event.schema.json +0 -56
  189. package/.agents/scripts/lib/feedback-loop/memory-freshness.js +0 -707
@@ -8,73 +8,67 @@ description:
8
8
  # /deliver
9
9
 
10
10
  > **Lean spine.** Happy path + gate list. Sequencing, dispatch mechanics,
11
- > intent phrases, ceremony, and the epilogue live in the on-demand
12
- > [`helpers/deliver-reference.md`](helpers/deliver-reference.md); the unplanned
13
- > path in [`helpers/deliver-light.md`](helpers/deliver-light.md). What every
14
- > delivery always needs is one read:
15
- > [`helpers/deliver-digest.md`](helpers/deliver-digest.md).
11
+ > intent phrases, ceremony and the epilogue live in on-demand
12
+ > [`helpers/deliver-reference.md`](helpers/deliver-reference.md) ("reference"
13
+ > below); the unplanned path in
14
+ > [`helpers/deliver-light.md`](helpers/deliver-light.md). Every delivery reads
15
+ > [`helpers/deliver-digest.md`](helpers/deliver-digest.md) once.
16
16
 
17
17
  ## Role
18
18
 
19
- One delivery door. `/deliver` owns input resolution and sequencing only — every
20
- Story lands through [`helpers/deliver-story.md`](helpers/deliver-story.md).
19
+ One delivery door. `/deliver` owns input resolution, sequencing and the
20
+ close-and-land tail; Stories are implemented via
21
+ [`helpers/deliver-story.md`](helpers/deliver-story.md).
21
22
 
22
- Nothing about the route is declared at the invocation; it is **derived, then
23
- announced, then acted on**. The dependency graph is **discovered, not
24
- declared** `resolve-stories.js` reads it from live state (body edges ∪ native
25
- `blocked_by` edges, each blocker resolved against its real issue state), so
26
- there is no graph to hand it and no batch label, which is what lets you deliver
27
- Stories **across plan runs and over time**.
28
- `plan-run::<id>` is filter metadata, never a resolution input; `route::lite` is
29
- a body-derived hint only. Ahead of all of it, a **single-Story run runs the
30
- engine inline** whatever the shape — sub-agent isolation only earns its cost
31
- against a concurrent sibling.
23
+ The dependency graph is **discovered, not declared** `resolve-stories.js`
24
+ reads it from live state, so you can deliver Stories **across plan runs and
25
+ over time**. `plan-run::<id>` is filter metadata, never a resolution input;
26
+ `route::lite` a body-derived hint only.
32
27
 
33
28
  ## Inputs
34
29
 
35
- Classify what the operator typed **before** doing anything else, and say which
36
- shape you read it as:
30
+ Classify what the operator typed **before** anything else, and say which shape
31
+ you read:
37
32
 
38
33
  | Invocation | Shape | Behavior |
39
34
  | --- | --- | --- |
40
35
  | `/deliver` | bare | List the open `agent::ready` Stories and ask which to deliver. Deliver nothing until answered. |
41
36
  | `/deliver 4712` | ids | One Story via `helpers/deliver-story.md`, **inline in this session** — no `story-worker` spawn. |
42
- | `/deliver 4712 4713 …` | ids | Resolve the set, then sequence by the discovered graph via `stories-wave-tick.js`, dispatching role-scoped sub-agents. |
37
+ | `/deliver 4712 4713 …` | ids | Resolve the set, sequence by the discovered graph via `stories-wave-tick.js`, dispatch sub-agents. |
38
+ | `/deliver 4712 - 4716` | ids | A **range** — every id in the inclusive span. |
43
39
  | `/deliver add a --json flag to doctor` | prompt | Unplanned work: gate, author a receipt Story, land it — [`helpers/deliver-light.md`](helpers/deliver-light.md). |
44
40
 
45
- **The discriminator is lexical and total.** Every positional argument matching
46
- `^#?\d+$` means ids; anything else means a prompt. A **mixed** invocation (ids
47
- *and* prose) is a **hard error** refuse it and ask which was meant, the way
48
- resolution refuses a whole set rather than under-delivering. A named ticket
49
- that is not `type::story`, or carries an `Epic: #N` footer, is a hard error too.
41
+ **The discriminator is lexical and total.** An argument matching `^#?\d+$` is an
42
+ id, and `^#?\d+\s*[-–—]\s*#?\d+$` an inclusive **range** pass one on as a
43
+ single unspaced token, never hand-expanded (reference § Ranges). Either shape
44
+ means ids; anything else means a prompt. A **mixed** invocation (ids *and*
45
+ prose) is a **hard error** refuse it and ask which was meant. A ticket not
46
+ `type::story`, or carrying an `Epic: #N` footer, is a hard error too.
50
47
 
51
48
  ## Saying what you want
52
49
 
53
- No flags to remember: state intent — *"…but I'll merge it myself"*, *"…take the
54
- lease"*, *"…one at a time"* — and announce what you read before acting.
55
- Phrasings and the flag each fills in:
56
- [`helpers/deliver-reference.md` § Intent phrases](helpers/deliver-reference.md).
50
+ No flags to remember: state intent — *"…but I'll merge it myself"*, *"…one at
51
+ a time"* — and announce what you read. Phrasings and the flags they fill in:
52
+ reference § Intent phrases.
57
53
 
58
- `--yes` is **runner-set, never operator-typed**: cron, `/loop`, and headless
59
- dispatch set it to mean *nobody is at the keyboard*, which is what makes the
60
- unplanned path's over-scope stop fail closed to a terminal envelope instead of
61
- a question. Never offer it to an operator or add it to an attended run.
54
+ `--yes` is **runner-set, never operator-typed**: cron, `/loop` and headless
55
+ dispatch set it to mean *nobody is at the keyboard*, which fails the unplanned
56
+ path's over-scope stop closed to an envelope instead of a question. Never offer
57
+ it to an operator or add it to an attended run.
62
58
 
63
59
  ## Procedure
64
60
 
65
61
  0. **Classify and announce.** Read the invocation per § Inputs and state the
66
- shape you derived. A prompt leaves for
67
- [`helpers/deliver-light.md`](helpers/deliver-light.md); bare asks; ids
68
- continue below.
69
-
70
- 1. **Resolve the set.** One command, for one Story or many:
71
- `node .agents/scripts/resolve-stories.js --ids <id,id,...>`. It validates
72
- the set and shows what will run: read `stories[]`, `dag[]`, and `done[]` to
73
- present the order in step 2. You do **not** thread them into step 3 — the
74
- tick re-resolves the graph itself every beat. Resolution hard-errors
75
- (exit 1) on a named id that is not a Story, carries an `Epic: #N` footer, or
76
- whose native edges cannot be read — a missing gate would co-dispatch against
77
- an unlanded blocker.
62
+ shape. A prompt leaves for
63
+ [`helpers/deliver-light.md`](helpers/deliver-light.md); bare asks; ids go on.
64
+
65
+ 1. **Resolve the set.** One command, one Story or many:
66
+ `node .agents/scripts/resolve-stories.js --ids <id,id,...>`. It validates the
67
+ set and shows what will run: read `stories[]`, `dag[]` and `done[]` to
68
+ present the order in step 2, but do **not** thread them into step 3 — the
69
+ tick re-resolves the graph every beat. It hard-errors (exit 1) on an id that
70
+ is not a Story, carries an `Epic: #N` footer, or whose edges cannot be
71
+ read a missing gate would co-dispatch against an unlanded blocker.
78
72
 
79
73
  2. **Confirm (N>1).** Present the order; wait unless `--yes`.
80
74
 
@@ -87,74 +81,75 @@ a question. Never offer it to an operator or add it to an attended run.
87
81
  ```
88
82
 
89
83
  **Do not add `--concurrency` unless the operator explicitly asked for a
90
- per-run cap** — an explicit value wins over config, so a filled-in literal
91
- silently defeats a `.agentrc.local.json` override.
84
+ per-run cap** — an explicit value wins over config, so a literal defeats a
85
+ `.agentrc.local.json` override.
92
86
 
93
87
  Each beat re-probes live state to derive done / in-flight itself; you never
94
88
  compute them. `--dispatched` is the one thing you must supply — the
95
- append-only list of every id you spawned this run and cross-run
96
- de-confliction via the assignee lease is automatic
97
- ([`helpers/deliver-reference.md`](helpers/deliver-reference.md) §§ Sequencing
98
- edge cases, Dispatch mechanics).
99
-
100
- Branch on the exit code:
101
- - **0** — dispatch each `ready` id (already capped and overlap-free). Empty
102
- `ready` with work in flight means "waiting"; keep looping.
103
- `epilogueDue: true` means every Story is done go to step 4.
104
- - **2** — `cycleError`: the graph is self-referential. Fix the `depends_on`
105
- declarations; do not retry.
106
- - **3** — `wedged`: nothing dispatchable, nothing in flight, undone Stories
107
- waiting on unmet blockers both named in the envelope. Land the blocker
108
- or include it in `--ids`; do not retry unchanged.
109
- - **4** — `blocked`: a Story carries `agent::blocked`, named in `blocked[]`
110
- with `blockedReason` the protocol's HITL pause
111
- ([`instructions.md` § 1.J](../instructions.md)). **Stop the loop and
112
- surface it; do not poll.** Read the friction comment
113
- (`gh issue view <id> --comments`) and resume only once the operator
114
- unblocks it (`update-ticket-state.js --ticket <id> --state agent::ready`).
115
- Blocked outranks a wedge but not a cycle (fix the graph first).
116
-
117
- 4. **Per-run epilogue (N>1).** Once step 3 reports `epilogueDue: true`, run
118
- `node .agents/scripts/plan-run-epilogue.js --stories 101,102` — audit
119
- roster, follow-up roll-up, sibling coherence. A single-Story run skips it
89
+ append-only list of every id you spawned this run. Cross-run de-confliction
90
+ via the assignee lease is automatic (reference §§ Sequencing edge cases,
91
+ Dispatch mechanics). Branch on the exit code:
92
+ - **0** dispatch each `ready` id (already capped and overlap-free); an
93
+ empty `ready` with work in flight means "waiting", so keep looping;
94
+ `epilogueDue: true` means every Story is done — step 4.
95
+ - **2** — `cycleError`: the graph is self-referential; fix `depends_on`, do
96
+ not retry. **3** — `wedged`: nothing dispatchable, nothing in flight, the
97
+ undone Stories and their unmet blockers named; land a blocker or add it to
98
+ `--ids`. **4** — `blocked`: a Story carries `agent::blocked` with
99
+ `blockedReason`, the protocol's HITL pause
100
+ ([`instructions.md` § 1.J](../instructions.md)) **stop the loop and
101
+ surface it; do not poll**, resuming once the operator unblocks it. Blocked
102
+ outranks a wedge, not a cycle.
103
+
104
+ 4. **Close each hand-off** (§ Closing what the workers hand back), then, with
105
+ every Story landed, run the **per-run epilogue (N>1)**:
106
+ `node .agents/scripts/plan-run-epilogue.js --stories 101,102`. N=1 skips it
120
107
  ([reference § Per-run epilogue](helpers/deliver-reference.md)).
121
108
 
122
- ## Branch model (authoritative)
109
+ 5. **Correct what the change invalidated.** If a memory you recalled this
110
+ session is now wrong — a trap this landed, a budget it moved — fix that
111
+ entry now, while both the old belief and the new fact are in context, and
112
+ say so when you report. No memory substrate → skip silently. Sweeping the
113
+ whole pool is [`/memory-consolidate`](memory-consolidate.md), not this step.
114
+
115
+ ## Closing what the workers hand back {#tail}
123
116
 
124
- `story-<id>` PR `main` (squash + required checks), per digest § 2.
125
- Dependent Stories land sequentially so each builds on the previous merge.
126
- Ceremony depth (profiles + derived level via `ceremony-routing.js`, review
127
- depth reading the same level):
128
- [`helpers/deliver-reference.md` § Ceremony](helpers/deliver-reference.md).
117
+ **The tail is the orchestrator's, not the worker's.** A dispatched
118
+ `story-worker` stops at a pushed branch and returns a hand-off; **you** run
119
+ [`helpers/deliver-story.md`](helpers/deliver-story.md) Step 3
120
+ (`single-story-close.js`) for it, foreground, and relay the envelope.
129
121
 
130
- ## Reading a Story's outcome
122
+ **Serialize the tail.** Implementation runs in parallel; closing does not. Close
123
+ one Story at a time — closes contend on the base branch, the merge queue and
124
+ the checkout. A worker handing back mid-close waits its turn.
131
125
 
132
- Each Story ends in exactly one schema-validated terminal envelope `landed` |
133
- `pending` | `blocked` | `failed`. Statuses, exits, and fields:
134
- [`helpers/deliver-digest.md`](helpers/deliver-digest.md) § 5.
126
+ **A worker returning no terminal envelope is the expected shape**, not a failure
127
+ to answer with a re-dispatch: only close mints one. Close the pushed branch, or
128
+ probe read-only with `node .agents/scripts/deliver-recover.js --story <storyId>`
129
+ and resume the worker or close it names.
135
130
 
136
- `pending` is **not** a failure: the bounded wait expired with the PR healthy
137
- (or a human owns the merge), nothing was mutated, and `nextCommand` resumes it
138
- run that rather than re-dispatching.
131
+ **Reading the outcome.** Each close ends the Story in one schema-validated
132
+ envelope `landed` | `pending` | `blocked` | `failed`; statuses, exits and
133
+ fields are digest § 5. `pending` is **not** a failure — `nextCommand` resumes
134
+ it; run that, do not re-dispatch.
139
135
 
140
- For a Story in an unclear state including the merged-but-label-stale one a
141
- re-run refuses outright probe it read-only with
142
- `node .agents/scripts/deliver-recover.js --story <storyId>`.
136
+ **Branch model (authoritative).** `story-<id>` PR `main` (squash +
137
+ required checks), per digest § 2; dependent Stories land sequentially. Ceremony
138
+ depth (profiles + derived level via `ceremony-routing.js`, review depth reading
139
+ it): reference § Ceremony.
143
140
 
144
141
  ## Constraints
145
142
 
146
- - **Land or block — never a silent local build** (digest § 2). Attended
147
- delivers default to close-and-land (`delivery.routing.closeAndLand: true`);
148
- rest at `agent::closing` only when a human owns the merge.
143
+ - **Land or block — never a silent local build** (digest § 2). Attended delivers
144
+ default to close-and-land (`delivery.routing.closeAndLand: true`); rest at
145
+ `agent::closing` only when a human owns it.
149
146
  - **`/deliver` never plans.** Planned tickets come from [`/plan`](plan.md), and
150
147
  an over-scope prompt **escalates and ends** — never invoke `/plan` in this
151
148
  session to rescue it ([`helpers/deliver-light.md`](helpers/deliver-light.md)
152
- § Escalation is terminal). The router performs no git/label mutations;
153
- `deliver-story` owns every script.
149
+ § Escalation is terminal).
154
150
 
155
151
  ## See also
156
152
 
157
- - [`/plan`](plan.md) unified planning entry point.
158
- - [`helpers/deliver-story.md`](helpers/deliver-story.md) the one engine.
159
- - [`helpers/deliver-light.md`](helpers/deliver-light.md) the unplanned
160
- prompt path, shared with `/plan` Gate #1.
153
+ [`/plan`](plan.md), [`helpers/deliver-story.md`](helpers/deliver-story.md) (the
154
+ engine), [`helpers/deliver-light.md`](helpers/deliver-light.md) (the unplanned
155
+ prompt path, shared with `/plan` Gate #1).
@@ -40,8 +40,8 @@ mid-delivery, and evaluates the actual work product.
40
40
  `resolveCeremonyForRisk`). **Never run both**, and never run a
41
41
  preliminary self-assessment pass before dispatching the fresh critic —
42
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
43
+ the acceptance-block cost. Step 3's gate is the deterministic **scorer**
44
+ of the one merged verdict, not a second (or third) pass over the
45
45
  criteria.
46
46
 
47
47
  > **Sub-agent type + derived-level ceremony.** When
@@ -134,20 +134,56 @@ mid-delivery, and evaluates the actual work product.
134
134
  fresh — a false-fresh coverage record without `coverage-final.json`
135
135
  silently weakens the floor. Limit the evidence-share to `lint` and
136
136
  `typecheck`.
137
- - Emits a verdict file under `temp/` conforming to
137
+ - Emits a **cluster** verdict file under `temp/` conforming to
138
138
  [`acceptance-eval-verdict.schema.json`](../../schemas/acceptance-eval-verdict.schema.json):
139
139
  one `{ index, criterion, verdict: met|partial|unmet, evidence,
140
- verifyEvidence[] }` record per acceptance item.
141
- 2. **Decide.** Run the gate against the verdict (the caller's Step 1a names the
142
- exact invocation omit `--epic`). The gate **scores the single verdict
143
- the round's owner authored** — schema validation, round cap, decision —
144
- and never re-scores the criteria itself:
140
+ verifyEvidence[] }` record per acceptance item **in that cluster**, each
141
+ `index` being the item's position in the Story's full `acceptance[]`
142
+ array. A fresh critic **returns that path to you** rather than calling the
143
+ gate itself.
144
+ 2. **Dispatch the round's clusters in parallel, then merge into one verdict.**
145
+ The clusters of a round are independent, so dispatch **all** of the round's
146
+ fresh critics as N `Agent` calls **in a single assistant turn** —
147
+ [`parallel-tooling.md`](parallel-tooling.md) **Rule 3** — never serially,
148
+ and never one round per cluster. Clusters routed `inline` are authored in
149
+ the same round alongside them.
150
+
151
+ Then **merge** the cluster verdicts into **one** verdict file under `temp/`:
152
+ concatenate every cluster's `criteria[]` records and order the merged array
153
+ by `index`, so it holds exactly one record per `acceptance[]` item in
154
+ **acceptance-array order**, under a single top-level `storyId`,
155
+ `schemaVersion`, `round` and `commitSha`. The verdict schema deliberately
156
+ carries **no `clusterId`** — the round's artifact is the merged verdict, and
157
+ which critic scored which record is not part of the contract.
158
+
159
+ > **Why one gate call and not N.** The round counter is **Story-scoped** —
160
+ > derived by counting `acceptance-eval` signals in the Story's
161
+ > `signals.ndjson` — and each cluster verdict has a distinct fingerprint, so
162
+ > the replay guard never collapses them. A gate call per cluster would spend
163
+ > one of the (default 2) rounds *per cluster*, so a Story with more than 8
164
+ > acceptance criteria would exhaust its redraft budget on cluster arithmetic
165
+ > alone; N concurrent calls would also race that same ledger. Cluster-scoped
166
+ > round counting exists in
167
+ > [`acceptance-eval-decision.js`](../../scripts/lib/orchestration/acceptance-eval-decision.js)
168
+ > but requires an integer `epicId`, which v2 pins `null` — it is not a way
169
+ > around the merge.
170
+ 3. **Decide — exactly one gate call per round.** Run the gate against the
171
+ **merged** verdict (the caller's Step 1a names the exact invocation — omit
172
+ `--epic`). The gate **scores the single verdict the round produced** —
173
+ schema validation, round cap, decision — and never re-scores the criteria
174
+ itself:
145
175
 
146
176
  ```bash
147
177
  node <main-repo>/.agents/scripts/acceptance-eval.js \
148
- --story <storyId> --verdict <verdict-path>
178
+ --story <storyId> --verdict <merged-verdict-path> \
179
+ --expected-criteria <number of acceptance[] items>
149
180
  ```
150
181
 
182
+ Pass `--expected-criteria` from the `acceptance[]` count you already read
183
+ off the Story body: a verdict whose `criteria[]` length differs — a single
184
+ cluster's verdict handed over unmerged — is rejected **before scoring**,
185
+ with an error naming the merge contract and consuming **no round**.
186
+
151
187
  The gate validates the verdict against the schema, applies the round cap,
152
188
  emits the per-criterion `acceptance-eval` signal into the retro / feedback
153
189
  substrate, prints a JSON envelope, and exits with one of three decisions:
@@ -161,4 +197,5 @@ mid-delivery, and evaluates the actual work product.
161
197
  (transition to `agent::blocked`) and post a `friction` comment naming the
162
198
  unmet criteria and their evidence. Never silently proceed to close.
163
199
 
164
- Write the verdict files under `temp/` only — they are scratch artifacts.
200
+ Write both the per-cluster verdicts and the merged verdict under `temp/` only —
201
+ they are scratch artifacts.
@@ -76,7 +76,7 @@ the primary file the finding lives in:]
76
76
  ### `path/to/primary-file.ext` — [Short title of the issue]
77
77
 
78
78
  - **Dimension:** [the lens-specific dimension — see the lens's own list]
79
- - **Severity:** [Critical | High | Medium | Low]
79
+ - **Severity:** [Critical | High | Medium | Low | Info]
80
80
  - **Location:** `path/to/primary-file.ext:line`
81
81
  - **Current State:** [the specific file/line/module and why it is problematic]
82
82
  - **Recommendation & Rationale:** [how to remediate and why it matters]
@@ -89,11 +89,18 @@ the primary file the finding lives in:]
89
89
 
90
90
  ## Severity scale {#severity-scale}
91
91
 
92
- Every finding grades its severity (labelled `Severity` or `Impact`) on this
93
- ordered scale. `parse-audit-md.js` recognizes every level, and a surviving
94
- **Critical** finding halts the delivery gate
92
+ Every finding grades its severity (labelled `Severity` or `Impact`) on the
93
+ ordered scale below. **The code owns this vocabulary**: it is defined in
94
+ [`lib/findings/severity.js`](../../scripts/lib/findings/severity.js)
95
+ (`SEVERITIES`), and every level here — including its accepted spellings — comes
96
+ from that module. A surviving **Critical** finding halts the delivery gate
95
97
  (`lib/audit-suite/findings.js#hasSurvivingCritical`).
96
98
 
99
+ Grade on **exactly** these five levels. A level of your own invention does not
100
+ parse: it resolves to no severity, tallies as `unknown`, and the finding is
101
+ dropped by every severity-filtered run — including the most permissive one. A
102
+ dropped finding is indistinguishable from a finding you never wrote.
103
+
97
104
  - **Critical** — an active, exploitable, or data-losing defect that must be
98
105
  fixed before the change can ship (e.g. a leaked secret, an auth bypass, a
99
106
  guaranteed production outage or data-loss path).
@@ -102,6 +109,11 @@ ordered scale. `parse-audit-md.js` recognizes every level, and a surviving
102
109
  - **Medium** — a real problem worth scheduling; contained blast radius, or a
103
110
  reasonable workaround exists.
104
111
  - **Low** — minor or cosmetic; fix opportunistically.
112
+ - **Info** — the canonical floor: a real, grounded observation worth recording
113
+ that asks for no scheduled work (a documented deviation worth noting, a
114
+ measurement that is fine today and worth watching). Accepts `Informational`.
115
+ Use it instead of inventing a below-`Low` word of your own; a finding that
116
+ cannot clear the evidence bar below is **dropped**, not filed as `Info`.
105
117
 
106
118
  ## Self-cross-check (mandatory — filter false positives before you finalize) {#self-cross-check}
107
119
 
@@ -149,6 +161,15 @@ that rests on one of them:
149
161
  - **Style-only nits already enforced by a formatter/linter** — do not
150
162
  re-litigate what the committed tooling already governs.
151
163
 
164
+ > **Boundary with the dead-wiring mandate.** The architecture and quality lenses
165
+ > are required to report shipped seams with no live production caller (their own
166
+ > bodies carry the mandate). The exclusions above **bound** that mandate rather
167
+ > than cancelling it: a test seam, a CLI entry point, a declared `exports`
168
+ > surface, or a dynamically-reached symbol is still never a finding. What the
169
+ > mandate targets is the case none of those cover — an **internal** seam that a
170
+ > delivery shipped and nothing in production ever calls. When a candidate is
171
+ > genuinely one of the exclusions, cite the exclusion and drop it.
172
+
152
173
  ### Final re-open-and-drop pass (mandatory)
153
174
 
154
175
  1. Walk your Detailed Findings once more, applying the bar and the exclusion
@@ -35,8 +35,15 @@ thresholds, sourced from
35
35
  | CC range | Action |
36
36
  | --- | --- |
37
37
  | ≤ 8 | Pass — no annotation required. |
38
- | > 8 (default `cyclomaticFlag`) | **Flag** in review: explain why, or split. The function is allowed to land but the audit report names it. |
39
- | > 12 (default `cyclomaticMustFix`) | **Must-fix**: refactor before the Story commits. `quality:preview` reports it as a violation; the close-validation chain refuses the merge. |
38
+ | > 8 (default `cyclomaticFlag`) | **Flag** `quality:preview` counts the function in its `new-method count over c=<flag>` column. The function is allowed to land but the report names it. |
39
+ | > 12 (default `cyclomaticMustFix`) | **Must-fix**: `check-cyclomatic.js` fails when a file gains a function above the ceiling, or when its worst function gets worse than the recorded baseline. |
40
+
41
+ `check-cyclomatic.js` is a **ratchet**, not a cliff: `baselines/cyclomatic.json`
42
+ records the over-ceiling functions a repository already carries, so adopting
43
+ the gate never demands a mass refactor. Burning a recorded breach down is
44
+ always allowed and re-records itself on the next `--update`; adding one is
45
+ what fails. It runs in the same required-check slot as `check-arch-cycles.js`
46
+ and `check-dead-exports.js`.
40
47
 
41
48
  A common refactor that pulls a 13-CC function under 8 is extracting the early-
42
49
  return guard chain into a named predicate, then collapsing the remaining
@@ -10,10 +10,9 @@ description: >-
10
10
  # Deliver digest (read once per session)
11
11
 
12
12
  > **Bundle, not a procedure.** [`deliver-story.md`](deliver-story.md) is still
13
- > the steps. This file is the material those steps referenced across five
14
- > separate files and a JSON schema bundled so one read covers the whole happy
15
- > path. Situational material (lease preflight, recovery routers, merge-wait
16
- > budgets, CI remediation) stays on demand in
13
+ > the steps; this file is the material they reference, bundled so one read
14
+ > covers the happy path. Situational material (lease preflight, recovery
15
+ > routers, merge-wait budgets, CI remediation) stays on demand in
17
16
  > [`deliver-story-reference.md`](deliver-story-reference.md) and
18
17
  > [`deliver-reference.md`](deliver-reference.md); read those **only** when an
19
18
  > envelope or a failure routes you there.
@@ -28,10 +27,10 @@ rule produces it:
28
27
  whatever its shape — sub-agent isolation is load-bearing only against a
29
28
  *concurrent* sibling racing the same checkout, and a one-Story run has none.
30
29
  2. **Every other run is `subagent`.** A multi-Story run dispatches every Story
31
- as a sub-agent however trivial its shape: a lite body does not conjure a
32
- second session for a sibling to run in, and the wave tick may hand you the
33
- whole set on one beat. Shape still sets ceremony and is reported alongside;
34
- the `route::lite` label is a human-visible hint, never the control signal.
30
+ as a sub-agent however trivial its shape a lite body does not conjure a
31
+ second session for a sibling, and the wave tick may hand you the whole set
32
+ on one beat. Shape still sets ceremony; the `route::lite` label is a
33
+ human-visible hint, never the control signal.
35
34
 
36
35
  `inline` removes model-side fan-out only — no `story-worker` boot, no fresh
37
36
  acceptance-critic spawn. **`subagent` and `inline` run the same engine**: same
@@ -54,21 +53,32 @@ the only sanctioned landing. A silent local build is not a delivery.
54
53
  ## 3. Change set — computed once, handed to everyone
55
54
 
56
55
  One enumeration per Story. A critic that re-runs its own `git diff`
57
- can score a different set than the one that routed it:
56
+ can score a different set than the one that routed it. Both routing calls take
57
+ a single options object and are **total — they never throw**, so a wrong-shaped
58
+ argument is silently absorbed into the `null` fail-safe:
58
59
 
59
60
  ```bash
60
61
  node --input-type=module -e '
61
- import { computeChangeSet } from "<main-repo>/.agents/scripts/lib/orchestration/change-set.js";
62
+ const lib = "<main-repo>/.agents/scripts/lib/orchestration";
63
+ const { computeChangeSet } = await import(`${lib}/change-set.js`);
64
+ const { deriveChangeLevel } = await import(`${lib}/review-depth.js`);
65
+ const { resolveCeremonyForRisk } = await import(`${lib}/ceremony-routing.js`);
62
66
  const { files } = computeChangeSet({ baseRef: "main", headRef: "story-<storyId>" });
63
- console.log(JSON.stringify(files));
67
+ // deriveChangeLevel({ changedFiles, injectedRules?, selectSensitivePathClassesFn? })
68
+ // -> { level, classes } — an OBJECT, never a bare level.
69
+ const { level, classes } = deriveChangeLevel({ changedFiles: files });
70
+ // resolveCeremonyForRisk({ derivedLevel, clusterIndex?, freshCriticSampleRate?,
71
+ // ceremonyProfile? }) -> { mode, reason, sampled, profile, verdictOwner }.
72
+ // derivedLevel is that level STRING. Handing it the object above matches no
73
+ // tier, so it routes to the null fail-safe: a fresh critic, silently.
74
+ const ceremony = resolveCeremonyForRisk({ derivedLevel: level, clusterIndex: 0 });
75
+ console.log(JSON.stringify({ files, level, classes, ...ceremony }));
64
76
  '
65
77
  ```
66
78
 
67
- Derive the level with `deriveChangeLevel`
68
- ([`review-depth.js`](../../scripts/lib/orchestration/review-depth.js)) over
69
- that one list: a sensitive path registered in `audit-rules.json` → `high`, none
70
- → `low`, an unenumerable diff (`files === null`) → `null`. Resolve
71
- fresh-vs-inline critics with `resolveCeremonyForRisk`
79
+ Level rules ([`review-depth.js`](../../scripts/lib/orchestration/review-depth.js)):
80
+ a sensitive path registered in `audit-rules.json` → `high`, none → `low`, an
81
+ unenumerable diff (`files === null`) → `null`. Ceremony rules
72
82
  ([`ceremony-routing.js`](../../scripts/lib/orchestration/ceremony-routing.js)):
73
83
  `minimal` → always inline, `strict` → always fresh, `standard` → `high`/`null`
74
84
  → fresh and `low` → inline unless the `freshCriticSampleRate` floor forces
@@ -78,16 +88,26 @@ fresh. An `inline` dispatch mode overrides all of it to inline critics. Close's
78
88
  ## 4. Acceptance self-eval (Step 1a, required)
79
89
 
80
90
  **One verdict-owner per cluster** — the fresh critic *or* the inline
81
- self-eval, named by `verdictOwner`, never both and never a warm-up pass. It
82
- scores each `acceptance[]` item against the change set above, with `verify[]`
83
- output as evidence. Bounded by `delivery.acceptanceEval.maxRounds` (default 2).
84
- Then score the authored verdict:
91
+ self-eval, named by `verdictOwner`, never both and never a warm-up pass. Each
92
+ scores its cluster's `acceptance[]` items against the change set above, with
93
+ `verify[]` output as evidence. Bounded by `delivery.acceptanceEval.maxRounds`
94
+ (default 2).
95
+
96
+ **One round = N cluster critics → ONE merged verdict → ONE gate call.** Merge
97
+ every cluster's records into a single `criteria[]` in `acceptance[]` order, one
98
+ per acceptance item, and score that once. A gate call per cluster spends a
99
+ round *per cluster* and races the round ledger:
85
100
 
86
101
  ```bash
87
102
  node <main-repo>/.agents/scripts/acceptance-eval.js \
88
- --story <storyId> --verdict <verdict-path>
103
+ --story <storyId> --verdict <merged-verdict-path> \
104
+ --expected-criteria <acceptance[] count>
89
105
  ```
90
106
 
107
+ Pass `--expected-criteria` — **without it the coverage assertion is inert**, so
108
+ an unmerged cluster verdict scores a fraction of the criteria and still reports
109
+ `proceed`. A mismatch is rejected before scoring and costs no round.
110
+
91
111
  `proceed` → close. `redraft` → one more round inside the cap. `block` → **do
92
112
  not close**: post a `friction` comment and flip `agent::blocked`.
93
113
  Per-round mechanics: [`acceptance-self-eval.md`](acceptance-self-eval.md).