mandrel 2.58.0 → 2.60.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 (124) hide show
  1. package/.agents/README.md +17 -12
  2. package/.agents/agents/acceptance-critic.md +24 -43
  3. package/.agents/agents/story-worker.md +18 -19
  4. package/.agents/docs/SDLC.md +12 -13
  5. package/.agents/docs/agentrc-reference.json +1 -2
  6. package/.agents/docs/configuration.md +29 -46
  7. package/.agents/docs/quality-gates.md +9 -5
  8. package/.agents/docs/workflows.md +1 -1
  9. package/.agents/instructions.md +5 -7
  10. package/.agents/rules/ci-remediation.md +41 -8
  11. package/.agents/rules/known-tooling-behavior.md +65 -15
  12. package/.agents/runtime-deps.json +7 -2
  13. package/.agents/schemas/acceptance-eval-verdict.schema.json +1 -1
  14. package/.agents/schemas/agentrc.schema.json +6 -11
  15. package/.agents/schemas/crap-baseline.schema.json +1 -1
  16. package/.agents/schemas/crap-report.schema.json +1 -1
  17. package/.agents/schemas/story-deliver-terminal.schema.json +3 -3
  18. package/.agents/scripts/README.md +11 -1
  19. package/.agents/scripts/acceptance-eval.js +25 -27
  20. package/.agents/scripts/ceremony-derive.js +15 -10
  21. package/.agents/scripts/check-context-budget.js +148 -228
  22. package/.agents/scripts/check-schema-references.js +5 -3
  23. package/.agents/scripts/check-workflow-citations.js +33 -147
  24. package/.agents/scripts/coverage-capture.js +7 -4
  25. package/.agents/scripts/deliver-light.js +41 -100
  26. package/.agents/scripts/deliver-run.js +631 -0
  27. package/.agents/scripts/file-ci-gap.js +59 -11
  28. package/.agents/scripts/install-matrix-assert.js +48 -3
  29. package/.agents/scripts/lib/audit-to-stories/seed-from-findings.js +51 -33
  30. package/.agents/scripts/lib/baselines/crap-preview-incremental.js +6 -2
  31. package/.agents/scripts/lib/baselines/kinds/_crap-read.js +0 -8
  32. package/.agents/scripts/lib/baselines/kinds/crap.js +35 -18
  33. package/.agents/scripts/lib/changed-files.js +30 -0
  34. package/.agents/scripts/lib/config/delivery-routing.js +5 -4
  35. package/.agents/scripts/lib/config/explain.js +1 -3
  36. package/.agents/scripts/lib/config/gates/crap-incremental-coverage.schema.js +1 -1
  37. package/.agents/scripts/lib/config-resolver.js +1 -0
  38. package/.agents/scripts/lib/config-settings-schema-delivery.js +28 -21
  39. package/.agents/scripts/lib/coverage-capture-fullscope.js +10 -2
  40. package/.agents/scripts/lib/coverage-capture-incremental.js +3 -2
  41. package/.agents/scripts/lib/coverage-capture-usage.js +4 -1
  42. package/.agents/scripts/lib/crap-engine.js +2 -2
  43. package/.agents/scripts/lib/crap-utils.js +21 -5
  44. package/.agents/scripts/lib/doc-tiers.js +4 -2
  45. package/.agents/scripts/lib/escomplex-ast-compat.js +39 -17
  46. package/.agents/scripts/lib/escomplex-kernel.js +298 -0
  47. package/.agents/scripts/lib/feedback-loop/graduator-core.js +7 -6
  48. package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +7 -5
  49. package/.agents/scripts/lib/generated/agentrc-validator.js +1 -1
  50. package/.agents/scripts/lib/gh-exec.js +160 -0
  51. package/.agents/scripts/lib/maintainability-engine.js +3 -3
  52. package/.agents/scripts/lib/observability/source-classifier.js +1 -0
  53. package/.agents/scripts/lib/orchestration/ceremony-routing.js +74 -132
  54. package/.agents/scripts/lib/orchestration/ci-rerun-guard.js +123 -12
  55. package/.agents/scripts/lib/orchestration/complexity-gate.js +180 -352
  56. package/.agents/scripts/lib/orchestration/light-suitability.js +71 -136
  57. package/.agents/scripts/lib/orchestration/plan-context.js +44 -50
  58. package/.agents/scripts/lib/orchestration/plan-persist/changes-repair.js +8 -6
  59. package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +104 -119
  60. package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +41 -25
  61. package/.agents/scripts/lib/orchestration/plan-persist/summary.js +11 -11
  62. package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +63 -29
  63. package/.agents/scripts/lib/orchestration/plan-persist/wave-collision-gate.js +107 -0
  64. package/.agents/scripts/lib/orchestration/review-depth.js +14 -11
  65. package/.agents/scripts/lib/orchestration/run-epilogue.js +260 -182
  66. package/.agents/scripts/lib/orchestration/run-scoped-config.js +63 -99
  67. package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +3 -3
  68. package/.agents/scripts/lib/orchestration/single-story-close/phases/graphql-preflight.js +137 -0
  69. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +105 -18
  70. package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +4 -3
  71. package/.agents/scripts/lib/orchestration/story-follow-ups.js +156 -39
  72. package/.agents/scripts/lib/orchestration/story-init-envelope.js +71 -0
  73. package/.agents/scripts/lib/orchestration/task-body-validator.js +8 -17
  74. package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +25 -209
  75. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +8 -5
  76. package/.agents/scripts/lib/orchestration/ticket-validator.js +44 -183
  77. package/.agents/scripts/lib/orchestration/ticketing/reads.js +14 -25
  78. package/.agents/scripts/lib/runtime-deps/dep-resolution.js +155 -0
  79. package/.agents/scripts/lib/runtime-deps/ensure-installed.js +44 -9
  80. package/.agents/scripts/lib/runtime-deps/parser-major.js +110 -0
  81. package/.agents/scripts/lib/runtime-deps/preflight.js +6 -25
  82. package/.agents/scripts/lib/runtime-deps/scan-imports.js +46 -1
  83. package/.agents/scripts/lib/skills/walk-skill-files.js +1 -1
  84. package/.agents/scripts/lib/story-body/body-format-lints.js +58 -12
  85. package/.agents/scripts/lib/story-body/story-body.js +83 -29
  86. package/.agents/scripts/lib/templates/decomposer-prompts.js +28 -33
  87. package/.agents/scripts/lib/wave-runner/live-probe.js +31 -5
  88. package/.agents/scripts/merge-baseline.js +4 -5
  89. package/.agents/scripts/plan-context.js +117 -28
  90. package/.agents/scripts/plan-persist.js +79 -39
  91. package/.agents/scripts/plan-run-epilogue.js +11 -8
  92. package/.agents/scripts/pr-watch-with-update.js +9 -2
  93. package/.agents/scripts/run-verify.js +13 -6
  94. package/.agents/scripts/single-story-init.js +7 -57
  95. package/.agents/scripts/stories-wave-tick.js +160 -26
  96. package/.agents/skills/core/gates-and-baselines/reference.md +0 -1
  97. package/.agents/skills/skills.index.json +2 -12
  98. package/.agents/skills/stack/qa/playwright/SKILL.md +26 -0
  99. package/.agents/workflows/audit-to-stories.md +14 -11
  100. package/.agents/workflows/helpers/acceptance-self-eval.md +84 -157
  101. package/.agents/workflows/helpers/code-review.md +4 -2
  102. package/.agents/workflows/helpers/deliver-digest.md +31 -24
  103. package/.agents/workflows/helpers/deliver-light.md +92 -101
  104. package/.agents/workflows/helpers/deliver-reference.md +116 -100
  105. package/.agents/workflows/helpers/deliver-story-reference.md +58 -124
  106. package/.agents/workflows/helpers/deliver-story.md +17 -18
  107. package/.agents/workflows/helpers/plan-reference.md +82 -60
  108. package/.agents/workflows/mandrel-deliver.md +47 -31
  109. package/.agents/workflows/mandrel-plan.md +32 -30
  110. package/.agents/workflows/mandrel-update.md +36 -21
  111. package/README.md +3 -3
  112. package/docs/CHANGELOG.md +43 -0
  113. package/lib/cli/registry.js +45 -25
  114. package/lib/cli/update.js +376 -17
  115. package/lib/migrations/index.js +2 -0
  116. package/lib/migrations/steps/2.60.0-retire-audit-results-autofile.js +40 -0
  117. package/package.json +8 -2
  118. package/.agents/schemas/model-attribution.schema.json +0 -53
  119. package/.agents/scripts/lib/orchestration/model-attribution.js +0 -418
  120. package/.agents/scripts/lib/orchestration/split-policy-validator.js +0 -188
  121. package/.agents/scripts/lib/orchestration/story-plan-state.js +0 -33
  122. package/.agents/scripts/lib/orchestration/structured-comment-parser.js +0 -67
  123. package/.agents/scripts/lib/templates/spec-author-prompts.js +0 -76
  124. package/.agents/skills/core/scope-triage/SKILL.md +0 -48
@@ -33,7 +33,7 @@ you read:
33
33
  | --- | --- | --- |
34
34
  | `/mandrel-deliver` | bare | List the open `agent::ready` Stories and ask which to deliver. Deliver nothing until answered. |
35
35
  | `/mandrel-deliver 4712` | ids | One Story via `helpers/deliver-story.md`, **inline in this session** — no `story-worker` spawn. |
36
- | `/mandrel-deliver 4712 4713 …` | ids | Resolve the set, sequence by the discovered graph via `stories-wave-tick.js`, dispatch sub-agents. |
36
+ | `/mandrel-deliver 4712 4713 …` | ids | Resolve the set, then beat `deliver-run.js` — it sequences by the discovered graph and hands back the spawns and closes. |
37
37
  | `/mandrel-deliver 4712 - 4716` | ids | A **range** — every id in the inclusive span. |
38
38
  | `/mandrel-deliver 4700` (a `type::epic`) | ids | The Epic's **open** child Stories. Mixes with Story ids. |
39
39
  | `/mandrel-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). |
@@ -42,10 +42,10 @@ you read:
42
42
  id, and `^#?\d+\s*[-–—]\s*#?\d+$` an inclusive **range** — pass one on as a
43
43
  single unspaced token, never hand-expanded (reference). Either shape
44
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 that
46
- is neither `type::story` nor `type::epic`, or that carries an `Epic: #N`
47
- footer, is a hard error too — container Epics link parent→child only, so that
48
- footer stays refused.
45
+ prose) is **ambiguous, not fatal** — disambiguate it rather than refusing:
46
+ ask which was meant, or, when the reading is obvious, name the one you
47
+ inferred before acting on it. A ticket that is neither `type::story` nor
48
+ `type::epic` is a **hard error**.
49
49
 
50
50
  ## Saying what you want
51
51
 
@@ -67,41 +67,51 @@ to an attended run.
67
67
  1. **Resolve the set.** One command, one Story or many:
68
68
  `node .agents/scripts/resolve-stories.js --ids <id,id,...>`. It validates the
69
69
  set and shows what will run: read `stories[]`, `dag[]` and `done[]` to
70
- present the order in step 2, but do **not** thread them into step 3 — the
71
- tick re-resolves the graph every beat. An Epic id expands to its open child
70
+ present the order in step 2, but do **not** thread them into step 3 — every
71
+ beat re-resolves the graph. A one-id run comes back `dispatchMode: "inline"`
72
+ and runs [`helpers/deliver-story.md`](helpers/deliver-story.md) in this
73
+ session; step 3 is the multi-Story path. An Epic id expands to its open child
72
74
  Stories first — **announce it**. It hard-errors (exit 1) on an id that is
73
- neither a Story nor an Epic, on an `Epic: #N` footer, on an Epic with no
74
- open children, or on edges it cannot read — a missing gate would co-dispatch
75
- against an unlanded blocker.
75
+ neither a Story nor an Epic, on an Epic with no open children, or on edges
76
+ it cannot read — a missing gate would co-dispatch against an unlanded
77
+ blocker.
76
78
 
77
79
  2. **Confirm (N>1).** Present the order; wait unless `--yes`.
78
80
 
79
- 3. **Sequence.** Loop until the tick reports `epilogueDue: true`:
81
+ 3. **Run the beat.** One command per beat, repeated until the envelope reports
82
+ the run `done`:
80
83
 
81
84
  ```bash
82
- node .agents/scripts/stories-wave-tick.js \
83
- --stories <id,id,...> --probe-live \
84
- --dispatched <every id you have dispatched so far>
85
+ node .agents/scripts/deliver-run.js \
86
+ --stories <id,id,...> [--handoff <id>]...
85
87
  ```
86
88
 
87
89
  **Do not add `--concurrency` unless the operator explicitly asked for a
88
90
  per-run cap** — an explicit value wins over config, so a literal defeats a
89
91
  `.agentrc.local.json` override.
90
92
 
91
- Each beat re-probes live state to derive done / in-flight itself; you never
92
- compute them. `--dispatched` is the one thing you must supply — the
93
- append-only list of every id you spawned this run. Cross-run de-confliction
94
- via the assignee lease is automatic. Branch on the exit code:
95
- - **0** — dispatch each `ready` id (already capped and overlap-free); an
96
- empty `ready` with work in flight means "waiting", so keep looping;
97
- `epilogueDue: true` means every Story is done — step 4.
93
+ Each beat re-probes live state and keeps its own accounting: the run ledger
94
+ under `<tempRoot>/run-<id>/` records every id it hands out, so **nothing is
95
+ maintained across beats by you**. Cross-run de-confliction via the assignee
96
+ lease is automatic. Branch on the exit code:
97
+ - **0** — spawn one `story-worker` per `ready[]` entry, **all in one turn**,
98
+ each with that entry's `promptPath` file as its prompt; run each `close[]`
99
+ entry's command foreground and serialized as hand-offs arrive. An empty
100
+ `ready` with work in flight means "waiting", so beat again — **unless
101
+ `stalledDispatch[]` is non-empty**: those ids were dispatched but still
102
+ read `agent::ready`, which is a live init window the first beats or two
103
+ and a spawn that never started one after that. Beating again cannot
104
+ clear it. Relay `stalledDispatchReason` — it carries the recovery — and
105
+ let the operator decide; never edit the ledger for them.
106
+ `done: true` means every Story is landed — step 4.
98
107
  - **2 / 3 / 4** — `cycleError` / `wedged` / `blocked`: stop the loop and
99
108
  route per reference. **4** is the protocol's HITL pause
100
109
  ([`instructions.md` § 1.J](../instructions.md)) — surface it and wait for
101
110
  the operator; never poll.
102
111
 
103
- 4. **Close each hand-off** (§ Closing what the workers hand back), then, with
104
- every Story landed, run the **per-run epilogue (N>1)**:
112
+ 4. **Land what the workers hand back** (§ Closing what the workers hand back —
113
+ the beat renders each command), then, with every Story landed, run the
114
+ **per-run epilogue (N>1)**:
105
115
  `node .agents/scripts/plan-run-epilogue.js --stories 101,102`; N=1 has none
106
116
  ([reference](helpers/deliver-reference.md)). Every close rolls its container
107
117
  Epic up from its children.
@@ -117,7 +127,10 @@ to an attended run.
117
127
  **The tail is the orchestrator's, not the worker's.** A dispatched
118
128
  `story-worker` stops at a pushed branch and returns a hand-off; **you** run
119
129
  [`helpers/deliver-story.md`](helpers/deliver-story.md) Step 3
120
- (`single-story-close.js`) for it, foreground, and relay its envelope.
130
+ (`single-story-close.js`) for it, foreground, and relay its envelope. Pass the
131
+ id as `--handoff` on the next beat and run the `close[]` command it prints
132
+ verbatim — the beat knows the run topology, so it decides
133
+ `--merge-watch-mode async` for you.
121
134
 
122
135
  **Serialize the tail.** Implementation runs in parallel; closing does not.
123
136
  Close one Story at a time — closes contend on the base branch, the merge queue
@@ -133,9 +146,11 @@ envelope — `landed` | `pending` | `blocked` | `failed`; statuses, exits and
133
146
  fields are digest § 5. `pending` is **not** a failure — run its `nextCommand`.
134
147
 
135
148
  **Branch model (authoritative).** `story-<id>` → PR → `main` (squash +
136
- required checks), per digest § 2; dependent Stories land sequentially. Ceremony
137
- depth (profiles + the derived level via `ceremony-routing.js`, which review
138
- depth reads): reference § Ceremony.
149
+ required checks), per digest § 2; dependent Stories land sequentially. The
150
+ acceptance verdict owner follows the ceremony profile alone
151
+ (`ceremony-routing.js`); the **derived level** feeds review depth instead
152
+ (`review-depth.js`). The rule and its one home are digest § 3; the profile
153
+ table is reference § Ceremony.
139
154
 
140
155
  ## Constraints
141
156
 
@@ -143,10 +158,11 @@ depth reads): reference § Ceremony.
143
158
  default to close-and-land (`delivery.routing.closeAndLand: true`); rest at
144
159
  `agent::closing` only when a human owns the merge.
145
160
  - **`/mandrel-deliver` never plans.** Planned tickets come from
146
- [`/mandrel-plan`](mandrel-plan.md), and an over-scope prompt **escalates and
147
- ends** — never invoke `/mandrel-plan` in this session to rescue it
148
- ([`helpers/deliver-light.md`](helpers/deliver-light.md) § Escalation is
149
- terminal).
161
+ [`/mandrel-plan`](mandrel-plan.md), and a refused prompt **escalates**: the
162
+ light path ends, and the escalation's `nextCommand` is what owns the work —
163
+ seeded with `escalation.reasons`, in this session or a fresh one
164
+ ([`helpers/deliver-light.md`](helpers/deliver-light.md) § Continuing into
165
+ `/mandrel-plan`).
150
166
 
151
167
  ## See also
152
168
 
@@ -11,7 +11,7 @@ description:
11
11
 
12
12
  ## Inputs
13
13
 
14
- Single planning path — there is no Epic/Story router, no scope-triage
14
+ Single planning path — there is no Epic/Story router, no split-triage
15
15
  `epic|story` verdict (Gate #3's container groups, never routes). **Derive the
16
16
  mode from what the operator typed, announce it, act**:
17
17
 
@@ -47,14 +47,14 @@ checkpoints, not sibling tickets ([ref](helpers/plan-reference.md)).
47
47
  ### 1. Interrogate
48
48
 
49
49
  ```bash
50
- node .agents/scripts/plan-context.js --seed "<seed>" \
51
- --out temp/plan-<slug>/plan-context.json
50
+ node .agents/scripts/plan-context.js --seed "<seed>"
52
51
  # or: --seed-file <path> | --tickets 123,456 | --amends #<id>
53
52
  ```
54
53
 
55
- **Always pass `--out`.** Persist auto-discovers the envelope from `--plan-dir`
56
- and derives source ids from its `sourceTickets[]`; it also writes
57
- **`stories.template.json`**, step 2's skeleton.
54
+ It writes the envelope and **`stories.template.json`** (step 2's skeleton) to
55
+ `<tempRoot>/plan-<slug>/` and prints a digest naming both; `--out` overrides
56
+ the path. Persist auto-discovers the envelope from `--plan-dir` and derives
57
+ source ids from its `sourceTickets[]`.
58
58
 
59
59
  The envelope carries docs context, the story-author prompt (`systemPrompts.story`,
60
60
  plus `systemPrompts.storySplitRules` for an N>1 draft and
@@ -87,18 +87,18 @@ persist parses either, serializes canonical markdown and syncs top-level
87
87
  `acceptance[]` / `verify[]` in — never dual-author them.
88
88
 
89
89
  **Grounding = your reads + Phase 8.** Nothing inventories the repo: read each
90
- file you cite; persist hard-errors on any `{path, assumption}` absent from the
91
- tree — a `refactors-existing` on a path the base branch **deleted or renamed**
92
- included. One rescue: a **never-tracked** one normalises to `creates`. Fields:
93
- [ref](helpers/plan-reference.md).
90
+ file you cite. A `changes[]` entry is a **bare path** by default — persist
91
+ derives its assumption by probing the base branch and reports the derivation;
92
+ pin `{path, assumption}` only when the probe would get it wrong, and always
93
+ for a `deletes`, which is the one shape still refused on an absent path.
94
+ Fields: [ref](helpers/plan-reference.md).
94
95
 
95
96
  Artifacts under `temp/plan-<slug>/`: `stories.json` (**length 1 by default**;
96
- a Spec is as long as the work needs, inline, never under `docs/`); optional
97
- `techspec.md` (**N===1 only**, folded into `## Spec`) and
98
- `acceptance-manifest.json` (N>1 — `--plan-acceptance`). Use the envelope
97
+ a Spec is as long as the work needs, inline, never under `docs/`) and optional
98
+ `techspec.md` (**N===1 only**, folded into `## Spec`). Use the envelope
99
99
  `systemPrompts.story`; split only under the policy above, and when you do,
100
- read `systemPrompts.storySplitRules` too — it carries the schedule and
101
- partition rules the core omits. In **tickets mode** also read
100
+ read `systemPrompts.storySplitRules` too — it carries the schedule rules and
101
+ the same-wave collision refusal the core omits. In **tickets mode** also read
102
102
  `systemPrompts.storyTicketsRules`: the source ticket is evidence, not a
103
103
  template — re-derive `acceptance[]` rather than carrying its list, handles
104
104
  and tier suffixes forward.
@@ -112,32 +112,36 @@ The maker-blind **pre-mortem** critic is not a step of this spine: run
112
112
 
113
113
  ### 3. Persist
114
114
 
115
- **Gate #2** — STOP for approval before persist **only** when the operator asked
116
- to review (`--force-review`). Under `--yes`, auto-proceed.
115
+ **Gate #2** — STOP for approval before persist when the draft carries **more
116
+ than one Story** (a split always earns operator eyes, `--force-review` or
117
+ not), or when the operator asked to review (`--force-review`).
118
+ Under `--yes`, auto-proceed.
117
119
 
118
120
  **Gate #3 — adopt, else create.** Offer the top `epicCandidates[]` Epic at
119
121
  **any N** (`--epic <id>`); else, at **N>2**, a new container (`--epic-title` /
120
122
  `--epic-goal`). Never unasked ([ref](helpers/plan-reference.md)).
121
123
 
122
- Run persist `--dry-run` **first** — same command, writes suppressed; every gate
123
- runs before the first `createIssue`, and the run **lists its warnings**
124
- (a `creates` / `refactors-existing` the base branch disagrees with, a goal or
125
- acceptance path absent at base, an open question in a body) and the
126
- `changes[]` repairs it applied ([list](helpers/plan-reference.md)). Read
127
- them; they never stop the persist:
124
+ **One command.** Persist runs every gate write-free first — including the
125
+ **same-wave collision refusal**, which rejects an N>1 draft whose siblings
126
+ declare a common path (merge them, or order them with `depends_on`) — and,
127
+ when the gate list comes back clean, creates the issues in the same
128
+ invocation:
128
129
 
129
130
  ```bash
130
131
  node .agents/scripts/plan-persist.js \
131
132
  --stories temp/plan-<slug>/stories.json \
132
133
  --plan-dir temp/plan-<slug> \
133
- [--plan-acceptance temp/plan-<slug>/acceptance-manifest.json] \
134
134
  [--tech-spec temp/plan-<slug>/techspec.md] \
135
135
  [--source-tickets 123,456] \
136
136
  [--epic <id> | --epic-title "<name>" --epic-goal "<one paragraph>"]
137
137
  ```
138
138
 
139
- `--chain-on-clean` folds a clean dry-run into the persist for **any** plan —
140
- the dry-run's warning list is the review.
139
+ The run **lists its warnings** (a `creates` / `refactors-existing` the base
140
+ branch disagrees with, a goal or acceptance path absent at base, an empty
141
+ `verify[]`, a source id assigned to the primary Story by default, an open
142
+ question in a body) and the `changes[]` repairs it applied
143
+ ([list](helpers/plan-reference.md)). Read them; they never stop the persist.
144
+ Add `--dry-run` to validate without creating anything.
141
145
 
142
146
  Persist creates `type::story` issue(s), a **metadata-only** `plan-run::<id>`
143
147
  label, `blocked by #<id>` footers for every `depends_on` edge, and on a Gate #3
@@ -152,12 +156,10 @@ also comments on and closes each source id ([ref](helpers/plan-reference.md)).
152
156
  - Duplicate search targets open Stories (`type::story`), not Epics; and
153
157
  deterministic gates still fail closed under `--yes`.
154
158
  - A container Epic is never a work item and only an **open** one is adoptable;
155
- no Story body gains an `Epic: #N` footer (linkage is parent→child only).
159
+ linkage is parent→child only.
156
160
  - `depends_on` takes a sibling slug or `#<id>` (open Story).
157
161
 
158
162
  ## See also
159
163
 
160
164
  [`/mandrel-deliver`](mandrel-deliver.md), [`/audit-to-stories`](audit-to-stories.md),
161
- [`helpers/plan-reference.md`](helpers/plan-reference.md) (on-demand detail),
162
- [`core/scope-triage`](../skills/core/scope-triage/SKILL.md) — optional
163
- split-advisory notes only (no routing verdict).
165
+ [`helpers/plan-reference.md`](helpers/plan-reference.md) (on-demand detail).
@@ -7,7 +7,7 @@ description: >-
7
7
  leaves unowned: reconcile `.agentrc.json`, install the stabilized
8
8
  quality-gate surface, refresh the harness permission allowlist, reconcile
9
9
  the consumer's `AGENTS.md` / runbooks against the surfaced changelog, and
10
- stage + commit the staged lockfile bump.
10
+ stage + commit the dependency bump the CLI's closing report describes.
11
11
  ---
12
12
 
13
13
  # /mandrel-update
@@ -19,14 +19,15 @@ description: >-
19
19
  > **distribution-agnostic judgment steps** the CLI deliberately does **not**
20
20
  > perform — config reconciliation, the quality-gate installs, the
21
21
  > permission-allowlist refresh, the consumer-side changelog reconciliation,
22
- > and the stage-and-commit of the staged lockfile bump.
22
+ > and the stage-and-commit of the dependency bump.
23
23
 
24
24
  The upgrade contract, in brief: the version only moves on explicit
25
25
  invocation (no `postinstall` drift — teammates track the committed
26
26
  lockfile pin, and CI's `npm ci` honours it); majors apply like any other
27
27
  bump (Mandrel ships hard cutovers — the surfaced changelog is the
28
- migration guide); the CLI **never commits** (the lockfile bump is left
29
- staged for operator review); and the only authoritative writer of the
28
+ migration guide); the CLI **never mutates git** — it reads the index and
29
+ **reports** whether the bump is staged, so the operator follows that
30
+ report rather than an assumption; and the only authoritative writer of the
30
31
  generated `.claude/commands/` tree is
31
32
  [`sync-claude-commands.js`](../scripts/sync-claude-commands.js), invoked by
32
33
  the CLI's sync step.
@@ -80,22 +81,32 @@ npx mandrel update
80
81
  The dry run resolves the newest published version and prints the ordered
81
82
  step plan (`npm-update → runSync → runMigrations → doctor → surface
82
83
  changelog`) without touching anything — read the planned target version
83
- before applying. The live run drives those phases in order, leaves the
84
- lockfile bump **staged** (never committed), and finishes by printing the
85
- `docs/CHANGELOG.md` sections covering the applied range `(current, target]`.
86
- **Capture that changelog output** — Step 4 reconciles the consumer's own
87
- instructions against it. Already-newest is a clean no-op (`Already up to
88
- date`, exit 0).
84
+ before applying. The live run drives those phases in order, never commits,
85
+ and finishes by printing the `docs/CHANGELOG.md` sections covering the
86
+ applied range `(current, target]`. **Capture that changelog output** —
87
+ Step 4 reconciles the consumer's own instructions against it.
88
+ Already-newest is a clean no-op (`Already up to date`, exit 0).
89
+
90
+ **Read the closing line — it reports the real index state.** Staging is
91
+ package-manager-specific (`npm install` may stage; `pnpm add` / `yarn add`
92
+ stage nothing), so the CLI probes git read-only and tells you which of three
93
+ states you are in rather than asserting one:
94
+
95
+ | Closing line | What it means | What you do |
96
+ | --- | --- | --- |
97
+ | `The dependency bump is staged for review (package.json, <lockfile>).` | The index already carries both. | Go to Step 2. |
98
+ | `The dependency bump is NOT staged. … Review and stage it: git add …` | Nothing (or only half) is staged. | Run the exact `git add` the line prints — it names `.agents/` too when this consumer tracks the materialized tree. |
99
+ | `Review the working tree and commit the bump (git not available to report staging state).` | The probe could not read git; the update still succeeded (exit 0). | Inspect `git status` yourself before Step 5. |
89
100
 
90
101
  ## Step 2.5 — Partial-upgrade recovery (**blocker — resolve before Step 5**)
91
102
 
92
- The install phase stages the lockfile bump *before* the later phases run,
103
+ The install phase writes the dependency bump *before* the later phases run,
93
104
  and by deliberate design the CLI **never rolls back the install on
94
105
  failure**. So when a post-install phase (`sync` / `sync-commands` /
95
106
  `migrate` / `doctor`) exits non-zero you land in a **partially-upgraded
96
- state**: the bump is already staged while `.agents/` may be
97
- half-materialized, the command tree out of sync, or a migration partially
98
- applied — and the operator is one `git commit` away from recording a broken
107
+ state**: the bump is already on disk (and possibly in the index) while
108
+ `.agents/` may be half-materialized, the command tree out of sync, or a
109
+ migration partially applied — and the operator is one `git commit` away from recording a broken
99
110
  half-upgrade as "done". **Treat any post-install phase failure as an
100
111
  explicit blocker: do not proceed to Step 5 until the failed phase is
101
112
  recovered and a clean re-run reports success.**
@@ -203,22 +214,25 @@ between the installed and target versions:
203
214
  workflows for renamed flags / changed exit codes / removed scripts.
204
215
 
205
216
  Do not invent updates — silence is a valid review outcome. Stage every
206
- consumer-side edit alongside the staged lockfile bump so the upgrade and
207
- the reconciliation land in one reviewable commit.
217
+ consumer-side edit alongside the dependency bump so the upgrade and the
218
+ reconciliation land in one reviewable commit.
208
219
 
209
220
  ## Step 5 — Commit the bump
210
221
 
211
- > **Blocker check before you commit.** The staged lockfile bump is only safe
222
+ > **Blocker check before you commit.** The dependency bump is only safe
212
223
  > to commit once every post-install phase has gone green. If `npx mandrel
213
224
  > update` exited non-zero, resolve it via
214
225
  > [Step 2.5 — Partial-upgrade recovery](#step-25--partial-upgrade-recovery-blocker--resolve-before-step-5)
215
226
  > **before** the `git commit` below. Committing over a half-upgrade records
216
227
  > a broken state as "done".
217
228
 
218
- Stage and commit the bump plus everything the wraparound touched:
229
+ Stage and commit the bump plus everything the wraparound touched. Start from
230
+ the `git add` the CLI's closing report printed (it names the detected
231
+ lockfile, and `.agents/` when this consumer tracks it), then add the
232
+ wraparound paths:
219
233
 
220
234
  ```bash
221
- git add package.json package-lock.json .agentrc.json .claude/settings.json AGENTS.md # plus any runbook files touched in Step 4
235
+ git add package.json package-lock.json .agentrc.json .claude/settings.json AGENTS.md # lockfile per the CLI report; plus any runbook files touched in Step 4
222
236
  git commit -m "chore: update mandrel to v<NEW_VERSION>
223
237
 
224
238
  Upgraded v<OLD_VERSION> → v<NEW_VERSION> via mandrel update.
@@ -239,8 +253,9 @@ materialized tree.
239
253
 
240
254
  - **Idempotent.** A second `mandrel update` after a successful run hits the
241
255
  no-op short-circuit — exit 0, nothing bumped.
242
- - **No auto-commit.** The CLI leaves the lockfile bump staged and never runs
243
- git; the operator writes the commit (Step 5).
256
+ - **No auto-commit.** The CLI never stages and never commits — it reads git
257
+ only to report whether the bump is staged; the operator writes the commit
258
+ (Step 5).
244
259
  - **No framework-side version bump.** This workflow advances the
245
260
  *consumer's* pinned version; framework releases remain the maintainer's
246
261
  call via release-please.
package/README.md CHANGED
@@ -86,9 +86,9 @@ time to confirm the install is healthy.
86
86
  >
87
87
  > Prefer a surgical alternative? Replace `shamefully-hoist` with a scoped
88
88
  > `public-hoist-pattern[]=` line per package listed in
89
- > `.agents/runtime-deps.json` (`ajv`, `ajv-formats`, `js-yaml`, `minimatch`,
90
- > `picomatch`, `typhonjs-escomplex`). If `mandrel doctor`
91
- > reports `runtime-deps missing: …`, this is the fix.
89
+ > `.agents/runtime-deps.json` — read the file rather than copying a list from
90
+ > here, since the complexity kernel's closure is several packages. If
91
+ > `mandrel doctor` reports `runtime-deps missing: …`, this is the fix.
92
92
 
93
93
  `bootstrap.js` is interactive on a TTY and auto-accepts the
94
94
  owner/repo/base branch/operator handle it can infer from your local
package/docs/CHANGELOG.md CHANGED
@@ -15,6 +15,49 @@ All notable changes to this project will be documented in this file.
15
15
  -->
16
16
  <!-- markdownlint-disable-file MD004 MD012 MD037 -->
17
17
 
18
+ ## [2.60.0](https://github.com/dsj1984/mandrel/compare/mandrel-v2.59.0...mandrel-v2.60.0) (2026-09-18)
19
+
20
+
21
+ ### ⚠ BREAKING CHANGES
22
+
23
+ * `delivery.feedbackLoop.auditResultsAutoFile` is no longer a valid `.agentrc.json` key. `mandrel update` strips it automatically.
24
+ * deliver-light.js no longer accepts --kinds, --magnitude, --uncertainty or --route; STORY_SHAPE_CEILINGS and the four ceiling SHAPE_CODES are removed, and no gate envelope carries warnings[].
25
+ * under delivery.routing.ceremonyProfile standard the acceptance verdict is authored inline; set strict to keep a fresh-context critic.
26
+ * plan-persist.js persists on a clean dry-run by default — pass --dry-run to validate only; the "Commit subject begins with '<prefix>:'" acceptance validator is removed; an empty verify[] is a warning, not a refusal.
27
+ * delivery.feedbackLoop.retroProposals and delivery.feedbackLoop.auditResultsAutoFile now default to false; a consumer that relied on auto-filed follow-up issues sets both to true in .agentrc.json.
28
+
29
+ ### Added
30
+
31
+ * add deliver-run.js so a multi-Story run is one script beat plus spawns and closes, not a hand-driven protocol ([#5345](https://github.com/dsj1984/mandrel/issues/5345)) ([#5354](https://github.com/dsj1984/mandrel/issues/5354)) ([591dad7](https://github.com/dsj1984/mandrel/commit/591dad75923133bef9e3a9d095ac47442464943b))
32
+ * clamp deliver-runner concurrency to one when worktree isolation resolves off, so two workers can never share a checkout ([#5357](https://github.com/dsj1984/mandrel/issues/5357)) ([#5360](https://github.com/dsj1984/mandrel/issues/5360)) ([1f3f651](https://github.com/dsj1984/mandrel/commit/1f3f6515e4a7a4fd2c730001dc6dd4cdb44f251e))
33
+ * close fails fast with a named blocker when GitHub GraphQL is unavailable, instead of a raw 403 after the full gate chain ([#5355](https://github.com/dsj1984/mandrel/issues/5355)) ([#5359](https://github.com/dsj1984/mandrel/issues/5359)) ([b46c263](https://github.com/dsj1984/mandrel/commit/b46c26329dd7cc1e968e607ccaae9d51a9f1ef84))
34
+ * collapse the ceremony decision to what it decides and delete the inert layer around it ([#5366](https://github.com/dsj1984/mandrel/issues/5366)) ([#5375](https://github.com/dsj1984/mandrel/issues/5375)) ([54d0ece](https://github.com/dsj1984/mandrel/commit/54d0ece864d8cb3ec824228dc097999781f7b29f))
35
+ * collapse the light path to one gate and let an escalation continue in-session ([#5344](https://github.com/dsj1984/mandrel/issues/5344)) ([#5353](https://github.com/dsj1984/mandrel/issues/5353)) ([c09acd6](https://github.com/dsj1984/mandrel/commit/c09acd69a8525dd33073922a7541bb9488929249))
36
+ * cut the restated delivery rules, the v1 refusals and the sibling-coherence step, and turn auto-filing off by default ([#5341](https://github.com/dsj1984/mandrel/issues/5341)) ([#5350](https://github.com/dsj1984/mandrel/issues/5350)) ([349d346](https://github.com/dsj1984/mandrel/commit/349d3468c11a23504b510ac80d0716b5dd709853))
37
+ * delivery ceremony diet: one inline verdict per Story, opt-in fresh critics and audit roster, fewer Story comments, and one CI rerun after a recorded verdict ([#5343](https://github.com/dsj1984/mandrel/issues/5343)) ([#5352](https://github.com/dsj1984/mandrel/issues/5352)) ([1ac35c4](https://github.com/dsj1984/mandrel/commit/1ac35c40745bd1d6f0945f9a5ffd3f975452dbff))
38
+ * loosen plan authoring: one persist command, bare paths in changes[], empty verify[] as a warning, and the fossil validators removed ([#5342](https://github.com/dsj1984/mandrel/issues/5342)) ([#5351](https://github.com/dsj1984/mandrel/issues/5351)) ([c7534a5](https://github.com/dsj1984/mandrel/commit/c7534a523cf39b24324d26f2cad81b2ec4fb22df))
39
+ * make a dispatch that never reached init visible in the beat envelope ([#5363](https://github.com/dsj1984/mandrel/issues/5363)) ([#5370](https://github.com/dsj1984/mandrel/issues/5370)) ([ab3c755](https://github.com/dsj1984/mandrel/commit/ab3c7553975ef5dce8c60112d7f8c278c5dd0d8d))
40
+
41
+
42
+ ### Fixed
43
+
44
+ * `mandrel update` reports whether the bump is actually staged instead of asserting it ([#5339](https://github.com/dsj1984/mandrel/issues/5339)) ([#5349](https://github.com/dsj1984/mandrel/issues/5349)) ([07f63c7](https://github.com/dsj1984/mandrel/commit/07f63c79bdb19a532e82d413de719a194c170504))
45
+ * anchor pre-push coverage capture and the CRAP preview on one resolved ref ([#5365](https://github.com/dsj1984/mandrel/issues/5365)) ([#5373](https://github.com/dsj1984/mandrel/issues/5373)) ([cb189e3](https://github.com/dsj1984/mandrel/commit/cb189e3a005749305cc0ce2075ea17793493bb0e))
46
+ * capture coverage before the pre-push quality preview (refs [#5356](https://github.com/dsj1984/mandrel/issues/5356)) ([#5358](https://github.com/dsj1984/mandrel/issues/5358)) ([14ef1dd](https://github.com/dsj1984/mandrel/commit/14ef1dde384455f16f3ddd502881c132fd6ae056))
47
+ * close the three correctness holes the plan-authoring loosening left in persist ([#5361](https://github.com/dsj1984/mandrel/issues/5361)) ([#5372](https://github.com/dsj1984/mandrel/issues/5372)) ([aef03bd](https://github.com/dsj1984/mandrel/commit/aef03bd90e14cc2dac68ea4d7a04f0f262e2494e))
48
+ * delete the write-only plan checkpoint payload and the three unreachable modules ([#5367](https://github.com/dsj1984/mandrel/issues/5367)) ([#5374](https://github.com/dsj1984/mandrel/issues/5374)) ([4c0c835](https://github.com/dsj1984/mandrel/commit/4c0c835d9eded8e993bb7ebd3e66e86bec4d2377))
49
+ * demote the workflow prose ratchets to reports and delete the reference sections that describe retired mechanisms ([#5340](https://github.com/dsj1984/mandrel/issues/5340)) ([#5347](https://github.com/dsj1984/mandrel/issues/5347)) ([8d76329](https://github.com/dsj1984/mandrel/commit/8d76329e3aacc726e39d1839f01e618fb7361662))
50
+ * report the index mandrel update actually observes, from one read ([#5364](https://github.com/dsj1984/mandrel/issues/5364)) ([#5371](https://github.com/dsj1984/mandrel/issues/5371)) ([12af953](https://github.com/dsj1984/mandrel/commit/12af95383f7ddbfe137715aa10bce3b25a7b95a3))
51
+ * treat a rate-limited GraphQL probe as fail-open rather than unreachable (refs [#5362](https://github.com/dsj1984/mandrel/issues/5362)) ([#5369](https://github.com/dsj1984/mandrel/issues/5369)) ([db6a9e0](https://github.com/dsj1984/mandrel/commit/db6a9e08441baf40281962e0b226575f9c7b1846))
52
+
53
+ ## [2.59.0](https://github.com/dsj1984/mandrel/compare/mandrel-v2.58.0...mandrel-v2.59.0) (2026-09-14)
54
+
55
+
56
+ ### Added
57
+
58
+ * planning stops fragmenting cohesive work: the acceptance band goes, the dispatcher's own collision predicate becomes the split gate, and the audit seed stops pre-cutting the partition ([#5332](https://github.com/dsj1984/mandrel/issues/5332)) ([#5334](https://github.com/dsj1984/mandrel/issues/5334)) ([e6408e4](https://github.com/dsj1984/mandrel/commit/e6408e44d3f9a7fcbdf8c9a7df4a1b68fdffd88b))
59
+ * replace the complexity kernel's parse and dispatch layers and declare the runtime closure it leaves behind, so the framework's dependency guards describe what actually loads ([#5336](https://github.com/dsj1984/mandrel/issues/5336)) ([#5337](https://github.com/dsj1984/mandrel/issues/5337)) ([821c4f5](https://github.com/dsj1984/mandrel/commit/821c4f55fe3fe9b91b83a4ef9fe9ddee5da9db8f))
60
+
18
61
  ## [2.58.0](https://github.com/dsj1984/mandrel/compare/mandrel-v2.57.0...mandrel-v2.58.0) (2026-09-12)
19
62
 
20
63
 
@@ -41,6 +41,8 @@ import {
41
41
  } from '../../.agents/scripts/lib/bootstrap/project-bootstrap.js';
42
42
  import { isCommandExcluded } from '../../.agents/scripts/lib/command-header.js';
43
43
  import { getDeliveryRouting } from '../../.agents/scripts/lib/config/delivery-routing.js';
44
+ import { isResolvable } from '../../.agents/scripts/lib/runtime-deps/dep-resolution.js';
45
+ import { describeParserMajorError } from '../../.agents/scripts/lib/runtime-deps/parser-major.js';
44
46
  import {
45
47
  defaultResolvePackageRoot,
46
48
  listFiles as listPayloadFiles,
@@ -519,6 +521,30 @@ function runAgentsInSync({
519
521
  // check: runtime-deps
520
522
  // ---------------------------------------------------------------------------
521
523
 
524
+ /**
525
+ * The verdict when every required package is present.
526
+ *
527
+ * Presence is not the whole contract: `.agents/` resolves from the consumer's
528
+ * node_modules, so a declared range cannot enforce which `@babel/parser` major
529
+ * the complexity kernel actually gets, and the wrong major fails scoring
530
+ * mid-scan with an opaque plugin-list error. Reporting it here puts it where a
531
+ * consumer is already looking for what to fix.
532
+ *
533
+ * @param {() => string|null} parserMajorError
534
+ * @returns {{ ok: boolean, detail: string, remedy?: string }}
535
+ */
536
+ function allPresentVerdict(parserMajorError) {
537
+ const parserProblem = parserMajorError();
538
+ if (parserProblem === null) {
539
+ return { ok: true, detail: 'all dependencies found' };
540
+ }
541
+ return {
542
+ ok: false,
543
+ detail: 'dependency version unsupported',
544
+ remedy: parserProblem,
545
+ };
546
+ }
547
+
522
548
  /**
523
549
  * Verify that the framework's required runtime dependencies are resolvable
524
550
  * from the project's node_modules.
@@ -536,6 +562,8 @@ function runAgentsInSync({
536
562
  * is missing.
537
563
  * - `manifestRequired` — array of required package names, skips the
538
564
  * filesystem read of `runtime-deps.json`.
565
+ * - `parserMajorError()` — replaces the resolved-parser-major probe, so the
566
+ * unsupported-major report is assertable without installing one.
539
567
  *
540
568
  * @param {{ projectRoot?: string, resolve?: (dep: string) => string, manifestRequired?: string[] }} [opts]
541
569
  * @returns {{ ok: boolean, detail: string, remedy?: string }}
@@ -544,6 +572,7 @@ function runRuntimeDeps({
544
572
  projectRoot,
545
573
  resolve: resolveSeam,
546
574
  manifestRequired,
575
+ parserMajorError = describeParserMajorError,
547
576
  } = {}) {
548
577
  // Anchor at process.cwd() (the consumer root), not resolveProjectRoot() (the
549
578
  // package root). Under pnpm isolated-mode the consumer's node_modules are not
@@ -569,33 +598,24 @@ function runRuntimeDeps({
569
598
 
570
599
  const missing = [];
571
600
 
572
- if (resolveSeam) {
573
- for (const dep of required) {
574
- try {
575
- resolveSeam(dep);
576
- } catch {
577
- missing.push(dep);
578
- }
579
- }
580
- } else {
581
- // Anchor resolution to the consumer project root so it mirrors the context
582
- // in which the framework scripts run (they free-ride on the consumer's
583
- // node_modules). Under pnpm isolated-mode the consumer's node_modules are
584
- // not reachable from inside node_modules/mandrel/; anchoring at process.cwd()
585
- // finds them correctly.
586
- const req = createRequire(path.join(root, 'package.json'));
587
- for (const dep of required) {
588
- try {
589
- req.resolve(dep);
590
- } catch {
591
- missing.push(dep);
592
- }
593
- }
601
+ // Anchor resolution to the consumer project root so it mirrors the context
602
+ // in which the framework scripts run (they free-ride on the consumer's
603
+ // node_modules). Under pnpm isolated-mode the consumer's node_modules are
604
+ // not reachable from inside node_modules/mandrel/; anchoring at process.cwd()
605
+ // finds them correctly.
606
+ //
607
+ // `isResolvable` is shared with the framework-side preflight rather than
608
+ // reimplemented here: it probes the bare specifier AND `<name>/package.json`,
609
+ // because a dependency with no `main` and no `exports` cannot be resolved by
610
+ // name at all. Probing only the bare name reports such a package missing
611
+ // while it sits installed, and this check gates `mandrel doctor`.
612
+ const resolve =
613
+ resolveSeam ?? createRequire(path.join(root, 'package.json')).resolve;
614
+ for (const dep of required) {
615
+ if (!isResolvable(dep, resolve)) missing.push(dep);
594
616
  }
595
617
 
596
- if (missing.length === 0) {
597
- return { ok: true, detail: 'all dependencies found' };
598
- }
618
+ if (missing.length === 0) return allPresentVerdict(parserMajorError);
599
619
  return {
600
620
  ok: false,
601
621
  detail: `missing: ${missing.join(', ')}`,