mandrel 2.31.0 → 2.32.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 (250) hide show
  1. package/.agents/README.md +13 -17
  2. package/.agents/agents/acceptance-critic.md +1 -2
  3. package/.agents/docs/SDLC.md +4 -4
  4. package/.agents/docs/agentrc-reference.json +61 -57
  5. package/.agents/docs/configuration.md +274 -227
  6. package/.agents/docs/execution-reference.md +13 -14
  7. package/.agents/docs/quality-gates.md +195 -23
  8. package/.agents/instructions.md +2 -5
  9. package/.agents/rules/git-conventions-reference.md +27 -27
  10. package/.agents/rules/git-conventions.md +4 -2
  11. package/.agents/rules/known-tooling-behavior.md +66 -30
  12. package/.agents/rules/testing-standards.md +35 -71
  13. package/.agents/runtime-deps.json +0 -1
  14. package/.agents/schemas/agentrc.schema.json +1939 -1400
  15. package/.agents/schemas/lifecycle/README.md +21 -14
  16. package/.agents/schemas/lifecycle/ledger-record.schema.json +76 -22
  17. package/.agents/schemas/story-deliver-terminal.schema.json +2 -2
  18. package/.agents/scripts/README.md +7 -29
  19. package/.agents/scripts/apply-quality-bootstrap.js +27 -34
  20. package/.agents/scripts/bootstrap.js +28 -26
  21. package/.agents/scripts/check-baseline-drift.js +73 -13
  22. package/.agents/scripts/check-baseline-scope.js +362 -0
  23. package/.agents/scripts/check-dead-exports.js +9 -1
  24. package/.agents/scripts/check-gherkin-corpus.js +508 -0
  25. package/.agents/scripts/check-knip-entries.js +136 -0
  26. package/.agents/scripts/check-lifecycle-lint.js +36 -112
  27. package/.agents/scripts/check-schema-references.js +1 -1
  28. package/.agents/scripts/diagnose-friction.js +7 -4
  29. package/.agents/scripts/generate-config-docs.js +263 -171
  30. package/.agents/scripts/install-matrix-assert.js +0 -1
  31. package/.agents/scripts/lib/ITicketingProvider.js +0 -58
  32. package/.agents/scripts/lib/audit-baselines/staleness.js +6 -6
  33. package/.agents/scripts/lib/audit-baselines/trend.js +7 -8
  34. package/.agents/scripts/lib/audit-baselines/weights.js +4 -5
  35. package/.agents/scripts/lib/audit-suite/checklist-threading.js +1 -1
  36. package/.agents/scripts/lib/audit-to-stories/build-story-body.js +0 -1
  37. package/.agents/scripts/lib/baselines/envelope.js +41 -60
  38. package/.agents/scripts/lib/baselines/git-base.js +30 -37
  39. package/.agents/scripts/lib/baselines/kinds/_crap-new-method-gate.js +103 -0
  40. package/.agents/scripts/lib/baselines/kinds/_crap-read.js +150 -0
  41. package/.agents/scripts/lib/baselines/kinds/crap.js +25 -65
  42. package/.agents/scripts/lib/baselines/orphan-pruner.js +233 -0
  43. package/.agents/scripts/lib/baselines/refresh-service.js +6 -8
  44. package/.agents/scripts/lib/baselines/scope-assert.js +223 -0
  45. package/.agents/scripts/lib/baselines/scope-inventory.js +314 -0
  46. package/.agents/scripts/lib/bdd-step-index.js +326 -0
  47. package/.agents/scripts/lib/bootstrap/install-ledger.js +5 -3
  48. package/.agents/scripts/lib/bootstrap/issue-forms-template.js +4 -6
  49. package/.agents/scripts/lib/bootstrap/manifest.js +17 -40
  50. package/.agents/scripts/lib/bootstrap/project-bootstrap.js +12 -59
  51. package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +62 -2
  52. package/.agents/scripts/lib/checks/loop-health.js +9 -37
  53. package/.agents/scripts/lib/child-exec.js +193 -0
  54. package/.agents/scripts/lib/cli/standard-args.js +1 -1
  55. package/.agents/scripts/lib/cli-args.js +64 -0
  56. package/.agents/scripts/lib/close-validation/gates.js +2 -2
  57. package/.agents/scripts/lib/close-validation/runner.js +3 -3
  58. package/.agents/scripts/lib/config/acceptance-eval.js +5 -52
  59. package/.agents/scripts/lib/config/commands.js +3 -5
  60. package/.agents/scripts/lib/config/explain.js +5 -7
  61. package/.agents/scripts/lib/config/gates/bundle-size.schema.js +32 -6
  62. package/.agents/scripts/lib/config/gates/coverage.schema.js +25 -5
  63. package/.agents/scripts/lib/config/gates/crap-incremental-coverage.schema.js +12 -2
  64. package/.agents/scripts/lib/config/gates/crap.schema.js +68 -23
  65. package/.agents/scripts/lib/config/gates/duplication.schema.js +29 -17
  66. package/.agents/scripts/lib/config/gates/index.js +5 -2
  67. package/.agents/scripts/lib/config/gates/lighthouse.schema.js +34 -6
  68. package/.agents/scripts/lib/config/gates/lint.schema.js +11 -2
  69. package/.agents/scripts/lib/config/gates/maintainability.schema.js +37 -15
  70. package/.agents/scripts/lib/config/gates/mutation.schema.js +15 -3
  71. package/.agents/scripts/lib/config/gates/shared.js +58 -9
  72. package/.agents/scripts/lib/config/github.js +0 -1
  73. package/.agents/scripts/lib/config/limits.js +3 -48
  74. package/.agents/scripts/lib/config/qa.js +105 -0
  75. package/.agents/scripts/lib/config/temp-paths.js +6 -5
  76. package/.agents/scripts/lib/config-settings-schema-delivery.js +237 -56
  77. package/.agents/scripts/lib/config-settings-schema-quality.js +209 -29
  78. package/.agents/scripts/lib/config-settings-schema.js +386 -39
  79. package/.agents/scripts/lib/crap-baseline-join.js +126 -9
  80. package/.agents/scripts/lib/crap-utils.js +84 -520
  81. package/.agents/scripts/lib/dead-exports-knip.js +79 -10
  82. package/.agents/scripts/lib/degraded-mode.js +2 -2
  83. package/.agents/scripts/lib/doc-tiers.js +3 -3
  84. package/.agents/scripts/lib/feedback-loop/graduator-core.js +46 -104
  85. package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +10 -8
  86. package/.agents/scripts/lib/fs-walk.js +52 -0
  87. package/.agents/scripts/lib/git-branch-lifecycle.js +2 -2
  88. package/.agents/scripts/lib/git-utils.js +16 -36
  89. package/.agents/scripts/lib/knip-entry-sync.js +469 -0
  90. package/.agents/scripts/lib/observability/metrics-ledger.js +1 -1
  91. package/.agents/scripts/lib/observability/runtime-friction.js +10 -0
  92. package/.agents/scripts/lib/observability/signal-validator.js +5 -85
  93. package/.agents/scripts/lib/observability/signals-writer.js +19 -62
  94. package/.agents/scripts/lib/observability/source-classifier.js +5 -7
  95. package/.agents/scripts/lib/observability/terse-result.js +3 -3
  96. package/.agents/scripts/lib/orchestration/behind-recovery.js +114 -0
  97. package/.agents/scripts/lib/orchestration/ceremony-routing.js +7 -8
  98. package/.agents/scripts/lib/orchestration/ci-rerun-guard.js +34 -33
  99. package/.agents/scripts/lib/orchestration/code-review.js +2 -2
  100. package/.agents/scripts/lib/orchestration/complexity-gate.js +43 -161
  101. package/.agents/scripts/lib/orchestration/diff-magnitude.js +4 -4
  102. package/.agents/scripts/lib/orchestration/label-transitions.js +3 -2
  103. package/.agents/scripts/lib/orchestration/lease-guard-shared.js +12 -38
  104. package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +5 -6
  105. package/.agents/scripts/lib/orchestration/plan-metrics.js +2 -3
  106. package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +6 -0
  107. package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +0 -1
  108. package/.agents/scripts/lib/orchestration/{lifecycle/listeners/watcher.js → pr-watch.js} +58 -208
  109. package/.agents/scripts/lib/orchestration/resolve-stories.js +5 -15
  110. package/.agents/scripts/lib/orchestration/review-providers/codex.js +1 -1
  111. package/.agents/scripts/lib/orchestration/review-providers/mi-exemptions.js +130 -0
  112. package/.agents/scripts/lib/orchestration/review-providers/native.js +30 -16
  113. package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +1 -1
  114. package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +37 -26
  115. package/.agents/scripts/lib/orchestration/single-story-close/phases/conventional-subject.js +376 -0
  116. package/.agents/scripts/lib/orchestration/single-story-close/phases/normalize-pr-title.js +161 -151
  117. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +15 -3
  118. package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +10 -15
  119. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +5 -0
  120. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-override.js +157 -0
  121. package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +0 -14
  122. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +59 -25
  123. package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +20 -31
  124. package/.agents/scripts/lib/orchestration/spec-spill.js +17 -3
  125. package/.agents/scripts/lib/orchestration/story-close/phases/review-core.js +7 -6
  126. package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +2 -1
  127. package/.agents/scripts/lib/orchestration/task-body-validator.js +4 -1
  128. package/.agents/scripts/lib/orchestration/ticket-lease.js +28 -127
  129. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +1 -1
  130. package/.agents/scripts/lib/orchestration/ticketing/reads.js +5 -5
  131. package/.agents/scripts/lib/orchestration/ticketing/transition.js +5 -4
  132. package/.agents/scripts/lib/orchestration/verify-tier-repair.js +107 -0
  133. package/.agents/scripts/lib/qa/coverage-verdict.js +5 -87
  134. package/.agents/scripts/lib/signals/detectors/common.js +1 -1
  135. package/.agents/scripts/lib/signals/index.js +8 -6
  136. package/.agents/scripts/lib/signals/schema.js +20 -25
  137. package/.agents/scripts/lib/signals/write.js +8 -8
  138. package/.agents/scripts/lib/story-body/story-body.js +12 -59
  139. package/.agents/scripts/lib/temp-retention.js +1 -1
  140. package/.agents/scripts/lib/templates/decomposer-prompts.js +16 -14
  141. package/.agents/scripts/lib/ticket-body-sections.js +4 -5
  142. package/.agents/scripts/lib/worktree/lifecycle/merge-reachability.js +13 -45
  143. package/.agents/scripts/lib/worktree/lifecycle/reap.js +4 -5
  144. package/.agents/scripts/lib/worktree-manager.js +2 -3
  145. package/.agents/scripts/lint-label-vocabulary.js +2 -24
  146. package/.agents/scripts/pr-watch-with-update.js +7 -5
  147. package/.agents/scripts/providers/github/cache.js +2 -2
  148. package/.agents/scripts/providers/github/comments.js +6 -28
  149. package/.agents/scripts/providers/github/compose.js +0 -15
  150. package/.agents/scripts/providers/github/errors.js +10 -27
  151. package/.agents/scripts/providers/github/request-helpers.js +1 -2
  152. package/.agents/scripts/providers/github/sub-issues.js +10 -218
  153. package/.agents/scripts/providers/github.js +4 -7
  154. package/.agents/scripts/prune-baseline-orphans.js +181 -0
  155. package/.agents/scripts/resolve-stories.js +0 -2
  156. package/.agents/scripts/run-lint.js +61 -61
  157. package/.agents/scripts/run-test-profile.js +6 -6
  158. package/.agents/scripts/run-verify.js +48 -30
  159. package/.agents/scripts/single-story-close.js +20 -0
  160. package/.agents/scripts/single-story-init.js +12 -35
  161. package/.agents/scripts/update-dead-exports-baseline.js +321 -0
  162. package/.agents/skills/core/gates-and-baselines/SKILL.md +2 -2
  163. package/.agents/skills/skills.index.json +1 -11
  164. package/.agents/workflows/audit-documentation.md +5 -6
  165. package/.agents/workflows/audit-to-stories.md +2 -2
  166. package/.agents/workflows/helpers/audit-lens-core.md +11 -12
  167. package/.agents/workflows/helpers/code-quality-guardrails.md +15 -14
  168. package/.agents/workflows/helpers/code-review.md +3 -8
  169. package/.agents/workflows/helpers/deliver-reference.md +2 -1
  170. package/.agents/workflows/helpers/deliver-story-reference.md +27 -16
  171. package/.agents/workflows/helpers/worktree-lifecycle.md +1 -2
  172. package/.agents/workflows/mandrel-update.md +10 -10
  173. package/.agents/workflows/qa-assist.md +15 -20
  174. package/.agents/workflows/qa-explore.md +9 -8
  175. package/README.md +1 -1
  176. package/docs/CHANGELOG.md +42 -0
  177. package/lib/migrations/index.js +2 -0
  178. package/lib/migrations/steps/2.32.0-retire-lint-baseline-command.js +127 -0
  179. package/package.json +12 -3
  180. package/.agents/schemas/lifecycle/checkpoint.written.schema.json +0 -13
  181. package/.agents/schemas/lifecycle/close-validate.end.schema.json +0 -18
  182. package/.agents/schemas/lifecycle/close-validate.start.schema.json +0 -13
  183. package/.agents/schemas/lifecycle/code-review.end.schema.json +0 -30
  184. package/.agents/schemas/lifecycle/code-review.start.schema.json +0 -12
  185. package/.agents/schemas/lifecycle/intervention.recorded.schema.json +0 -15
  186. package/.agents/schemas/lifecycle/loop.tick.schema.json +0 -20
  187. package/.agents/schemas/lifecycle/notification.emitted.schema.json +0 -18
  188. package/.agents/schemas/lifecycle/pr.created.schema.json +0 -14
  189. package/.agents/schemas/lifecycle/retro.end.schema.json +0 -16
  190. package/.agents/schemas/lifecycle/retro.start.schema.json +0 -12
  191. package/.agents/schemas/lifecycle/story.blocked.schema.json +0 -13
  192. package/.agents/schemas/lifecycle/story.dispatch.end.schema.json +0 -17
  193. package/.agents/schemas/lifecycle/story.dispatch.start.schema.json +0 -15
  194. package/.agents/schemas/lifecycle/story.merged.schema.json +0 -13
  195. package/.agents/scripts/check-gherkin-placeholders.js +0 -663
  196. package/.agents/scripts/check-lifecycle-doc-drift.js +0 -411
  197. package/.agents/scripts/lib/audit-suite/cli.js +0 -64
  198. package/.agents/scripts/lib/bootstrap/baselines-layout-migration.js +0 -202
  199. package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +0 -212
  200. package/.agents/scripts/lib/checks/baseline-drift-main-checkout.js +0 -104
  201. package/.agents/scripts/lib/checks/push-hook-parity.js +0 -106
  202. package/.agents/scripts/lib/checks/windows-coverage-noise-floor.js +0 -92
  203. package/.agents/scripts/lib/checks/worktree-bootstrap-env.js +0 -81
  204. package/.agents/scripts/lib/checks/worktree-residue-biome.js +0 -55
  205. package/.agents/scripts/lib/crap-baseline-index.js +0 -46
  206. package/.agents/scripts/lib/crap-utils-incremental.js +0 -113
  207. package/.agents/scripts/lib/dynamic-workflow/capability.js +0 -396
  208. package/.agents/scripts/lib/feedback-loop/audit-results-graduator.js +0 -335
  209. package/.agents/scripts/lib/mutation/baseline-snapshot.js +0 -239
  210. package/.agents/scripts/lib/mutation/config-detector.js +0 -119
  211. package/.agents/scripts/lib/mutation/stryker-runner.js +0 -306
  212. package/.agents/scripts/lib/mutation/survivor-report.js +0 -160
  213. package/.agents/scripts/lib/observability/active-story-env.js +0 -170
  214. package/.agents/scripts/lib/observability/tool-trace-hook.js +0 -456
  215. package/.agents/scripts/lib/orchestration/acceptance-clusters.js +0 -111
  216. package/.agents/scripts/lib/orchestration/context-envelope.js +0 -277
  217. package/.agents/scripts/lib/orchestration/detectors-phase.js +0 -194
  218. package/.agents/scripts/lib/orchestration/lifecycle/bus.js +0 -309
  219. package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +0 -181
  220. package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +0 -229
  221. package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +0 -54
  222. package/.agents/scripts/lib/orchestration/lifecycle/trace-logger.js +0 -344
  223. package/.agents/scripts/lib/orchestration/lint-baseline-service.js +0 -114
  224. package/.agents/scripts/lib/orchestration/pr-base-guard.js +0 -37
  225. package/.agents/scripts/lib/orchestration/resolves-token.js +0 -127
  226. package/.agents/scripts/lib/orchestration/spec-section-validator.js +0 -130
  227. package/.agents/scripts/lib/orchestration/story-close/emit-blocked.js +0 -55
  228. package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +0 -211
  229. package/.agents/scripts/lib/planning-corpus.js +0 -37
  230. package/.agents/scripts/lib/qa/coverage-report.js +0 -181
  231. package/.agents/scripts/lib/qa/propose-missing-test.js +0 -95
  232. package/.agents/scripts/lib/qa/qa-context-hydrator.js +0 -217
  233. package/.agents/scripts/lib/signals/detectors/index.js +0 -14
  234. package/.agents/scripts/lib/signals/detectors/retry.js +0 -253
  235. package/.agents/scripts/lib/signals/detectors/rework.js +0 -167
  236. package/.agents/scripts/lib/signals/read.js +0 -268
  237. package/.agents/scripts/lib/signals/span-tree.js +0 -291
  238. package/.agents/scripts/lib/story-lifecycle.js +0 -194
  239. package/.agents/scripts/lib/story-plan.js +0 -379
  240. package/.agents/scripts/lib/util/phase-timer-state.js +0 -72
  241. package/.agents/scripts/lib/util/phase-timer.js +0 -163
  242. package/.agents/scripts/lib/workers/combined-mi-crap-worker.js +0 -169
  243. package/.agents/scripts/lint-baseline.js +0 -507
  244. package/.agents/scripts/providers/github/prs.js +0 -103
  245. package/.agents/scripts/signals-view.js +0 -309
  246. package/.agents/scripts/story-plan.js +0 -370
  247. package/.agents/scripts/sync-branch-from-base.js +0 -149
  248. package/.agents/scripts/validate-docs-freshness.js +0 -314
  249. package/.agents/skills/core/diagnose-friction/SKILL.md +0 -78
  250. package/.agents/workflows/helpers/signals.md +0 -112
@@ -7,10 +7,10 @@ description: Audit the repository's main documentation for staleness, semantic d
7
7
  You are a Staff Engineer & Documentation Steward verifying the repository's prose
8
8
  documentation is **up to date and complete**. Prose rots silently: commands get
9
9
  renamed, scripts move, workflows change shape, version/topology claims go stale.
10
- The deterministic gates (`check-doc-links.js`, `check-lifecycle-doc-drift.js`,
11
- `validate-docs-freshness.js`) catch broken links, generator drift, and
12
- per-delivery freshness they cannot tell whether the prose still describes how
13
- the code actually behaves. That semantic verification is this lens's job. The
10
+ The deterministic gates (`check-doc-links.js` and the generators' `--check`
11
+ mode) catch broken links and generator drift — they cannot tell whether the
12
+ prose still describes how the code actually behaves. That semantic
13
+ verification is this lens's job. The
14
14
  shared lens machinery — read-only constraint, scope interpretation, report
15
15
  envelope + finding-block skeleton, severity scale, self-cross-check, and
16
16
  execution strategy — lives in
@@ -78,7 +78,6 @@ are cheap, exact, and de-duplicate the easy findings:
78
78
 
79
79
  ```bash
80
80
  node .agents/scripts/check-doc-links.js
81
- node .agents/scripts/check-lifecycle-doc-drift.js
82
81
  node .agents/scripts/generate-config-docs.js --check
83
82
  node .agents/scripts/generate-lifecycle-docs.js --check
84
83
  node .agents/scripts/generate-workflows-doc.js --check
@@ -87,7 +86,7 @@ node .agents/scripts/resolve-doc-tiers.js --json
87
86
 
88
87
  Fold the results in as findings:
89
88
 
90
- - **Checker failures** (broken links, lifecycle drift) become individual
89
+ - **Checker failures** (broken links, generator drift) become individual
91
90
  findings with `Category: Link Integrity` (or `Generator Drift` for the
92
91
  lifecycle gate), citing the checker output verbatim.
93
92
  - **Generator dirtiness** (any `--check` reporting stale output, including
@@ -227,8 +227,8 @@ runs FIRST and widens the net across open + closed issues; the exact
227
227
  was reworded but whose *location* is unchanged still confirms against the Issue
228
228
  that already tracks that location, because the audit filers stamp a
229
229
  location-based `audit-semantic-keys` footer alongside the `audit-fingerprints`
230
- footer. Close-time filings from the
231
- [`audit-results-graduator`](../scripts/lib/feedback-loop/audit-results-graduator.js)
230
+ footer. Filings from the
231
+ [`retro-proposals-graduator`](../scripts/lib/feedback-loop/retro-proposals-graduator.js)
232
232
  carry the same canonical `audit-fingerprints` footer, so a sweep recognizes a
233
233
  graduator-filed issue and never re-files it.
234
234
 
@@ -229,18 +229,17 @@ which path produced it.
229
229
  > stage before synthesising the report. It derives its per-dimension prompts
230
230
  > from the *lens* markdown at run time — the lens stays the single source of
231
231
  > truth. This is a performance optimization over path 1, **not** a separate
232
- > contract: strategy selection lives in
233
- > [`../../scripts/lib/dynamic-workflow/capability.js`](../../scripts/lib/dynamic-workflow/capability.js)
234
- > (`selectAuditStrategy`), and it is not covered by the No-Shim / hard-cutover
235
- > rule in [`../../rules/git-conventions.md`](../../rules/git-conventions.md)
236
- > because there is one report contract and only the execution strategy varies —
237
- > the same capability-degradation pattern the protocol endorses for live-docs
238
- > fallback. Force a path for testing with `MANDREL_AUDIT_STRATEGY=sequential`
239
- > or `MANDREL_AUDIT_STRATEGY=orchestrated`; exercise the real disable signals
240
- > with `CLAUDE_CODE_DISABLE_WORKFLOWS=1` or `disableWorkflows: true` in
241
- > `.claude/settings.json`. On the orchestrated path the analysis subagents are
242
- > granted only read/search tools (`Read`, `Grep`, `Glob`) — the single write is
243
- > the final report artifact.
232
+ > contract, and it is not covered by the No-Shim / hard-cutover rule in
233
+ > [`../../rules/git-conventions.md`](../../rules/git-conventions.md) because
234
+ > there is one report contract and only the execution strategy varies — the
235
+ > same capability-degradation pattern the protocol endorses for live-docs
236
+ > fallback. **The host owns the choice.** Mandrel ships no in-repo strategy
237
+ > selector and no force-override env var: Claude Code launches the saved
238
+ > workflow when it can, and you get path 1 or 2 above when it cannot.
239
+ > Suppress the orchestrated path with `CLAUDE_CODE_DISABLE_WORKFLOWS=1`
240
+ > or `disableWorkflows: true` in `.claude/settings.json`. On the orchestrated
241
+ > path the analysis subagents are granted only read/search tools (`Read`,
242
+ > `Grep`, `Glob`) — the single write is the final report artifact.
244
243
 
245
244
  ## Parallel tooling {#parallel-tooling}
246
245
 
@@ -95,17 +95,18 @@ suppress the noise.
95
95
 
96
96
  ## When a number changes
97
97
 
98
- Update three places **in the same commit**:
99
-
100
- 1. `delivery.quality.codingGuardrails.<key>` in
101
- [`agentrc-reference.json`](../../docs/agentrc-reference.json).
102
- 2. The matching schema bound in
103
- [`schemas/agentrc.schema.json`](../../schemas/agentrc.schema.json) and the
104
- AJV mirror in
105
- [`scripts/lib/config-settings-schema.js`](../../scripts/lib/config-settings-schema.js).
106
- 3. The threshold cell or sentence in this helper.
107
-
108
- The drift test (`tests/config-schema-mirror-drift.test.js`) catches schema
109
- divergence between the JSON mirror and the AJV runtime; the helper-prose
110
- drift is caught by `audit-clean-code` and `agent-protocol` linking back here
111
- rather than restating the numbers.
98
+ Update two places **in the same commit**:
99
+
100
+ 1. `CODING_GUARDRAILS_DEFAULTS` in
101
+ [`scripts/lib/config/quality.js`](../../scripts/lib/config/quality.js).
102
+ The schema literal in
103
+ [`config-settings-schema-quality.js`](../../scripts/lib/config-settings-schema-quality.js)
104
+ imports it, and `npm run docs:gen` propagates the number into
105
+ [`agentrc-reference.json`](../../docs/agentrc-reference.json), the shipped
106
+ JSON-Schema mirror, and the `configuration.md` key table.
107
+ 2. The threshold cell or sentence in this helper.
108
+
109
+ The generator-fidelity test (`tests/config-schema-mirror-drift.test.js`)
110
+ catches a stale generated artifact; the helper-prose drift is caught by
111
+ `audit-clean-code` and `agent-protocol` linking back here rather than
112
+ restating the numbers.
@@ -293,14 +293,9 @@ For every finding, provide:
293
293
 
294
294
  Findings that Step 4.5 remediated on `[HEAD_REF]` MUST be rendered under a
295
295
  dedicated **`## Fixed on-branch`** heading, **not** in the severity groups
296
- above. This is the contract seam that keeps remediated findings from
297
- spawning ghost follow-up issues: the
298
- [audit-results graduator](../../scripts/lib/feedback-loop/audit-results-graduator.js)
299
- (the sole canonical reader of the unified comment)
300
- skips every entry inside this section (both because a fixed entry is
301
- rendered with a **✅ prefix** — so it carries no leading severity emoji the
302
- parser would match — and because the parser has an explicit
303
- Fixed-on-branch section guard).
296
+ above. This keeps a remediated finding legible as remediated to every reader
297
+ of the unified comment — human or downstream — rather than reading as an
298
+ outstanding severity-grouped finding.
304
299
 
305
300
  Render each fixed finding as a `✅`-prefixed line naming its original
306
301
  severity, the file path in backticks, and the remediating commit SHA, e.g.:
@@ -118,7 +118,8 @@ engine runs, never what runs — gates, PR, and terminal envelope are identical.
118
118
  **Read the mode; never infer it from shape.** Before spawning
119
119
  anything, read the Story's `dispatchMode` from the resolver envelope
120
120
  (`stories[].dispatchMode`, produced by `resolveStoryDispatchMode` in
121
- `lib/orchestration/complexity-gate.js`). A Story with `dispatchMode: "inline"`
121
+ `lib/orchestration/complexity-gate.js`, which decides on the resolved set size
122
+ alone — it does not read the Story body). A Story with `dispatchMode: "inline"`
122
123
  executes [`deliver-story.md`](deliver-story.md) **inline in this session** — no
123
124
  `story-worker` sub-agent boot and no fresh acceptance-critic sub-agents
124
125
  (sub-agent boots are the dominant deliver-phase token cost at trivial scope) —
@@ -139,19 +139,18 @@ and the `rules/security-baseline.md` MUSTs all run exactly as for a
139
139
  full-ceremony Story. The lite route's `preserves` field is the machine-readable
140
140
  record of those non-negotiables; there is no lite-specific gate bypass.
141
141
 
142
- **Deliver derives the route from the Story body's shape and the
143
- dispatch mode from the run.** Persist stamps a lite cohort's Stories with the
144
- `route::lite` label as a _human-visible hint only_ (and ledgers the authored
145
- verdict — recorded reason plus per-Story shape evidence — on the
146
- `story-plan-state` checkpoint); the label is never the control signal.
147
- `/deliver` computes the route from the fetched Story body via
148
- `resolveStoryDispatchMode` (`lib/orchestration/complexity-gate.js`) the same
149
- shape taxonomy `deriveChangeLevel` applies to the landed diff at close:
150
- `changes[]` count, acceptance count, creates-vs-refactors mix, sensitive-path
151
- classes. A footprint intersecting a sensitive-path class derives `full` —
152
- sensitivity wins, and the Story keeps its fresh acceptance critic.
153
-
154
- That derived route sets ceremony. It does **not** set the dispatch mode,
142
+ **Ceremony comes from the landed diff; the dispatch mode comes from the
143
+ run.** Persist stamps a lite cohort's Stories with the `route::lite` label as a
144
+ _human-visible hint only_ (and ledgers the authored verdict — recorded reason
145
+ plus per-Story shape evidence — on the `story-plan-state` checkpoint); the
146
+ label is never the control signal. Ceremony is resolved from the **derived
147
+ change level** (`deriveChangeLevel` over the computed change set digest § 3),
148
+ not from a body-shape read: a footprint intersecting a sensitive-path class
149
+ derives `high`, so the Story keeps its fresh acceptance critic. The light path
150
+ is the one caller that reads the authored body's shape, through
151
+ `deriveStoryShape` (`lib/orchestration/complexity-gate.js`).
152
+
153
+ That derived level sets ceremony. It does **not** set the dispatch mode,
155
154
  because `inline` names one indivisible resource — the router's own session —
156
155
  and only run topology can say whether it is free: a **single-Story run**
157
156
  executes inline, and every Story of a multi-Story run dispatches as a
@@ -200,8 +199,8 @@ critic (the redundant pre-pass buys no measurable quality and roughly
200
199
  triples the acceptance-block cost). `acceptance-eval.js` is the
201
200
  deterministic **scorer** of that one authored verdict — schema validation,
202
201
  round cap, proceed / redraft / block — not an independent additional pass
203
- over the criteria. The M4-B floor holds: one verdict per cluster, the
204
- cluster count owned by `acceptance-clusters.js` alone.
202
+ over the criteria. The M4-B floor holds: one verdict per cluster, with the
203
+ cluster count owned by the dispatching caller and never by routing.
205
204
 
206
205
  **One round = N cluster critics → ONE merged verdict → ONE gate call.** The
207
206
  clusters are how a round is _authored_; they are not how it is _scored_.
@@ -279,7 +278,9 @@ floor forces `fresh`). Review depth reads the same derived level via
279
278
  `review-depth.js` inside close, so the two decisions cannot disagree.
280
279
 
281
280
  **Inline-dispatch override.** When the Story dispatches
282
- `inline` (`resolveStoryDispatchMode` → `inline`, i.e. a single-Story run), run
281
+ `inline` (`resolveStoryDispatchMode` → `inline`, which is exactly a
282
+ single-Story run — the function reads the resolved set size and nothing
283
+ else), run
283
284
  every acceptance critic **inline** — do not spawn fresh-context critic
284
285
  sub-agents regardless of what the profile would otherwise resolve. The self-eval rigor
285
286
  (scoring each `acceptance[]` item against the one computed change set, with
@@ -393,6 +394,16 @@ judgment that help text cannot carry.
393
394
  wants the PR left at `agent::closing` for a human land (or a wrapper that
394
395
  will invoke `single-story-confirm-merge.js` itself). Reports `pending` —
395
396
  the work is not done, nothing is broken, and one named command finishes it.
397
+ - `--override-review-block "<reason>"` — when the Story-scope review's
398
+ **critical** blocker is one you have read and judged wrong (a false positive,
399
+ or a finding the ratchet correctly exempts). It is the only sanctioned way
400
+ past that halt: reach for it instead of merging the PR by hand, because a
401
+ hand-merge bypasses the gate and records nothing. The reason is mandatory and
402
+ is written to three places (Story comment, PR comment, a
403
+ `review-block-overridden` friction signal), and the terminal envelope reports
404
+ `gates.codeReview: "overridden"` rather than `"passed"`. If you find yourself
405
+ reaching for it twice for the same shape of finding, the gate is
406
+ miscalibrated — fix the gate, not the run.
396
407
  - `--max-wait-seconds <n>` — from a headless caller with no host
397
408
  tool-invocation ceiling, to keep single-block semantics
398
409
  without editing the consumer's config.
@@ -116,8 +116,7 @@ PowerShell `Get-CimInstance Win32_Process`, terminating them with
116
116
  - The close output reports `pending-cleanup persistent-lock: story-N, ...`.
117
117
  - `git worktree list` shows `.worktrees/story-N/` for a closed Story.
118
118
  - `npm run lint` fails because of a nested `biome.json` in a half-reaped
119
- worktree. The `worktree-residue-biome` self-healing check detects this
120
- failure mode.
119
+ worktree.
121
120
 
122
121
  ### Manual usage
123
122
 
@@ -150,22 +150,22 @@ in Step 5). Full procedure:
150
150
  node .agents/scripts/apply-quality-bootstrap.js
151
151
  ```
152
152
 
153
- Runs the same idempotent installs the quality-gates phase of
154
- [`bootstrap.js`](../scripts/bootstrap.js) uses — `applyQualityBootstrap`
155
- then `migrateBaselinesLayout` — and prints a `{ quality, baselines }` JSON
156
- envelope. The four quality-bootstrap outcomes: **helper** (materialize
153
+ Runs the same idempotent install the quality-gates phase of
154
+ [`bootstrap.js`](../scripts/bootstrap.js) uses — `applyQualityBootstrap`
155
+ and prints a `{ quality }` JSON envelope. The five quality-bootstrap
156
+ outcomes: **helper** (materialize
157
157
  [`code-quality-guardrails.md`](helpers/code-quality-guardrails.md)),
158
158
  **hook** (install the `.husky/pre-commit` diff-scoped `quality:preview`
159
159
  invocation — a pre-existing **custom hook is never overwritten silently**;
160
160
  the action is `custom-hook-skip` and the helper returns the snippet to
161
161
  append by hand), **scripts** (backfill `quality:preview` /
162
162
  `quality:watch` only when absent), **config** (seed missing
163
- `delivery.quality.*` defaults — operator overrides survive). The baselines
164
- step migrates legacy per-Epic snapshot layouts into the ephemeral
165
- `temp/epic/<id>/baselines/` namespace when upgrading from pre-v2 shapes; the
166
- main-tracked root baselines are never touched. A second run reports
167
- `no-change` on every path — the idempotence contract this workflow
168
- requires.
163
+ `delivery.quality.*` defaults — operator overrides survive), and
164
+ **legacyBaselines** (`git rm` a committed pre-v2 `baselines/epic/` tree
165
+ the Story-only v2 model retired every reader of those per-Epic snapshots;
166
+ the main-tracked root baselines are never touched). A second run reports
167
+ `no-change` / `already-present` / `absent` on every path — the idempotence
168
+ contract this workflow requires.
169
169
 
170
170
  ## Step 3.6 — Refresh the harness permission allowlist
171
171
 
@@ -172,27 +172,21 @@ every decision to the shared helpers; never re-derive them in prose.
172
172
  surfaces it touches, the **options**, and a brief **recommendation** with
173
173
  trade-offs. Still pin the relevant `file:line` anchor(s) where the change
174
174
  would land.
175
- 3. **Hydrate the QA context** to locate code precisely, via
176
- [`qa-context-hydrator.js`](../scripts/lib/qa/qa-context-hydrator.js) it
177
- resolves the source ticket body, the feature-file set, the surface map, and
178
- recent git log:
179
-
180
- ```js
181
- import { hydrateQaContext } from '../scripts/lib/qa/qa-context-hydrator.js';
182
- const context = await hydrateQaContext({ ticketNumber, githubPort, gitPort, surfaceMap });
183
- ```
184
-
175
+ 3. **Locate the code.** Read the source ticket with
176
+ `gh issue view <ticketNumber> --json title,body,labels`, and verify each
177
+ surface-map path resolves with `git cat-file -e HEAD:<path>` flag every
178
+ miss rather than citing a path that does not exist.
185
179
  4. **Compute the coverage verdict** for the surface the observation points at,
186
180
  via [`coverage-verdict.js`](../scripts/lib/qa/coverage-verdict.js) — the
187
181
  deterministic seam behind the
188
182
  [`core/qa-coverage-mapping`](../skills/core/qa-coverage-mapping/SKILL.md)
189
183
  skill. Read that skill for how to assemble the `surface` input and read the
190
- per-tier `{present|absent}` verdict. Optionally render a human-readable
191
- summary via [`coverage-report.js`](../scripts/lib/qa/coverage-report.js).
192
- 5. **Propose the missing test** (if any) from that verdict, via
193
- [`propose-missing-test.js`](../scripts/lib/qa/propose-missing-test.js) it
194
- names the lowest absent tier, or returns `null` when every tier is covered.
195
- Record its `description` as the ledger item's `missingTest`.
184
+ per-tier `{present|absent}` verdict.
185
+ 5. **Name the missing test** (if any) from that verdict: take the lowest tier
186
+ the verdict marks `absent` (unit → contract → acceptance) and write one
187
+ concrete sentence describing the test that would close it. Every tier
188
+ `present` means no missing test. Record that sentence as the ledger item's
189
+ `missingTest`.
196
190
  6. **Classify** the finding via
197
191
  [`classify-finding.js`](../scripts/lib/findings/classify-finding.js) so the
198
192
  tentative `class` resolves to the correct focus/meta label set. The helper
@@ -273,10 +267,11 @@ the `/qa-assist`-specific deltas are:
273
267
  - **Persistent, resumable rolling session** — `/qa-assist` defaults to resuming
274
268
  the same session and appending; a reused session carries the untriaged backlog
275
269
  forward and never overwrites a prior ledger.
276
- - **Enrichment helpers are deterministic** — context hydration
277
- ([`qa-context-hydrator.js`](../scripts/lib/qa/qa-context-hydrator.js)),
278
- coverage verdict/report, missing-test, and classification are never re-derived
279
- in prose.
270
+ - **Enrichment delegates where a helper exists** — the coverage verdict and
271
+ the finding classification come from their deterministic helpers, never from
272
+ prose. Context lookup and the missing-test sentence are the model's own work:
273
+ they are judgments, not computations, and routing them through a module only
274
+ bought a round-trip.
280
275
 
281
276
  ## See also
282
277
 
@@ -176,10 +176,11 @@ For each observation the agent makes while driving:
176
176
  [`core/qa-coverage-mapping`](../skills/core/qa-coverage-mapping/SKILL.md)
177
177
  skill. Read that skill for how to assemble the `surface` input and read the
178
178
  per-tier `{present|absent}` verdict.
179
- 3. **Propose the missing test** (if any) from that verdict, via
180
- [`propose-missing-test.js`](../scripts/lib/qa/propose-missing-test.js) it
181
- names the lowest absent tier, or returns `null` when every tier is covered.
182
- Record its `description` as the ledger item's `missingTest` (or `null`).
179
+ 3. **Name the missing test** (if any) from that verdict: take the lowest tier
180
+ the verdict marks `absent` (unit → contract → acceptance) and write one
181
+ concrete sentence describing the test that would close it. Every tier
182
+ `present` means no missing test. Record that sentence as the ledger item's
183
+ `missingTest` (or `null`).
183
184
  4. **Append a `QaLedgerItem`** to the ledger (shape per
184
185
  [`helpers/qa-core.md`](helpers/qa-core.md)): a stable `id`, the redacted
185
186
  `evidence`, the `coverage` label (the `surface`, or `unknown`), a tentative
@@ -230,10 +231,10 @@ the `/qa-explore`-specific deltas are:
230
231
  and fall back to static.
231
232
  - **Broken navigation is a finding, not a workaround** — never URL-jump around a
232
233
  missing affordance, a nav 404, or a guard redirect loop.
233
- - **Delegate coverage decisions to the helpers.** Coverage verdict
234
- ([`coverage-verdict.js`](../scripts/lib/qa/coverage-verdict.js)) and
235
- missing-test ([`propose-missing-test.js`](../scripts/lib/qa/propose-missing-test.js))
236
- are deterministic never re-derive them in prose.
234
+ - **Delegate the coverage verdict to the helper.** Tier placement comes from
235
+ [`coverage-verdict.js`](../scripts/lib/qa/coverage-verdict.js) — deterministic,
236
+ never re-derived in prose. The missing-test sentence is yours to write from
237
+ that verdict's lowest `absent` tier.
237
238
 
238
239
  ## See also
239
240
 
package/README.md CHANGED
@@ -87,7 +87,7 @@ time to confirm the install is healthy.
87
87
  > Prefer a surgical alternative? Replace `shamefully-hoist` with a scoped
88
88
  > `public-hoist-pattern[]=` line per package listed in
89
89
  > `.agents/runtime-deps.json` (`ajv`, `ajv-formats`, `js-yaml`, `minimatch`,
90
- > `picomatch`, `string-argv`, `typhonjs-escomplex`). If `mandrel doctor`
90
+ > `picomatch`, `typhonjs-escomplex`). If `mandrel doctor`
91
91
  > reports `runtime-deps missing: …`, this is the fix.
92
92
 
93
93
  `bootstrap.js` is interactive on a TTY and auto-accepts the
package/docs/CHANGELOG.md CHANGED
@@ -15,6 +15,48 @@ 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.32.0](https://github.com/dsj1984/mandrel/compare/mandrel-v2.31.0...mandrel-v2.32.0) (2026-08-06)
19
+
20
+
21
+ ### ⚠ BREAKING CHANGES
22
+
23
+ * `.agents/scripts/validate-docs-freshness.js` is removed from the published payload. It had no invoker in `package.json` scripts, Husky hooks, CI workflows, or any workflow markdown, so no shipped flow loses a step. A consumer calling it by hand should drop the call; the `delivery.docsFreshness.paths` key it read is unchanged and is still consumed by the audit-documentation lens.
24
+ * `project.commands.lintBaseline` is removed from the `.agentrc.json` schema. The `project.commands` block is `additionalProperties: false`, so a config still carrying the key now fails validation instead of being ignored. `npx mandrel update` deletes it for you; delete it by hand otherwise. Nothing replaces it — the framework no longer ships a lint-baseline capture CLI, and a consumer that wants the `lint` baseline kind writes `baselines/lint.json` from its own linter.
25
+ * `ITicketingProvider` no longer declares `getRecentComments`, `addSubIssue`, `removeSubIssue` or `createPullRequest`, and `GitHubProvider` no longer installs them. A consumer implementing or calling those members must drop them: open pull requests with `gh pr create` (as `single-story-close.js` does) and read a ticket's comments through `getTicketComments`.
26
+ * `apply-quality-bootstrap.js` no longer prints a `baselines` key. Its stdout envelope is now `{ quality }`, and the retired baselines migration is reported as `quality.legacyBaselines` instead. A consumer parsing `.baselines.action` from `/mandrel-update` Step 3.5 must read `.quality.legacyBaselines.action` (`absent` | `pruned`); the pre-v2 snapshot relocation into `temp/epic/<id>/baselines/` no longer happens at all.
27
+ * the delivery.lease config block (delivery.lease.ttlMs) is removed from .agentrc.json. The schema now rejects it as an unknown key, so a consumer carrying it must delete the block. Nothing replaces it: the lease has no TTL, and a stranded claim is cleared with --steal.
28
+ * `delivery.acceptanceEval.clusterCeiling` is removed from the agentrc schema. The block is `additionalProperties: false`, so a consumer `.agentrc.json` still carrying the key now fails AJV validation — delete the key; nothing read it.
29
+
30
+ ### Added
31
+
32
+ * baseline honesty surface: framework-owned scope/staleness gate + measurement-free orphan pruner ([#5012](https://github.com/dsj1984/mandrel/issues/5012)) ([#5016](https://github.com/dsj1984/mandrel/issues/5016)) ([71f7134](https://github.com/dsj1984/mandrel/commit/71f713419bba8523ab6bbca60fb962d5919f18b1))
33
+ * **baselines:** baseline-refresh: schedule the full-scope re-score ([#5028](https://github.com/dsj1984/mandrel/issues/5028)) ([eee9005](https://github.com/dsj1984/mandrel/commit/eee9005933a572f69ac3faa94bfc1d7193dfd7d4))
34
+ * **gates:** make the dead-exports ratchet detect whole-file death (refs [#5001](https://github.com/dsj1984/mandrel/issues/5001)) ([#5014](https://github.com/dsj1984/mandrel/issues/5014)) ([38dfc3c](https://github.com/dsj1984/mandrel/commit/38dfc3c690c14c80b0fd2dc97dd7cbd599a19322))
35
+ * one child-process exec wrapper: single maxBuffer SSOT, normalized errors, lint-banned raw spawns ([#5009](https://github.com/dsj1984/mandrel/issues/5009)) ([#5029](https://github.com/dsj1984/mandrel/issues/5029)) ([956dcfc](https://github.com/dsj1984/mandrel/commit/956dcfc777fe74b2c35e208ee1a44ee7a47a3fd1))
36
+ * planning surface diet: delete the parallel authoring pipeline and stop rejecting output the validator can already fix ([#5005](https://github.com/dsj1984/mandrel/issues/5005)) ([#5020](https://github.com/dsj1984/mandrel/issues/5020)) ([a62efd5](https://github.com/dsj1984/mandrel/commit/a62efd5d27f9d82761f150a5edd0388eb0370493))
37
+ * **quality:** derive knip's CLI entry list instead of trusting memory ([#5026](https://github.com/dsj1984/mandrel/issues/5026)) ([7554851](https://github.com/dsj1984/mandrel/commit/7554851979de86622baf58f2467b5198d3202ed9))
38
+ * ship a fail-closed dead-exports baseline producer so the ratchet has an updater, not a hand-edit ([#5011](https://github.com/dsj1984/mandrel/issues/5011)) ([#5033](https://github.com/dsj1984/mandrel/issues/5033)) ([eede007](https://github.com/dsj1984/mandrel/commit/eede007c502fab485de480c2517516f8e015491d))
39
+ * ship a static gherkin corpus gate: must-compile with the real parser, must-bind scoped per step root ([#5013](https://github.com/dsj1984/mandrel/issues/5013)) ([#5034](https://github.com/dsj1984/mandrel/issues/5034)) ([9f4e83e](https://github.com/dsj1984/mandrel/commit/9f4e83ede2c7a55a6dd94e2d8b162e54fb4de217))
40
+
41
+
42
+ ### Fixed
43
+
44
+ * close six defects found reviewing the v2.32.0 release diff ([#5035](https://github.com/dsj1984/mandrel/issues/5035)) ([625004c](https://github.com/dsj1984/mandrel/commit/625004ccc65a65508ef99b239ddd09fafb13c56f))
45
+ * **close:** review lens must honour maintainability ignoreGlobs; add a logged override ([#5030](https://github.com/dsj1984/mandrel/issues/5030)) ([e21ff82](https://github.com/dsj1984/mandrel/commit/e21ff82b8ee80c80a9d3ff7803247c992f0a3abc))
46
+ * derive the squash subject from release impact, keep acronyms, propagate breaking changes ([#5027](https://github.com/dsj1984/mandrel/issues/5027)) ([a7bb9af](https://github.com/dsj1984/mandrel/commit/a7bb9af0353853c60ac6a2b59ea04b7c66695dbc))
47
+
48
+
49
+ ### Changed
50
+
51
+ * cRAP surface diet: delete the dead combined scan, unify the baseline read path, stop the c≈5 cap on untestable CLI wiring ([#5002](https://github.com/dsj1984/mandrel/issues/5002)) ([#5017](https://github.com/dsj1984/mandrel/issues/5017)) ([7624c12](https://github.com/dsj1984/mandrel/commit/7624c125f974cce0820d494b63efad6e56b9beae))
52
+ * delivery/orchestration sweep: retire Epic-era close residue and collapse self-documented-unreachable machinery ([#5006](https://github.com/dsj1984/mandrel/issues/5006)) ([#5021](https://github.com/dsj1984/mandrel/issues/5021)) ([7c19dc5](https://github.com/dsj1984/mandrel/commit/7c19dc5d449f4a9926a7b59c9c9f0dbadf92e091))
53
+ * **orchestration:** delete production-dead story-lifecycle module ([#5025](https://github.com/dsj1984/mandrel/issues/5025)) ([811966c](https://github.com/dsj1984/mandrel/commit/811966c0e1d0f2a746261e342f64acfe1b6f6bd5))
54
+ * providers/QA/misc sweep: prune Epic-era provider surface, dead subsystems, and QA round-trip ceremony ([#5008](https://github.com/dsj1984/mandrel/issues/5008)) ([#5023](https://github.com/dsj1984/mandrel/issues/5023)) ([a019c14](https://github.com/dsj1984/mandrel/commit/a019c144149b23b7102fcd39e466842a3880cfa2))
55
+ * remove the write-only observability surface: tool-trace hooks, dead signals viewer, Epic-era graduator ([#5003](https://github.com/dsj1984/mandrel/issues/5003)) ([#5018](https://github.com/dsj1984/mandrel/issues/5018)) ([3a6273f](https://github.com/dsj1984/mandrel/commit/3a6273f3dc78796ac89a62c1fefa0d95200bac8e))
56
+ * retire the orphaned lifecycle-bus stratum: bus, both observers, the emitter-less schemas, and the doc-drift guard behind them ([#5024](https://github.com/dsj1984/mandrel/issues/5024)) ([#5032](https://github.com/dsj1984/mandrel/issues/5032)) ([caed250](https://github.com/dsj1984/mandrel/commit/caed2501520ce292832b99f12a5478350954c8b9))
57
+ * rules refresh: retire the phantom docs-freshness gate, demote §1.H, condense testing pedagogy ([#5010](https://github.com/dsj1984/mandrel/issues/5010)) ([#5031](https://github.com/dsj1984/mandrel/issues/5031)) ([18715c4](https://github.com/dsj1984/mandrel/commit/18715c4a13448b0992ad8b005193414a0fcf493f))
58
+ * single-source the config surface and finish the bootstrap vestige cleanup ([#5007](https://github.com/dsj1984/mandrel/issues/5007)) ([#5022](https://github.com/dsj1984/mandrel/issues/5022)) ([073415e](https://github.com/dsj1984/mandrel/commit/073415ebbefb2b5d4df05e3e52bbd4b11cf73120))
59
+
18
60
  ## [2.31.0](https://github.com/dsj1984/mandrel/compare/mandrel-v2.30.0...mandrel-v2.31.0) (2026-08-05)
19
61
 
20
62
 
@@ -57,6 +57,7 @@ import { retireVerifyConcurrencyCap } from './steps/2.1.0-retire-verify-concurre
57
57
  import { retireEpicAcTags } from './steps/2.2.0-retire-epic-ac-tags.js';
58
58
  import { retireMaxSeedWords } from './steps/2.11.0-retire-max-seed-words.js';
59
59
  import { retireCodebaseSnapshot } from './steps/2.20.0-retire-codebase-snapshot.js';
60
+ import { retireLintBaselineCommand } from './steps/2.32.0-retire-lint-baseline-command.js';
60
61
 
61
62
  /**
62
63
  * Ordered registry of migration steps. MUST stay sorted ascending by
@@ -75,6 +76,7 @@ export const migrations = [
75
76
  retireEpicAcTags,
76
77
  retireMaxSeedWords,
77
78
  retireCodebaseSnapshot,
79
+ retireLintBaselineCommand,
78
80
  ];
79
81
 
80
82
  /**
@@ -0,0 +1,127 @@
1
+ // lib/migrations/steps/2.32.0-retire-lint-baseline-command.js
2
+ /**
3
+ * Story #5004 follow-up — strip the retired `project.commands.lintBaseline`
4
+ * key from a consumer's `.agentrc.json`.
5
+ *
6
+ * #5004 (PR #5019) deleted `lint-baseline.js` and
7
+ * `lib/orchestration/lint-baseline-service.js`: the CLI spawned the configured
8
+ * command and parsed ESLint-shaped JSON, a shape this repo's own `npm run
9
+ * lint` (Biome + markdownlint fan-out) never produced, and nothing invoked the
10
+ * service. The key left the runtime AJV schema, the published mirror,
11
+ * `.agentrc.json` and `agentrc-reference.json` in the same change.
12
+ *
13
+ * `project.commands` carries `additionalProperties: false`, so a consumer
14
+ * whose config still sets `lintBaseline` hits a hard validation failure on
15
+ * upgrade, not a warning. This step strips the key before that check runs —
16
+ * the same contract-cutover pattern as `2.11.0-retire-max-seed-words.js`.
17
+ *
18
+ * It sweeps **both** config surfaces. `config-resolver.js` deep-merges
19
+ * `.agentrc.local.json` over `.agentrc.json` and validates the result, so a
20
+ * key surviving in the operator's overlay fails exactly as a base one would.
21
+ * Sweeping only the base would report "nothing to migrate" and leave that
22
+ * consumer hard-broken with no self-service remedy — re-running
23
+ * `mandrel update` would keep reporting clean — while this commit's
24
+ * `BREAKING CHANGE:` footer promises the upgrade deletes the key for them.
25
+ *
26
+ * The `lint` baseline KIND survives for consumers whose own linter writes
27
+ * `baselines/lint.json`; only the framework-owned capture shell is gone, so
28
+ * nothing here touches the baseline file or the gate config.
29
+ *
30
+ * This step is also the carrier for the release note #5004 never emitted: its
31
+ * commit ships the `BREAKING CHANGE:` footer that the squash subject on `main`
32
+ * (`227e1af4`) lacks, and which release-please therefore could not surface.
33
+ * See `single-story-close/phases/conventional-subject.js` for the close-side
34
+ * fix that stops the next one being lost.
35
+ */
36
+
37
+ import nodeFs from 'node:fs';
38
+ import path from 'node:path';
39
+
40
+ /**
41
+ * Both config surfaces the resolver reads. `.agentrc.local.json` is deep-merged
42
+ * over the base *before* the AJV gate runs
43
+ * (`.agents/scripts/lib/config-resolver.js`), so a `lintBaseline` surviving in
44
+ * the overlay fails validation exactly as one in the base would — and a step
45
+ * that swept only the base would leave that consumer hard-broken with no
46
+ * self-service remedy, since re-running `mandrel update` would keep reporting
47
+ * clean. The overlay is operator-owned and gitignored, which is why it is easy
48
+ * to forget and why the sweep has to name it explicitly.
49
+ */
50
+ const AGENTRC_FILENAMES = Object.freeze([
51
+ '.agentrc.json',
52
+ '.agentrc.local.json',
53
+ ]);
54
+
55
+ /**
56
+ * @param {unknown} ctx
57
+ * @param {string} filename
58
+ * @returns {string}
59
+ */
60
+ function resolveAgentrcPath(ctx, filename) {
61
+ const projectRoot = ctx?.projectRoot ?? process.cwd();
62
+ return path.join(projectRoot, filename);
63
+ }
64
+
65
+ /**
66
+ * @param {unknown} ctx
67
+ * @param {typeof nodeFs} fsImpl
68
+ * @param {string} filename
69
+ * @returns {object | null}
70
+ */
71
+ function readAgentrcConfig(ctx, fsImpl, filename) {
72
+ try {
73
+ const raw = fsImpl.readFileSync(resolveAgentrcPath(ctx, filename), 'utf8');
74
+ return JSON.parse(raw);
75
+ } catch {
76
+ return null;
77
+ }
78
+ }
79
+
80
+ /**
81
+ * @param {object | null} config
82
+ * @returns {boolean}
83
+ */
84
+ function hasRetiredKey(config) {
85
+ const commands = config?.project?.commands;
86
+ return Boolean(commands) && Object.hasOwn(commands, 'lintBaseline');
87
+ }
88
+
89
+ export const retireLintBaselineCommand = {
90
+ version: '2.32.0',
91
+ description:
92
+ 'strip retired project.commands.lintBaseline from .agentrc.json ' +
93
+ '(the framework lint-baseline capture CLI is gone — Story #5004)',
94
+ /**
95
+ * @param {{ projectRoot?: string, fs?: typeof nodeFs }} [ctx]
96
+ * @returns {boolean}
97
+ */
98
+ detect(ctx) {
99
+ const fsImpl = ctx?.fs ?? nodeFs;
100
+ return AGENTRC_FILENAMES.some((filename) =>
101
+ hasRetiredKey(readAgentrcConfig(ctx, fsImpl, filename)),
102
+ );
103
+ },
104
+ /**
105
+ * @param {{ projectRoot?: string, fs?: typeof nodeFs }} [ctx]
106
+ * @returns {void}
107
+ */
108
+ apply(ctx) {
109
+ const fsImpl = ctx?.fs ?? nodeFs;
110
+ for (const filename of AGENTRC_FILENAMES) {
111
+ const config = readAgentrcConfig(ctx, fsImpl, filename);
112
+ // An absent overlay is the common case, not an error.
113
+ if (!config || !hasRetiredKey(config)) continue;
114
+
115
+ delete config.project.commands.lintBaseline;
116
+ // An emptied `commands` block is left in place: unlike
117
+ // `planning.complexityGate`, `project` is a required block and an empty
118
+ // `commands` object is valid against the schema, so pruning it would be
119
+ // a cosmetic edit to a config the consumer owns.
120
+
121
+ fsImpl.writeFileSync(
122
+ resolveAgentrcPath(ctx, filename),
123
+ `${JSON.stringify(config, null, 2)}\n`,
124
+ );
125
+ }
126
+ },
127
+ };