mandrel 2.31.0 → 2.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/README.md +13 -17
- package/.agents/agents/acceptance-critic.md +1 -2
- package/.agents/docs/SDLC.md +12 -9
- package/.agents/docs/agentrc-reference.json +63 -58
- package/.agents/docs/configuration.md +275 -227
- package/.agents/docs/execution-reference.md +13 -14
- package/.agents/docs/quality-gates.md +195 -23
- package/.agents/instructions.md +2 -5
- package/.agents/rules/git-conventions-reference.md +27 -27
- package/.agents/rules/git-conventions.md +4 -2
- package/.agents/rules/known-tooling-behavior.md +66 -30
- package/.agents/rules/testing-standards.md +35 -71
- package/.agents/runtime-deps.json +2 -2
- package/.agents/schemas/agentrc.schema.json +1955 -1410
- package/.agents/schemas/lifecycle/README.md +21 -14
- package/.agents/schemas/lifecycle/ledger-record.schema.json +76 -22
- package/.agents/schemas/story-deliver-terminal.schema.json +2 -2
- package/.agents/scripts/README.md +16 -29
- package/.agents/scripts/apply-quality-bootstrap.js +27 -34
- package/.agents/scripts/audit-to-stories.js +160 -41
- package/.agents/scripts/bootstrap.js +28 -26
- package/.agents/scripts/check-baseline-drift.js +73 -13
- package/.agents/scripts/check-baseline-scope.js +362 -0
- package/.agents/scripts/check-dead-exports.js +9 -1
- package/.agents/scripts/check-gherkin-corpus.js +508 -0
- package/.agents/scripts/check-knip-entries.js +159 -0
- package/.agents/scripts/check-lifecycle-lint.js +108 -124
- package/.agents/scripts/check-schema-references.js +1 -1
- package/.agents/scripts/diagnose-friction.js +7 -4
- package/.agents/scripts/generate-config-docs.js +263 -171
- package/.agents/scripts/install-matrix-assert.js +0 -1
- package/.agents/scripts/lib/ITicketingProvider.js +0 -58
- package/.agents/scripts/lib/audit-baselines/staleness.js +6 -6
- package/.agents/scripts/lib/audit-baselines/trend.js +7 -8
- package/.agents/scripts/lib/audit-baselines/weights.js +4 -5
- package/.agents/scripts/lib/audit-suite/checklist-threading.js +1 -1
- package/.agents/scripts/lib/audit-to-stories/build-story-body.js +81 -35
- package/.agents/scripts/lib/audit-to-stories/wire-dependencies.js +185 -0
- package/.agents/scripts/lib/baselines/envelope.js +41 -60
- package/.agents/scripts/lib/baselines/git-base.js +30 -37
- package/.agents/scripts/lib/baselines/kinds/_crap-new-method-gate.js +103 -0
- package/.agents/scripts/lib/baselines/kinds/_crap-read.js +150 -0
- package/.agents/scripts/lib/baselines/kinds/crap.js +25 -65
- package/.agents/scripts/lib/baselines/orphan-pruner.js +233 -0
- package/.agents/scripts/lib/baselines/refresh-service.js +6 -8
- package/.agents/scripts/lib/baselines/scope-assert.js +223 -0
- package/.agents/scripts/lib/baselines/scope-inventory.js +314 -0
- package/.agents/scripts/lib/bdd-step-index.js +326 -0
- package/.agents/scripts/lib/bootstrap/install-ledger.js +5 -3
- package/.agents/scripts/lib/bootstrap/issue-forms-template.js +4 -6
- package/.agents/scripts/lib/bootstrap/manifest.js +17 -40
- package/.agents/scripts/lib/bootstrap/project-bootstrap.js +12 -59
- package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +62 -2
- package/.agents/scripts/lib/checks/loop-health.js +9 -37
- package/.agents/scripts/lib/child-exec.js +193 -0
- package/.agents/scripts/lib/cli/standard-args.js +1 -1
- package/.agents/scripts/lib/cli-args.js +64 -0
- package/.agents/scripts/lib/close-validation/gates.js +2 -2
- package/.agents/scripts/lib/close-validation/runner.js +3 -3
- package/.agents/scripts/lib/config/acceptance-eval.js +5 -52
- package/.agents/scripts/lib/config/commands.js +3 -5
- package/.agents/scripts/lib/config/explain.js +5 -7
- package/.agents/scripts/lib/config/gates/bundle-size.schema.js +32 -6
- package/.agents/scripts/lib/config/gates/coverage.schema.js +25 -5
- package/.agents/scripts/lib/config/gates/crap-incremental-coverage.schema.js +12 -2
- package/.agents/scripts/lib/config/gates/crap.schema.js +68 -23
- package/.agents/scripts/lib/config/gates/duplication.schema.js +29 -17
- package/.agents/scripts/lib/config/gates/index.js +5 -2
- package/.agents/scripts/lib/config/gates/lighthouse.schema.js +34 -6
- package/.agents/scripts/lib/config/gates/lint.schema.js +11 -2
- package/.agents/scripts/lib/config/gates/maintainability.schema.js +37 -15
- package/.agents/scripts/lib/config/gates/mutation.schema.js +15 -3
- package/.agents/scripts/lib/config/gates/shared.js +58 -9
- package/.agents/scripts/lib/config/github.js +0 -1
- package/.agents/scripts/lib/config/limits.js +3 -48
- package/.agents/scripts/lib/config/qa.js +105 -0
- package/.agents/scripts/lib/config/runners.js +38 -16
- package/.agents/scripts/lib/config/temp-paths.js +6 -5
- package/.agents/scripts/lib/config-settings-schema-delivery.js +245 -56
- package/.agents/scripts/lib/config-settings-schema-quality.js +209 -29
- package/.agents/scripts/lib/config-settings-schema.js +386 -39
- package/.agents/scripts/lib/crap-baseline-join.js +126 -9
- package/.agents/scripts/lib/crap-utils.js +84 -520
- package/.agents/scripts/lib/dead-exports-knip.js +79 -10
- package/.agents/scripts/lib/degraded-mode.js +2 -2
- package/.agents/scripts/lib/dependency-parser.js +20 -7
- package/.agents/scripts/lib/doc-tiers.js +3 -3
- package/.agents/scripts/lib/feedback-loop/graduator-core.js +46 -104
- package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +10 -8
- package/.agents/scripts/lib/findings/provenance-field.js +135 -0
- package/.agents/scripts/lib/findings/route-finding.js +57 -8
- package/.agents/scripts/lib/fs-walk.js +52 -0
- package/.agents/scripts/lib/git-branch-lifecycle.js +2 -2
- package/.agents/scripts/lib/git-utils.js +16 -36
- package/.agents/scripts/lib/knip-config-resolver.js +181 -0
- package/.agents/scripts/lib/knip-entry-sync.js +508 -0
- package/.agents/scripts/lib/observability/metrics-ledger.js +1 -1
- package/.agents/scripts/lib/observability/runtime-friction.js +10 -0
- package/.agents/scripts/lib/observability/signal-validator.js +5 -85
- package/.agents/scripts/lib/observability/signals-writer.js +19 -62
- package/.agents/scripts/lib/observability/source-classifier.js +5 -7
- package/.agents/scripts/lib/observability/terse-result.js +3 -3
- package/.agents/scripts/lib/orchestration/behind-recovery.js +114 -0
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +7 -8
- package/.agents/scripts/lib/orchestration/ci-rerun-guard.js +34 -33
- package/.agents/scripts/lib/orchestration/code-review.js +2 -2
- package/.agents/scripts/lib/orchestration/complexity-gate.js +43 -161
- package/.agents/scripts/lib/orchestration/diff-magnitude.js +4 -4
- package/.agents/scripts/lib/orchestration/label-transitions.js +3 -2
- package/.agents/scripts/lib/orchestration/lease-guard-shared.js +12 -38
- package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +5 -6
- package/.agents/scripts/lib/orchestration/plan-metrics.js +2 -3
- package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +7 -26
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +69 -5
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +69 -13
- package/.agents/scripts/lib/orchestration/plan-persist/summary.js +49 -0
- package/.agents/scripts/lib/orchestration/{lifecycle/listeners/watcher.js → pr-watch.js} +58 -208
- package/.agents/scripts/lib/orchestration/resolve-stories.js +77 -50
- package/.agents/scripts/lib/orchestration/review-providers/codex.js +1 -1
- package/.agents/scripts/lib/orchestration/review-providers/mi-exemptions.js +130 -0
- package/.agents/scripts/lib/orchestration/review-providers/native.js +30 -16
- package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +37 -26
- package/.agents/scripts/lib/orchestration/single-story-close/phases/conventional-subject.js +376 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/normalize-pr-title.js +161 -151
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +15 -3
- package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +10 -15
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +5 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-override.js +157 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +0 -14
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +59 -25
- package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +20 -31
- package/.agents/scripts/lib/orchestration/spec-spill.js +17 -3
- package/.agents/scripts/lib/orchestration/story-close/phases/review-core.js +7 -6
- package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +2 -1
- package/.agents/scripts/lib/orchestration/task-body-validator.js +4 -1
- package/.agents/scripts/lib/orchestration/ticket-lease.js +28 -127
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +116 -1
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +1 -1
- package/.agents/scripts/lib/orchestration/ticket-validator.js +38 -0
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +5 -5
- package/.agents/scripts/lib/orchestration/ticketing/transition.js +5 -4
- package/.agents/scripts/lib/orchestration/verify-tier-repair.js +107 -0
- package/.agents/scripts/lib/qa/coverage-verdict.js +5 -87
- package/.agents/scripts/lib/signals/detectors/common.js +1 -1
- package/.agents/scripts/lib/signals/index.js +8 -6
- package/.agents/scripts/lib/signals/schema.js +20 -25
- package/.agents/scripts/lib/signals/write.js +8 -8
- package/.agents/scripts/lib/story-body/footer-block.js +97 -0
- package/.agents/scripts/lib/story-body/story-body.js +18 -81
- package/.agents/scripts/lib/temp-retention.js +1 -1
- package/.agents/scripts/lib/templates/decomposer-prompts.js +16 -14
- package/.agents/scripts/lib/ticket-body-sections.js +4 -5
- package/.agents/scripts/lib/wave-runner/footprint.js +306 -0
- package/.agents/scripts/lib/wave-runner/ready-set.js +198 -181
- package/.agents/scripts/lib/worktree/lifecycle/merge-reachability.js +13 -45
- package/.agents/scripts/lib/worktree/lifecycle/reap.js +4 -5
- package/.agents/scripts/lib/worktree-manager.js +2 -3
- package/.agents/scripts/lint-label-vocabulary.js +2 -24
- package/.agents/scripts/pr-watch-with-update.js +7 -5
- package/.agents/scripts/providers/github/blocked-by-add.js +25 -10
- package/.agents/scripts/providers/github/cache.js +2 -2
- package/.agents/scripts/providers/github/comments.js +6 -28
- package/.agents/scripts/providers/github/compose.js +0 -15
- package/.agents/scripts/providers/github/errors.js +10 -27
- package/.agents/scripts/providers/github/request-helpers.js +1 -2
- package/.agents/scripts/providers/github/sub-issues.js +10 -218
- package/.agents/scripts/providers/github.js +4 -7
- package/.agents/scripts/prune-baseline-orphans.js +181 -0
- package/.agents/scripts/resolve-stories.js +21 -7
- package/.agents/scripts/run-lint.js +61 -61
- package/.agents/scripts/run-test-profile.js +6 -6
- package/.agents/scripts/run-verify.js +48 -30
- package/.agents/scripts/single-story-close.js +20 -0
- package/.agents/scripts/single-story-init.js +12 -35
- package/.agents/scripts/stories-wave-tick.js +192 -9
- package/.agents/scripts/update-dead-exports-baseline.js +321 -0
- package/.agents/skills/core/gates-and-baselines/SKILL.md +2 -2
- package/.agents/skills/skills.index.json +1 -11
- package/.agents/workflows/audit-documentation.md +5 -6
- package/.agents/workflows/audit-to-stories.md +28 -2
- package/.agents/workflows/helpers/audit-lens-core.md +11 -12
- package/.agents/workflows/helpers/code-quality-guardrails.md +15 -14
- package/.agents/workflows/helpers/code-review.md +3 -8
- package/.agents/workflows/helpers/deliver-reference.md +30 -2
- package/.agents/workflows/helpers/deliver-story-reference.md +84 -16
- package/.agents/workflows/helpers/plan-reference.md +76 -0
- package/.agents/workflows/helpers/worktree-lifecycle.md +1 -2
- package/.agents/workflows/mandrel-update.md +10 -10
- package/.agents/workflows/qa-assist.md +15 -20
- package/.agents/workflows/qa-explore.md +9 -8
- package/README.md +1 -1
- package/docs/CHANGELOG.md +58 -0
- package/lib/migrations/index.js +2 -0
- package/lib/migrations/steps/2.32.0-retire-lint-baseline-command.js +127 -0
- package/package.json +14 -5
- package/.agents/schemas/lifecycle/checkpoint.written.schema.json +0 -13
- package/.agents/schemas/lifecycle/close-validate.end.schema.json +0 -18
- package/.agents/schemas/lifecycle/close-validate.start.schema.json +0 -13
- package/.agents/schemas/lifecycle/code-review.end.schema.json +0 -30
- package/.agents/schemas/lifecycle/code-review.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/intervention.recorded.schema.json +0 -15
- package/.agents/schemas/lifecycle/loop.tick.schema.json +0 -20
- package/.agents/schemas/lifecycle/notification.emitted.schema.json +0 -18
- package/.agents/schemas/lifecycle/pr.created.schema.json +0 -14
- package/.agents/schemas/lifecycle/retro.end.schema.json +0 -16
- package/.agents/schemas/lifecycle/retro.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/story.blocked.schema.json +0 -13
- package/.agents/schemas/lifecycle/story.dispatch.end.schema.json +0 -17
- package/.agents/schemas/lifecycle/story.dispatch.start.schema.json +0 -15
- package/.agents/schemas/lifecycle/story.merged.schema.json +0 -13
- package/.agents/scripts/check-gherkin-placeholders.js +0 -663
- package/.agents/scripts/check-lifecycle-doc-drift.js +0 -411
- package/.agents/scripts/lib/audit-suite/cli.js +0 -64
- package/.agents/scripts/lib/bootstrap/baselines-layout-migration.js +0 -202
- package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +0 -212
- package/.agents/scripts/lib/checks/baseline-drift-main-checkout.js +0 -104
- package/.agents/scripts/lib/checks/push-hook-parity.js +0 -106
- package/.agents/scripts/lib/checks/windows-coverage-noise-floor.js +0 -92
- package/.agents/scripts/lib/checks/worktree-bootstrap-env.js +0 -81
- package/.agents/scripts/lib/checks/worktree-residue-biome.js +0 -55
- package/.agents/scripts/lib/crap-baseline-index.js +0 -46
- package/.agents/scripts/lib/crap-utils-incremental.js +0 -113
- package/.agents/scripts/lib/dynamic-workflow/capability.js +0 -396
- package/.agents/scripts/lib/feedback-loop/audit-results-graduator.js +0 -335
- package/.agents/scripts/lib/mutation/baseline-snapshot.js +0 -239
- package/.agents/scripts/lib/mutation/config-detector.js +0 -119
- package/.agents/scripts/lib/mutation/stryker-runner.js +0 -306
- package/.agents/scripts/lib/mutation/survivor-report.js +0 -160
- package/.agents/scripts/lib/observability/active-story-env.js +0 -170
- package/.agents/scripts/lib/observability/tool-trace-hook.js +0 -456
- package/.agents/scripts/lib/orchestration/acceptance-clusters.js +0 -111
- package/.agents/scripts/lib/orchestration/context-envelope.js +0 -277
- package/.agents/scripts/lib/orchestration/detectors-phase.js +0 -194
- package/.agents/scripts/lib/orchestration/lifecycle/bus.js +0 -309
- package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +0 -181
- package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +0 -229
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +0 -54
- package/.agents/scripts/lib/orchestration/lifecycle/trace-logger.js +0 -344
- package/.agents/scripts/lib/orchestration/lint-baseline-service.js +0 -114
- package/.agents/scripts/lib/orchestration/pr-base-guard.js +0 -37
- package/.agents/scripts/lib/orchestration/resolves-token.js +0 -127
- package/.agents/scripts/lib/orchestration/spec-section-validator.js +0 -130
- package/.agents/scripts/lib/orchestration/story-close/emit-blocked.js +0 -55
- package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +0 -211
- package/.agents/scripts/lib/planning-corpus.js +0 -37
- package/.agents/scripts/lib/qa/coverage-report.js +0 -181
- package/.agents/scripts/lib/qa/propose-missing-test.js +0 -95
- package/.agents/scripts/lib/qa/qa-context-hydrator.js +0 -217
- package/.agents/scripts/lib/signals/detectors/index.js +0 -14
- package/.agents/scripts/lib/signals/detectors/retry.js +0 -253
- package/.agents/scripts/lib/signals/detectors/rework.js +0 -167
- package/.agents/scripts/lib/signals/read.js +0 -268
- package/.agents/scripts/lib/signals/span-tree.js +0 -291
- package/.agents/scripts/lib/story-lifecycle.js +0 -194
- package/.agents/scripts/lib/story-plan.js +0 -379
- package/.agents/scripts/lib/util/phase-timer-state.js +0 -72
- package/.agents/scripts/lib/util/phase-timer.js +0 -163
- package/.agents/scripts/lib/workers/combined-mi-crap-worker.js +0 -169
- package/.agents/scripts/lint-baseline.js +0 -507
- package/.agents/scripts/providers/github/prs.js +0 -103
- package/.agents/scripts/signals-view.js +0 -309
- package/.agents/scripts/story-plan.js +0 -370
- package/.agents/scripts/sync-branch-from-base.js +0 -149
- package/.agents/scripts/validate-docs-freshness.js +0 -314
- package/.agents/skills/core/diagnose-friction/SKILL.md +0 -78
- package/.agents/workflows/helpers/signals.md +0 -112
|
@@ -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
|
-
**
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
|
@@ -162,6 +161,63 @@ terminal envelope are byte-identical either way.
|
|
|
162
161
|
|
|
163
162
|
---
|
|
164
163
|
|
|
164
|
+
## Declared dependency edges — what actually gates dispatch
|
|
165
|
+
|
|
166
|
+
`resolve-stories.js` builds each `dag[].dependsOn` from the **union of two
|
|
167
|
+
declared-edge channels**, and nothing else: the Story body's footer block and
|
|
168
|
+
the issue's native GitHub `blocked_by` relations. Both are read strictly — an
|
|
169
|
+
edge the resolver cannot read is never quietly reported as an edge that does
|
|
170
|
+
not exist.
|
|
171
|
+
|
|
172
|
+
**The body channel is footer-scoped.** Only a `blocked by #N` line standing
|
|
173
|
+
alone inside the `---` footer block declares an edge:
|
|
174
|
+
|
|
175
|
+
```markdown
|
|
176
|
+
## Goal
|
|
177
|
+
|
|
178
|
+
…
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
blocked by #42
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Prose elsewhere in the body declares **nothing**, and this is a deliberate,
|
|
186
|
+
user-visible change from the whole-body scan that preceded it. A sentence
|
|
187
|
+
merely mentioning a blocker — an example, a changelog note, an acceptance
|
|
188
|
+
criterion quoting the phrase — used to mint a real dispatch gate that withheld
|
|
189
|
+
the Story until an unrelated issue closed. `plan-persist` has always
|
|
190
|
+
serialized the canonical footer form, so no machine-authored body is affected;
|
|
191
|
+
only a **hand-written prose edge** stops gating, and the fix is to move it into
|
|
192
|
+
the footer block. The loose spellings never reached the footer grammar either:
|
|
193
|
+
`depends on #N`, `Blocked by: #N`, and `blocked by #N once X lands` all declare
|
|
194
|
+
nothing. One grammar serves both readers — the body parser and the
|
|
195
|
+
dispatch-edge parser share it — so what a Story body round-trips and what gates
|
|
196
|
+
dispatch cannot drift apart.
|
|
197
|
+
|
|
198
|
+
**The native channel fails loud.** The read paginates to exhaustion (a
|
|
199
|
+
first-page read silently truncated a Story's gates at GitHub's 30-item
|
|
200
|
+
default), and **a 404 is not an empty result**. An issue with no dependencies
|
|
201
|
+
answers `200 []`; a 404 is how GitHub also answers a token that cannot see the
|
|
202
|
+
dependencies API, so treating it as "no edges" erased every native edge in the
|
|
203
|
+
run under a mis-scoped token, silently, with a clean exit code. Any non-OK
|
|
204
|
+
read now fails the resolution naming the Story — check the token's scopes
|
|
205
|
+
first. The one degrade that is scoped rather than fatal is a **cross-repo
|
|
206
|
+
edge**: another repository's issue number cannot be matched against this
|
|
207
|
+
repo's same-numbered issue without risking a false match, so that edge is
|
|
208
|
+
dropped with a warning naming the Story, and its siblings resolve normally.
|
|
209
|
+
|
|
210
|
+
**Edges are monotone — retraction is not built.** Both channels only ever
|
|
211
|
+
_add_ a gate for the current resolution. Removing a `blocked by` footer line
|
|
212
|
+
or deleting a native relation makes the edge absent from the **next** resolve,
|
|
213
|
+
but nothing reconciles an edge that a previous run already acted on, and the
|
|
214
|
+
write path never deletes a native relation it did not need. In practice that
|
|
215
|
+
means: re-resolve after editing edges, and treat a stale gate as a body/issue
|
|
216
|
+
edit plus a fresh `resolve-stories.js` run, never as something delivery
|
|
217
|
+
un-declares on your behalf. This is a known limitation, not an oversight.
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
165
221
|
## Step 1 — Implementation detail
|
|
166
222
|
|
|
167
223
|
**Docs context — digest-first.** Read a full doc only when the Story's own
|
|
@@ -200,8 +256,8 @@ critic (the redundant pre-pass buys no measurable quality and roughly
|
|
|
200
256
|
triples the acceptance-block cost). `acceptance-eval.js` is the
|
|
201
257
|
deterministic **scorer** of that one authored verdict — schema validation,
|
|
202
258
|
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
|
|
259
|
+
over the criteria. The M4-B floor holds: one verdict per cluster, with the
|
|
260
|
+
cluster count owned by the dispatching caller and never by routing.
|
|
205
261
|
|
|
206
262
|
**One round = N cluster critics → ONE merged verdict → ONE gate call.** The
|
|
207
263
|
clusters are how a round is _authored_; they are not how it is _scored_.
|
|
@@ -279,7 +335,9 @@ floor forces `fresh`). Review depth reads the same derived level via
|
|
|
279
335
|
`review-depth.js` inside close, so the two decisions cannot disagree.
|
|
280
336
|
|
|
281
337
|
**Inline-dispatch override.** When the Story dispatches
|
|
282
|
-
`inline` (`resolveStoryDispatchMode` → `inline`,
|
|
338
|
+
`inline` (`resolveStoryDispatchMode` → `inline`, which is exactly a
|
|
339
|
+
single-Story run — the function reads the resolved set size and nothing
|
|
340
|
+
else), run
|
|
283
341
|
every acceptance critic **inline** — do not spawn fresh-context critic
|
|
284
342
|
sub-agents regardless of what the profile would otherwise resolve. The self-eval rigor
|
|
285
343
|
(scoring each `acceptance[]` item against the one computed change set, with
|
|
@@ -393,6 +451,16 @@ judgment that help text cannot carry.
|
|
|
393
451
|
wants the PR left at `agent::closing` for a human land (or a wrapper that
|
|
394
452
|
will invoke `single-story-confirm-merge.js` itself). Reports `pending` —
|
|
395
453
|
the work is not done, nothing is broken, and one named command finishes it.
|
|
454
|
+
- `--override-review-block "<reason>"` — when the Story-scope review's
|
|
455
|
+
**critical** blocker is one you have read and judged wrong (a false positive,
|
|
456
|
+
or a finding the ratchet correctly exempts). It is the only sanctioned way
|
|
457
|
+
past that halt: reach for it instead of merging the PR by hand, because a
|
|
458
|
+
hand-merge bypasses the gate and records nothing. The reason is mandatory and
|
|
459
|
+
is written to three places (Story comment, PR comment, a
|
|
460
|
+
`review-block-overridden` friction signal), and the terminal envelope reports
|
|
461
|
+
`gates.codeReview: "overridden"` rather than `"passed"`. If you find yourself
|
|
462
|
+
reaching for it twice for the same shape of finding, the gate is
|
|
463
|
+
miscalibrated — fix the gate, not the run.
|
|
396
464
|
- `--max-wait-seconds <n>` — from a headless caller with no host
|
|
397
465
|
tool-invocation ceiling, to keep single-block semantics
|
|
398
466
|
without editing the consumer's config.
|
|
@@ -243,6 +243,82 @@ as a hard error — so the grounding contract is the author's own targeted reads
|
|
|
243
243
|
plus that gate. There is no pre-computed codebase snapshot to fall back on,
|
|
244
244
|
and no manifest-derived replacement to build.
|
|
245
245
|
|
|
246
|
+
### Per-Story audit provenance (`provenance`)
|
|
247
|
+
|
|
248
|
+
An audit-seeded plan carries dedup identities forward so the next sweep
|
|
249
|
+
recognises what it already planned. The optional top-level `provenance` field
|
|
250
|
+
says **which of them this Story owns**:
|
|
251
|
+
|
|
252
|
+
```jsonc
|
|
253
|
+
{
|
|
254
|
+
"slug": "own-the-seam",
|
|
255
|
+
"provenance": {
|
|
256
|
+
"fingerprints": ["<40-char sha1, one per finding this Story tracks>"],
|
|
257
|
+
"semanticKeys": ["architecture␟lib/owned.js"]
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Both arrays are optional; a malformed entry is a validator rejection, never a
|
|
263
|
+
silent drop — a dropped identity is invisible until the next sweep re-files
|
|
264
|
+
work this plan already tracked.
|
|
265
|
+
|
|
266
|
+
| `provenance` | What persist stamps |
|
|
267
|
+
| --- | --- |
|
|
268
|
+
| Present | **Exactly** the identities listed — siblings' groups never leak in. |
|
|
269
|
+
| Present but empty (`{}`) | Nothing. "Owns no findings" is a real answer. |
|
|
270
|
+
| **Absent** | The **whole seed's** footers (the union) — the recall-safe default. |
|
|
271
|
+
|
|
272
|
+
**The union fallback is load-bearing, not legacy.** Leaving the authoring agent
|
|
273
|
+
to hand-carry provenance out of the seed's HTML comments was measured to fail —
|
|
274
|
+
a remembered step is no step at all — and the mechanical union carry is what
|
|
275
|
+
closed it. Attribution is additive: it sharpens a plan that opts in and changes
|
|
276
|
+
nothing for one that does not. Never remove the fallback to "finish the
|
|
277
|
+
migration".
|
|
278
|
+
|
|
279
|
+
Attribution is what makes the next sweep's dedup answerable rather than
|
|
280
|
+
arbitrary. Under the union every sibling carried every key, so a finding
|
|
281
|
+
confirming against several open Stories could only pick one at random, and a
|
|
282
|
+
key whose owning Story had since **closed** was masked by any open neighbour —
|
|
283
|
+
a genuine regression filed as a routine update. With ownership stamped, the
|
|
284
|
+
issue carrying a finding's own fingerprint decides both the match and its
|
|
285
|
+
state (`lib/findings/route-finding.js`).
|
|
286
|
+
|
|
287
|
+
The audit path authors this mechanically from the per-group footers the seed
|
|
288
|
+
already carries — see [`audit-to-stories`](../audit-to-stories.md). A `--seed`
|
|
289
|
+
or `--tickets` plan has nothing to attribute and omits the field.
|
|
290
|
+
|
|
291
|
+
## Cross-Story conflict analysis at persist
|
|
292
|
+
|
|
293
|
+
The conflict passes run **twice**: once over the raw `stories.json` payload
|
|
294
|
+
(alongside the freshness, file-assumption and sizing gates), and again over the
|
|
295
|
+
**assembled, footer-stamped bodies** — the artifact persist actually posts.
|
|
296
|
+
The second pass is not belt-and-braces. The canonical authoring shape carries
|
|
297
|
+
`acceptance[]` / `verify[]` at the ticket's top level and assembly folds them
|
|
298
|
+
into the body, so the passes that scan `body.acceptance` / `body.verify`
|
|
299
|
+
(`implicit-cross-story-dep`, `missing-bdd-scaffold`) saw two empty arrays on
|
|
300
|
+
the real payload and emitted nothing. Both passes complete before the first
|
|
301
|
+
`createIssue`, so a refusal still costs no writes.
|
|
302
|
+
|
|
303
|
+
`shared-editor` findings are rendered into the posted `plan-summary` comment,
|
|
304
|
+
directly beneath the wave table: the table promises which Stories can run
|
|
305
|
+
together, and a path two same-wave Stories both write is exactly where that
|
|
306
|
+
promise breaks. Promise and caveat belong on one durable surface — previously
|
|
307
|
+
the caveat was a stderr warning nobody kept.
|
|
308
|
+
|
|
309
|
+
Two `planning.*` knobs upgrade a conflict class from advisory to a hard
|
|
310
|
+
refusal. **Both default to `false` and are documented, not recommended:**
|
|
311
|
+
|
|
312
|
+
| Knob | Upgrades | Why it is off |
|
|
313
|
+
| --- | --- | --- |
|
|
314
|
+
| `planning.failOnSharedEditors` | `shared-editor` → `hard` | Co-editing one file is routine and often correct; the delivery scheduler already serializes file-overlapping Stories. |
|
|
315
|
+
| `planning.requireExplicitCrossStoryDeps` | `implicit-cross-story-dep` → `hard` | Path references are matched by substring, so a legitimate mention in prose can read as a dependency. |
|
|
316
|
+
|
|
317
|
+
Turn one on for a repo where the class is genuinely fatal; expect a refusal to
|
|
318
|
+
name the Stories and the fix (a `depends_on` edge, or folding the shared edit
|
|
319
|
+
into one Story). The sibling knobs `failOnRegistryConflicts`,
|
|
320
|
+
`failOnMissingBddScaffold` and `failOnLargeFanOut` behave the same way.
|
|
321
|
+
|
|
246
322
|
## Tickets mode — authoring `supersedes[]`
|
|
247
323
|
|
|
248
324
|
In `--tickets` mode each Story carries a top-level `supersedes` array claiming
|
|
@@ -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.
|
|
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
|
|
154
|
-
[`bootstrap.js`](../scripts/bootstrap.js) uses — `applyQualityBootstrap`
|
|
155
|
-
|
|
156
|
-
|
|
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)
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
main-tracked root baselines are never touched. A second run reports
|
|
167
|
-
`no-change` on every path — the idempotence
|
|
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. **
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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.
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
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. **
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
Record
|
|
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
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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`, `
|
|
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,64 @@ 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.33.0](https://github.com/dsj1984/mandrel/compare/mandrel-v2.32.0...mandrel-v2.33.0) (2026-08-07)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
* deliver: widen footprints from edit intent only, explain every withhold, and give standalone audit cohorts declared edges ([#5044](https://github.com/dsj1984/mandrel/issues/5044)) ([#5049](https://github.com/dsj1984/mandrel/issues/5049)) ([6bd0653](https://github.com/dsj1984/mandrel/commit/6bd0653e88e07ddbcc90c3c947b5f135374649d4))
|
|
24
|
+
* plan: stamp only the provenance a Story owns, and run cross-Story conflict analysis over the bodies persist actually writes ([#5045](https://github.com/dsj1984/mandrel/issues/5045)) ([#5048](https://github.com/dsj1984/mandrel/issues/5048)) ([804b308](https://github.com/dsj1984/mandrel/commit/804b3087c001f21815aecb109bb636e59a87b2cb))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
* check-knip-entries: resolve the entry list through knip's own config resolver, and skip cleanly when there is no knip config ([#5039](https://github.com/dsj1984/mandrel/issues/5039)) ([#5041](https://github.com/dsj1984/mandrel/issues/5041)) ([7f69a18](https://github.com/dsj1984/mandrel/commit/7f69a18ead79da77779981bec3eed8772dc05643))
|
|
30
|
+
* deliver: make the declared-edge channel trustworthy — paginate native reads, fail loud on 404, parse footers strictly ([#5046](https://github.com/dsj1984/mandrel/issues/5046)) ([#5047](https://github.com/dsj1984/mandrel/issues/5047)) ([38b543d](https://github.com/dsj1984/mandrel/commit/38b543dec2634318b2407b54404239e7ae34d381))
|
|
31
|
+
* **lint:** add a --root scan seam so the lint test stops mutating the shared tree (refs [#5052](https://github.com/dsj1984/mandrel/issues/5052)) ([#5053](https://github.com/dsj1984/mandrel/issues/5053)) ([69d1952](https://github.com/dsj1984/mandrel/commit/69d1952fb7410659ca21a9bb0d34eefadb654d2e))
|
|
32
|
+
* plan: resolve the cross-Story conflict policy once for the raw and assembled passes ([#5050](https://github.com/dsj1984/mandrel/issues/5050)) ([711199a](https://github.com/dsj1984/mandrel/commit/711199a861bc1e85218a09fe4674c507b0c0edb1))
|
|
33
|
+
|
|
34
|
+
## [2.32.0](https://github.com/dsj1984/mandrel/compare/mandrel-v2.31.0...mandrel-v2.32.0) (2026-08-06)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
### ⚠ BREAKING CHANGES
|
|
38
|
+
|
|
39
|
+
* `.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.
|
|
40
|
+
* `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.
|
|
41
|
+
* `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`.
|
|
42
|
+
* `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.
|
|
43
|
+
* 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.
|
|
44
|
+
* `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.
|
|
45
|
+
|
|
46
|
+
### Added
|
|
47
|
+
|
|
48
|
+
* 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))
|
|
49
|
+
* **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))
|
|
50
|
+
* **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))
|
|
51
|
+
* 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))
|
|
52
|
+
* 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))
|
|
53
|
+
* **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))
|
|
54
|
+
* 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))
|
|
55
|
+
* 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))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
### Fixed
|
|
59
|
+
|
|
60
|
+
* 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))
|
|
61
|
+
* **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))
|
|
62
|
+
* 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))
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
### Changed
|
|
66
|
+
|
|
67
|
+
* 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))
|
|
68
|
+
* 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))
|
|
69
|
+
* **orchestration:** delete production-dead story-lifecycle module ([#5025](https://github.com/dsj1984/mandrel/issues/5025)) ([811966c](https://github.com/dsj1984/mandrel/commit/811966c0e1d0f2a746261e342f64acfe1b6f6bd5))
|
|
70
|
+
* 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))
|
|
71
|
+
* 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))
|
|
72
|
+
* 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))
|
|
73
|
+
* 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))
|
|
74
|
+
* 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))
|
|
75
|
+
|
|
18
76
|
## [2.31.0](https://github.com/dsj1984/mandrel/compare/mandrel-v2.30.0...mandrel-v2.31.0) (2026-08-05)
|
|
19
77
|
|
|
20
78
|
|
package/lib/migrations/index.js
CHANGED
|
@@ -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
|
+
};
|