mandrel 2.24.0 → 2.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/.agents/agents/acceptance-critic.md +19 -21
  2. package/.agents/agents/auditor.md +16 -19
  3. package/.agents/agents/story-worker.md +41 -54
  4. package/.agents/audit-checklists/architecture.md +5 -1
  5. package/.agents/audit-checklists/baselines.md +21 -0
  6. package/.agents/audit-checklists/quality.md +3 -0
  7. package/.agents/docs/agentrc-reference.json +2 -1
  8. package/.agents/docs/configuration.md +2 -1
  9. package/.agents/docs/quality-gates.md +80 -18
  10. package/.agents/docs/workflows.md +4 -2
  11. package/.agents/instructions.md +1 -1
  12. package/.agents/rules/ci-remediation.md +68 -3
  13. package/.agents/schemas/agentrc.schema.json +6 -1
  14. package/.agents/schemas/audit-rules.json +15 -0
  15. package/.agents/schemas/baselines/audit-baselines-envelope.schema.json +242 -0
  16. package/.agents/schemas/baselines/baseline-envelope.schema.json +8 -0
  17. package/.agents/schemas/baselines/crap.schema.json +13 -1
  18. package/.agents/schemas/crap-report.schema.json +37 -0
  19. package/.agents/schemas/model-attribution.schema.json +4 -0
  20. package/.agents/scripts/acceptance-eval.js +124 -15
  21. package/.agents/scripts/audit-baselines.js +136 -0
  22. package/.agents/scripts/audit-labels-bootstrap.js +16 -95
  23. package/.agents/scripts/audit-to-stories.js +33 -3
  24. package/.agents/scripts/check-arch-cycles.js +12 -93
  25. package/.agents/scripts/check-baseline-drift.js +16 -3
  26. package/.agents/scripts/check-baselines.js +19 -3
  27. package/.agents/scripts/check-context-budget.js +95 -14
  28. package/.agents/scripts/check-cyclomatic.js +214 -0
  29. package/.agents/scripts/check-schema-references.js +392 -0
  30. package/.agents/scripts/check-test-temp-hygiene.js +121 -1
  31. package/.agents/scripts/check-workflow-timeouts.js +291 -0
  32. package/.agents/scripts/cleanup-repo-test-temp.js +7 -5
  33. package/.agents/scripts/deliver-light.js +3 -4
  34. package/.agents/scripts/deliver-recover.js +13 -0
  35. package/.agents/scripts/diagnose-friction.js +85 -19
  36. package/.agents/scripts/lib/audit-baselines/engine.js +177 -0
  37. package/.agents/scripts/lib/audit-baselines/gate-surface.js +63 -0
  38. package/.agents/scripts/lib/audit-baselines/headroom.js +72 -0
  39. package/.agents/scripts/lib/audit-baselines/hotspots.js +69 -0
  40. package/.agents/scripts/lib/audit-baselines/kinds.js +313 -0
  41. package/.agents/scripts/lib/audit-baselines/outliers.js +100 -0
  42. package/.agents/scripts/lib/audit-baselines/read.js +87 -0
  43. package/.agents/scripts/lib/audit-baselines/staleness.js +123 -0
  44. package/.agents/scripts/lib/audit-baselines/surface-entry.js +106 -0
  45. package/.agents/scripts/lib/audit-baselines/trend.js +125 -0
  46. package/.agents/scripts/lib/audit-baselines/weights.js +193 -0
  47. package/.agents/scripts/lib/audit-suite/index.js +0 -5
  48. package/.agents/scripts/lib/audit-suite/selector.js +9 -62
  49. package/.agents/scripts/lib/audit-to-stories/audit-label-taxonomy.js +177 -0
  50. package/.agents/scripts/lib/audit-to-stories/audit-lenses.js +1 -0
  51. package/.agents/scripts/lib/audit-to-stories/build-story-body.js +35 -3
  52. package/.agents/scripts/lib/audit-to-stories/group-findings.js +20 -11
  53. package/.agents/scripts/lib/audit-to-stories/parse-audit-md.js +23 -9
  54. package/.agents/scripts/lib/audit-to-stories/seed-from-findings.js +9 -2
  55. package/.agents/scripts/lib/baseline-loader.js +0 -0
  56. package/.agents/scripts/lib/baseline-schema-registry.js +13 -1
  57. package/.agents/scripts/lib/baselines/diff-scope-cli.js +22 -160
  58. package/.agents/scripts/lib/baselines/duplication-scanner.js +27 -0
  59. package/.agents/scripts/lib/baselines/git-base.js +0 -0
  60. package/.agents/scripts/lib/baselines/kinds/crap.js +499 -57
  61. package/.agents/scripts/lib/baselines/preview-gates.js +58 -15
  62. package/.agents/scripts/lib/baselines/reader.js +10 -0
  63. package/.agents/scripts/lib/baselines/refresh-service.js +69 -11
  64. package/.agents/scripts/lib/baselines/scope.js +39 -90
  65. package/.agents/scripts/lib/baselines/writer.js +16 -7
  66. package/.agents/scripts/lib/changed-files.js +8 -1
  67. package/.agents/scripts/lib/cli-args.js +115 -1
  68. package/.agents/scripts/lib/close-validation/runner.js +70 -25
  69. package/.agents/scripts/lib/config-settings-schema-delivery.js +5 -0
  70. package/.agents/scripts/lib/crap-engine.js +124 -27
  71. package/.agents/scripts/lib/crap-method-identity.js +153 -0
  72. package/.agents/scripts/lib/crap-utils.js +86 -13
  73. package/.agents/scripts/lib/cyclomatic-ceiling.js +265 -0
  74. package/.agents/scripts/lib/env-loader.js +46 -16
  75. package/.agents/scripts/lib/feedback-loop/audit-results-graduator.js +0 -2
  76. package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +0 -2
  77. package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +0 -2
  78. package/.agents/scripts/lib/findings/route-finding.js +123 -17
  79. package/.agents/scripts/lib/findings/severity.js +80 -2
  80. package/.agents/scripts/lib/git/cached-fetch.js +0 -0
  81. package/.agents/scripts/lib/git-utils.js +136 -80
  82. package/.agents/scripts/lib/import-graph.js +156 -0
  83. package/.agents/scripts/lib/label-constants.js +17 -0
  84. package/.agents/scripts/lib/observability/runtime-friction.js +17 -2
  85. package/.agents/scripts/lib/observability/source-classifier.js +175 -2
  86. package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +110 -17
  87. package/.agents/scripts/lib/orchestration/ceremony-routing.js +17 -12
  88. package/.agents/scripts/lib/orchestration/check-baselines/phases/compare.js +36 -6
  89. package/.agents/scripts/lib/orchestration/check-baselines/phases/evaluate.js +5 -0
  90. package/.agents/scripts/lib/orchestration/check-baselines/phases/floors.js +12 -1
  91. package/.agents/scripts/lib/orchestration/check-baselines/phases/report.js +8 -1
  92. package/.agents/scripts/lib/orchestration/ci-rerun-guard.js +548 -0
  93. package/.agents/scripts/lib/orchestration/deliver-recover.js +2 -2
  94. package/.agents/scripts/lib/orchestration/git-cleanup/phases/phase-drivers.js +10 -5
  95. package/.agents/scripts/lib/orchestration/git-cleanup/phases/render.js +39 -3
  96. package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +71 -20
  97. package/.agents/scripts/lib/orchestration/light-backstop.js +20 -3
  98. package/.agents/scripts/lib/orchestration/light-escalation.js +82 -3
  99. package/.agents/scripts/lib/orchestration/light-suitability.js +103 -5
  100. package/.agents/scripts/lib/orchestration/plan-context.js +119 -66
  101. package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +31 -5
  102. package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +216 -109
  103. package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +61 -13
  104. package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +79 -22
  105. package/.agents/scripts/lib/orchestration/plan-text-hygiene.js +51 -20
  106. package/.agents/scripts/lib/orchestration/planning/authoring-context.js +70 -74
  107. package/.agents/scripts/lib/orchestration/planning/memory-pool-advisory.js +231 -0
  108. package/.agents/scripts/lib/orchestration/resolve-stories.js +18 -17
  109. package/.agents/scripts/lib/orchestration/retro-proposals.js +0 -0
  110. package/.agents/scripts/lib/orchestration/run-epilogue.js +12 -0
  111. package/.agents/scripts/lib/orchestration/run-scoped-config.js +276 -0
  112. package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +41 -10
  113. package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +125 -37
  114. package/.agents/scripts/lib/orchestration/single-story-close/phases/normalize-pr-title.js +6 -6
  115. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +42 -38
  116. package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +129 -21
  117. package/.agents/scripts/lib/orchestration/single-story-close/phases/push.js +6 -1
  118. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +267 -125
  119. package/.agents/scripts/lib/orchestration/spec-budget.js +16 -5
  120. package/.agents/scripts/lib/orchestration/story-follow-ups.js +182 -95
  121. package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +22 -0
  122. package/.agents/scripts/lib/orchestration/ticket-validator.js +5 -11
  123. package/.agents/scripts/lib/orchestration/ticketing/reads.js +4 -4
  124. package/.agents/scripts/lib/reserved-test-ids.js +77 -0
  125. package/.agents/scripts/lib/story-adjacency.js +3 -3
  126. package/.agents/scripts/lib/story-plan.js +137 -42
  127. package/.agents/scripts/lib/templates/decomposer-prompts.js +4 -4
  128. package/.agents/scripts/lib/test-runner-contract.js +134 -0
  129. package/.agents/scripts/lib/test-tiers.js +11 -2
  130. package/.agents/scripts/lib/ticket-body-sections.js +1 -1
  131. package/.agents/scripts/lib/util/concurrent-map.js +17 -0
  132. package/.agents/scripts/lib/util/parse-id-list.js +103 -0
  133. package/.agents/scripts/lib/util/poll-loop.js +18 -2
  134. package/.agents/scripts/lib/wave-runner/live-probe.js +29 -13
  135. package/.agents/scripts/lib/wave-runner/ready-set.js +254 -43
  136. package/.agents/scripts/lib/workers/combined-mi-crap-worker.js +10 -13
  137. package/.agents/scripts/lib/workers/crap-worker.js +8 -13
  138. package/.agents/scripts/lib/workers/maintainability-report-worker.js +4 -10
  139. package/.agents/scripts/lib/workers/maintainability-worker.js +4 -10
  140. package/.agents/scripts/lib/workers/serve-worker-messages.js +35 -0
  141. package/.agents/scripts/lib/worktree/git-hooks.js +206 -0
  142. package/.agents/scripts/lib/worktree/lifecycle/creation.js +6 -0
  143. package/.agents/scripts/lib/worktree-manager.js +14 -0
  144. package/.agents/scripts/plan-run-epilogue.js +17 -5
  145. package/.agents/scripts/pr-watch-with-update.js +568 -213
  146. package/.agents/scripts/providers/github/tickets.js +33 -10
  147. package/.agents/scripts/provision-git-hooks.js +85 -0
  148. package/.agents/scripts/quality-preview.js +137 -28
  149. package/.agents/scripts/resolve-stories.js +4 -1
  150. package/.agents/scripts/run-coverage.js +86 -35
  151. package/.agents/scripts/run-lint.js +20 -0
  152. package/.agents/scripts/run-tests.js +36 -36
  153. package/.agents/scripts/single-story-close.js +34 -2
  154. package/.agents/scripts/single-story-confirm-merge.js +22 -6
  155. package/.agents/scripts/single-story-init.js +7 -0
  156. package/.agents/scripts/stories-wave-tick.js +308 -47
  157. package/.agents/scripts/story-plan.js +65 -9
  158. package/.agents/scripts/update-coverage-baseline.js +34 -4
  159. package/.agents/scripts/update-crap-baseline.js +42 -4
  160. package/.agents/scripts/update-duplication-baseline.js +209 -83
  161. package/.agents/scripts/update-maintainability-baseline.js +31 -4
  162. package/.agents/scripts/validate-docs-freshness.js +1 -0
  163. package/.agents/skills/core/diagnose-friction/SKILL.md +4 -1
  164. package/.agents/skills/core/gates-and-baselines/SKILL.md +17 -11
  165. package/.agents/skills/skills.index.json +2 -2
  166. package/.agents/templates/single-story-body.md +16 -8
  167. package/.agents/workflows/audit-architecture.md +69 -13
  168. package/.agents/workflows/audit-baselines.md +289 -0
  169. package/.agents/workflows/audit-navigability.md +5 -4
  170. package/.agents/workflows/audit-quality.md +26 -0
  171. package/.agents/workflows/audit-to-stories.md +30 -4
  172. package/.agents/workflows/deliver.md +92 -97
  173. package/.agents/workflows/helpers/acceptance-self-eval.md +47 -10
  174. package/.agents/workflows/helpers/audit-lens-core.md +25 -4
  175. package/.agents/workflows/helpers/code-quality-guardrails.md +9 -2
  176. package/.agents/workflows/helpers/deliver-digest.md +41 -21
  177. package/.agents/workflows/helpers/deliver-reference.md +95 -4
  178. package/.agents/workflows/helpers/deliver-story-reference.md +174 -52
  179. package/.agents/workflows/helpers/deliver-story.md +104 -119
  180. package/.agents/workflows/helpers/plan-reference.md +49 -5
  181. package/.agents/workflows/memory-consolidate.md +116 -0
  182. package/.agents/workflows/plan.md +62 -75
  183. package/README.md +13 -6
  184. package/docs/CHANGELOG.md +93 -0
  185. package/lib/cli/update.js +14 -11
  186. package/lib/cli/version-check.js +9 -1
  187. package/package.json +9 -4
  188. package/.agents/schemas/friction-event.schema.json +0 -56
  189. package/.agents/scripts/lib/feedback-loop/memory-freshness.js +0 -707
@@ -7,47 +7,43 @@ description:
7
7
 
8
8
  # /plan
9
9
 
10
- > **Lean spine.** Happy path + gate list; edge-case detail lives in on-demand
10
+ > **Lean spine.** Happy path + gate list; edge-case detail lives on demand in
11
11
  > [`helpers/plan-reference.md`](helpers/plan-reference.md).
12
12
 
13
13
  ## Inputs
14
14
 
15
15
  Single planning path — there is no Epic/Story router, no scope-triage
16
16
  `epic|story` verdict. **Derive the mode from what the operator typed, announce
17
- it, then act**:
17
+ it, act**:
18
18
 
19
19
  | Invocation | Mode | Behavior |
20
20
  | --- | --- | --- |
21
21
  | `/plan` | ask | Ask what to plan; nothing runs first. |
22
- | `/plan add a --json flag to doctor` | seed | Ideation from the prose: interrogate → author **one Story by default** → persist. |
23
- | `/plan temp/notes/idea.md` | seed-file | Same, from notes. An argument resolving to an existing file is a path, never prose. |
22
+ | `/plan add a --json flag to doctor` | seed | Ideation from prose: interrogate → author **one Story by default** → persist. |
23
+ | `/plan temp/notes/idea.md` | seed-file | Same, from notes. An existing file is a path, not prose. |
24
24
  | `/plan 4712[,4713…]` | tickets | Fetch issue(s), analyze into proper Stories (prefer N=1 rewrite). |
25
25
  | `/plan 4712`, already delivered | amends | Amend a shipped Story from a **delta envelope**, not a re-interrogation. |
26
26
 
27
27
  **Resolving a bare id.** Read live state rather than asking: `agent::done` can
28
28
  only be amended, an open unplanned issue can only be planned. **Announce the
29
- derivation** — "4712 is `agent::done` → amending" so a wrong read costs one
30
- correction. Ask **only** for an open Story already at `agent::ready`.
31
-
32
- `--body` is **not** a `/plan` entry; persist goes through `plan-persist.js`.
29
+ derivation** — "4712 is `agent::done` → amending". Ask **only** for an open
30
+ Story already at `agent::ready`.
33
31
 
34
32
  ## Saying what you want
35
33
 
36
- No flags to remember — state intent and the workflow fills in the CLI
37
- ([reference](helpers/plan-reference.md)). Run any script with `--help` rather
38
- than copying its surface here.
34
+ No flags to remember — state intent; the workflow fills in the CLI
35
+ ([reference](helpers/plan-reference.md)). Run scripts with `--help`.
39
36
 
40
37
  `--yes` is **runner-set, never operator-typed** — cron, `/loop`, and headless
41
- dispatch set it to mean *nobody is at the keyboard*, which is what auto-proceeds
42
- the gates below. Never offer it to an operator or add it to an attended run.
38
+ dispatch set it to mean *nobody is at the keyboard*, which auto-proceeds the
39
+ gates below (#1 and #2). Never offer it to an operator or an attended run.
43
40
 
44
41
  ## Default-single split policy
45
42
 
46
43
  Author **one Story** unless the pieces have **near-zero overlap** or sit across
47
- an **architectural seam** (different deployables, migration vs consumer).
48
- Coupled work stays one Story — `## Slicing` checkpoints, not sibling tickets;
49
- when N>1 every acceptance criterion belongs to exactly one Story
50
- (`assertAcceptancePartition` refuses coupled splits). **N=1 is the lean path.**
44
+ an **architectural seam**. Coupled work stays one Story — `## Slicing`
45
+ checkpoints, not sibling tickets
46
+ ([detail](helpers/plan-reference.md)). **N=1 is lean.**
51
47
 
52
48
  ## Procedure
53
49
 
@@ -63,54 +59,49 @@ node .agents/scripts/plan-context.js --seed "<seed>" \
63
59
  and derives source ids from its `sourceTickets[]`; the CLI also writes
64
60
  **`stories.template.json`**, step 2's skeleton.
65
61
 
66
- The envelope carries docs context, the story-author
67
- prompt, `sourceTickets[]`, `duplicates[]` (open **Stories**, never Epics), and
68
- advisory `complexitySignals` (**no routing authority**). A trivial scope earns
69
- `--route-downgrade-reason "<why>"` at persist — shape-validated, failing closed
70
- to `full` ([detail](helpers/plan-reference.md)).
71
-
72
- **Triage each unknown by resolver**
73
- ([detail](helpers/plan-reference.md)): an **AFK** unknown (research settles
74
- it) is resolved before authoring, never assumed; a **HITL** unknown (an
75
- operator call) goes to Gate #1 as "needs your decision". Under `--yes`, do
76
- not ask free-form operator questions AFK unknowns are still researched;
77
- only HITL unknowns land in Key Assumptions, each marked a
62
+ The envelope carries docs context, the story-author prompt, `sourceTickets[]`,
63
+ `duplicates[]` (open **Stories**, never Epics) and advisory `complexitySignals`
64
+ (**no routing authority**). A trivial scope can claim the lite route at
65
+ persist — shape-validated, failing closed to `full`
66
+ ([detail](helpers/plan-reference.md)).
67
+
68
+ **Triage each unknown by resolver** ([detail](helpers/plan-reference.md)): an
69
+ **AFK** unknown (research settles it) is resolved before authoring, never
70
+ assumed; a **HITL** unknown (an operator call) goes to Gate #1 as "needs your
71
+ decision". Under `--yes` do not ask free-form operator questions AFK unknowns
72
+ are still researched; only HITL unknowns land in Key Assumptions, each marked a
78
73
  decision-made-by-default.
79
74
 
80
75
  **Gate #1** — STOP to confirm the sharpened plan intent and any
81
76
  duplicate-candidate review. Under `--yes`, auto-proceed.
82
77
 
83
- On a truthy `deliverLightSuggestion.suggested`, offer
84
- **advisory, never an automatic reroute** to deliver the seed instead of
85
- planning it. On confirm, route **in this session** into
86
- [`helpers/deliver-light.md`](helpers/deliver-light.md), filling its gate from
87
- this envelope, not the raw seed; an `ask-operator` verdict returns here to
88
- step 2 with the interrogation intact. Under `--yes` it is recorded and planning
89
- proceeds — never auto-downgraded to light.
78
+ On a truthy `memoryPoolAdvisory.recommend`, name [`/memory-consolidate`](memory-consolidate.md)
79
+ as an operator option, quoting its `reasons[]` advisory, never invoked here.
90
80
 
91
- A truthy `complexitySignals.uiSurface` marks a UI-touching plan: name
92
- [`/prototype`](prototype.md) as an operator option never invoke it here.
81
+ On a truthy `deliverLightSuggestion.suggested`, offer advisory, never an
82
+ automatic reroute to deliver the seed instead; on confirm, route **in this
83
+ session** into [`helpers/deliver-light.md`](helpers/deliver-light.md), its gate
84
+ filled from this envelope. A truthy `complexitySignals.uiSurface` marks a
85
+ UI-touching plan: name [`/prototype`](prototype.md) as an operator option,
86
+ never invoke it here. [Both offers](helpers/plan-reference.md).
93
87
 
94
88
  ### 2. Author
95
89
 
96
90
  **One-shot authoring.** Start from `stories.template.json`; author
97
- `stories.json` in one pass. `body` is a markdown string **or** a structured
98
- object; persist parses either, serializes the canonical markdown, and syncs
99
- top-level `acceptance[]` /
100
- `verify[]` into it — never dual-author those lists.
101
-
102
- **Grounding = your reads + Phase 8.** Nothing inventories the repo for you:
103
- read each file you cite, then persist's file-assumption gate hard-errors on
104
- every `{path, assumption}` that misses the real tree.
91
+ `stories.json` in one pass. `body` is markdown **or** a structured object;
92
+ persist parses either, serializes canonical markdown and syncs top-level
93
+ `acceptance[]` / `verify[]` into it — never dual-author those lists.
105
94
 
106
- Entry fields (the `stories.template.json` shape):
95
+ **Grounding = your reads + Phase 8.** Nothing inventories the repo: read each
96
+ file you cite; persist's file-assumption gate hard-errors on any
97
+ `{path, assumption}` absent from the tree. Entry fields:
107
98
  [reference](helpers/plan-reference.md).
108
99
 
109
100
  Artifacts under `temp/plan-<slug>/`: `stories.json` (**length 1 by default**;
110
- over-budget Specs fail closed — split or tighten, never under `docs/`);
111
- optional `techspec.md` (**N===1 only** — folded into `## Spec`); optional
112
- `acceptance-manifest.json` (N>1 partition list — `--plan-acceptance`). For N=1
113
- use the envelope `systemPrompts.story`; split only under the policy above.
101
+ over-budget Specs fail closed — split or tighten, never under `docs/`); optional
102
+ `techspec.md` (**N===1 only** — folded into `## Spec`); optional
103
+ `acceptance-manifest.json` (N>1 partition — `--plan-acceptance`). For N=1 use
104
+ the envelope `systemPrompts.story`; split only under the policy above.
114
105
 
115
106
  **Tickets mode:** every Story authors a top-level `supersedes[]`; persist
116
107
  refuses a partial map ([shape](helpers/plan-reference.md)).
@@ -123,28 +114,27 @@ node .agents/scripts/plan-critics.js \
123
114
  [--tech-spec temp/plan-<slug>/techspec.md]
124
115
  ```
125
116
 
126
- Run **before** persist — the last point a finding folds into a re-author round.
117
+ Run **before** persist — the last point a finding folds into a re-author.
127
118
  It exits 0 on **any** verdict (verdicts route work, they do not gate) and exits
128
119
  **1** only on a usage/IO error — no critic ran, no skip ledgered: **do not
129
- proceed to Persist**; fix and re-run.
120
+ proceed to Persist**, fix and re-run.
130
121
 
131
122
  - **Both `dispatch: false`** — proceed to Persist (each skip is ledgered).
132
123
  - **Either `dispatch: true`** — dispatch **one fresh-context, maker-blind
133
124
  sub-agent per firing critic** (hand it only the draft artifacts, never the
134
125
  authoring transcript), fold findings into Gate #2 or a re-author round, re-run
135
126
  this step. Pre-mortem triggers (incl. the external-dependency probe), the
136
- advisory-only `textHygiene.findings[]` lints, and the dispatch shape:
137
- [`helpers/plan-reference.md` § Critic dispatch detail](helpers/plan-reference.md).
127
+ advisory-only `textHygiene.findings[]` lints and dispatch shape:
128
+ [reference § Critic dispatch detail](helpers/plan-reference.md).
138
129
 
139
130
  ### 3. Persist
140
131
 
141
132
  **Gate #2** — STOP for approval before persist **only** when the operator asked
142
133
  to review (`--force-review`). Under `--yes`, auto-proceed.
143
134
 
144
- Run persist with `--dry-run` **first** — same command, GitHub writes suppressed;
145
- every gate (validator, body parse, DAG, capacity, budget, reachability,
146
- split/supersede partitions, Spec fold) runs before the first `createIssue`.
147
- Then:
135
+ Run persist with `--dry-run` **first** — same command, writes suppressed;
136
+ every gate runs before the first `createIssue`
137
+ ([the list](helpers/plan-reference.md)):
148
138
 
149
139
  ```bash
150
140
  node .agents/scripts/plan-persist.js \
@@ -155,28 +145,25 @@ node .agents/scripts/plan-persist.js \
155
145
  [--source-tickets 123,456]
156
146
  ```
157
147
 
158
- At lite shape, `--chain-on-clean` chains a clean dry-run into the real persist
159
- in one round-trip; a full plan keeps its review round-trip.
160
-
161
- Persist creates `type::story` issue(s) plus a `plan-run::<id>` grouping label
162
- (**metadata only**); N>1 `depends_on` edges become `blocked by #<id>` footers.
163
- `agent::ready` is the **terminal** flip after all receipts land. stdout is pure
164
- JSON.
148
+ At lite shape, `--chain-on-clean` folds a clean dry-run into the real persist;
149
+ a full plan keeps its review trip.
165
150
 
166
- In tickets mode persist resolves source ids **envelope-first** and closes each
167
- as `not_planned` with a comment (default on;
168
- [detail](helpers/plan-reference.md)).
151
+ Persist creates `type::story` issue(s), a **metadata-only** `plan-run::<id>`
152
+ label, and `blocked by #<id>` footers for N>1 `depends_on` edges.
153
+ `agent::ready` is the **terminal** flip after receipts land; stdout is pure
154
+ JSON. Tickets mode also comments on and closes each source id
155
+ ([detail](helpers/plan-reference.md)).
169
156
 
170
157
  ## Constraints
171
158
 
172
159
  - `/plan` starts delivery **only** through a confirmed Gate #1 light route —
173
160
  never off its own authored Stories, which land via [`/deliver`](deliver.md).
174
- - Duplicate search targets open Stories (`type::story`), not Epics.
175
- - Deterministic gates still fail closed under `--yes`.
161
+ - Duplicate search targets open Stories (`type::story`), not Epics; and
162
+ deterministic gates still fail closed under `--yes`.
176
163
 
177
164
  ## See also
178
165
 
179
- - [`/deliver`](deliver.md), [`/audit-to-stories`](audit-to-stories.md),
180
- [`helpers/plan-reference.md`](helpers/plan-reference.md) on-demand detail.
181
- - [`core/scope-triage`](../skills/core/scope-triage/SKILL.md) — optional
182
- split-advisory notes only (no routing verdict).
166
+ [`/deliver`](deliver.md), [`/audit-to-stories`](audit-to-stories.md),
167
+ [`helpers/plan-reference.md`](helpers/plan-reference.md) (on-demand detail),
168
+ [`core/scope-triage`](../skills/core/scope-triage/SKILL.md) — optional
169
+ split-advisory notes only (no routing verdict).
package/README.md CHANGED
@@ -182,7 +182,9 @@ dimensions, run model, and how to benchmark a new version.
182
182
  ## Contributors
183
183
 
184
184
  The published `mandrel` package ships three directories — `.agents/`, `bin/`,
185
- and `lib/` (see the `files` array in [`package.json`](package.json)).
185
+ and `lib/` plus the single file `docs/CHANGELOG.md`, and excludes the
186
+ `__tests__` subtrees under `.agents/` and `lib/` (see the `files` array in
187
+ [`package.json`](package.json)).
186
188
  `.agents/` is the payload `mandrel sync` materializes into a consumer's
187
189
  `./.agents/` directory; `bin/mandrel.js` and its `lib/` implementation stay
188
190
  inside `node_modules/mandrel/` and back the `npx mandrel …` CLI used
@@ -193,8 +195,8 @@ tooling and is not published.
193
195
  Common commands while developing the framework itself:
194
196
 
195
197
  ```bash
196
- npm run lint # markdown + biome
197
- npm run format # auto-format
198
+ npm run lint # biome + markdownlint + repo ratchets + generated-doc drift
199
+ npm run format # biome format — JavaScript/JSON only, not markdown
198
200
  npm test # framework tests
199
201
  npm run test:coverage # tests with coverage gate
200
202
  ```
@@ -226,9 +228,14 @@ against malicious lifecycle scripts in compromised transitive packages
226
228
  install scripts for a specific install, run
227
229
  `npm install --ignore-scripts=false` for that invocation only.
228
230
 
229
- CRAP and Maintainability gates fire at three sites — close-validation
230
- (story close), `.husky/pre-push`, and CI (`ci.yml`, push + PR) — against
231
- the same thresholds from `delivery.quality.*` in `.agentrc.json`.
231
+ CRAP and Maintainability gates fire at four sites — `.husky/pre-commit`
232
+ (`quality-preview.js --staged`, scored on the staged index), `.husky/pre-push`
233
+ (diff-scoped against `origin/main`), close-validation (story close), and CI
234
+ (`ci.yml`, push + PR) — against the same thresholds from
235
+ `delivery.quality.*` in `.agentrc.json`. All four are **blocking**: the
236
+ pre-commit hook fires earliest and refuses the commit on a threshold
237
+ violation, and a green `npm run lint` / `npm test` / `check-baselines.js`
238
+ run does not predict it.
232
239
 
233
240
  ## License
234
241
 
package/docs/CHANGELOG.md CHANGED
@@ -2,6 +2,99 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ <!--
6
+ Machine-generated by release-please (`changelog-path` in
7
+ `release-please-config.json`); only the header above is hand-owned — every
8
+ release prepends a section beneath it. The three rules below are properties
9
+ of conventional-changelog-writer's output, not of this repository's prose
10
+ style: `*` list markers (MD004), a double blank line before each `###`
11
+ section (MD012), and `*`-wrapped commit subjects that read as unbalanced
12
+ emphasis (MD037). They would be reintroduced on every release cut, failing
13
+ lint on release-please's own PR, so they are exempted file-wide rather than
14
+ hand-repaired. Do not extend this list to cover hand-written prose defects.
15
+ -->
16
+ <!-- markdownlint-disable-file MD004 MD012 MD037 -->
17
+
18
+ ## [2.26.0](https://github.com/dsj1984/mandrel/compare/mandrel-v2.25.0...mandrel-v2.26.0) (2026-08-03)
19
+
20
+
21
+ ### Added
22
+
23
+ * /audit-baselines lens: ratchet-tightening audit workflow over the committed baseline surface ([#4903](https://github.com/dsj1984/mandrel/issues/4903)) ([#4913](https://github.com/dsj1984/mandrel/issues/4913)) ([420fb09](https://github.com/dsj1984/mandrel/commit/420fb09df605edfd8ace86e6b2295c7bd6909c3f))
24
+ * **acceptance-eval:** merge parallel cluster verdicts into one gate call per round (refs [#4951](https://github.com/dsj1984/mandrel/issues/4951)) ([#4954](https://github.com/dsj1984/mandrel/issues/4954)) ([56b69dd](https://github.com/dsj1984/mandrel/commit/56b69dde0a5faee418d5f49f4b0c3eab5f682245))
25
+ * **audit:** add the read-only baseline hotspot engine (refs [#4902](https://github.com/dsj1984/mandrel/issues/4902)) ([#4910](https://github.com/dsj1984/mandrel/issues/4910)) ([22a2245](https://github.com/dsj1984/mandrel/commit/22a22458645823705a2cfcf94ea6d1a7acb4546e))
26
+ * **deliver:** accept dash ranges in delivery Story-id lists (refs [#4927](https://github.com/dsj1984/mandrel/issues/4927)) ([#4928](https://github.com/dsj1984/mandrel/issues/4928)) ([9000044](https://github.com/dsj1984/mandrel/commit/90000442d5f578a4895b5950784e2fb8493d316a))
27
+ * **deliver:** add --merge-watch-mode and mandate one-turn fan-out dispatch (refs [#4949](https://github.com/dsj1984/mandrel/issues/4949)) ([#4957](https://github.com/dsj1984/mandrel/issues/4957)) ([fa1c426](https://github.com/dsj1984/mandrel/commit/fa1c426c181cb3696b4ea34166c99719cea877c2))
28
+ * make the coverage instrument measure: widen CI's run to every test tier, configure the gate, and reseed the baseline ([#4922](https://github.com/dsj1984/mandrel/issues/4922)) ([#4931](https://github.com/dsj1984/mandrel/issues/4931)) ([bc94fbf](https://github.com/dsj1984/mandrel/commit/bc94fbf65b41d53098606aff7f7b58dc1679ea5d))
29
+ * repair the inert quality instruments and tighten the ratchets behind the reclaimed slack ([#4923](https://github.com/dsj1984/mandrel/issues/4923)) ([#4933](https://github.com/dsj1984/mandrel/issues/4933)) ([3cc3a30](https://github.com/dsj1984/mandrel/commit/3cc3a30b5e54b3dac78649dd9536ad617086ee0c))
30
+ * retire friction-event.schema.json or wire it, and stop four docs citing a schema nothing compiles ([#4938](https://github.com/dsj1984/mandrel/issues/4938)) ([#4941](https://github.com/dsj1984/mandrel/issues/4941)) ([259f6e6](https://github.com/dsj1984/mandrel/commit/259f6e6cc47db88434b0612aa9327638afb73911))
31
+ * retire the memory-freshness scanner and ship the visible memory-hygiene loop ([#4919](https://github.com/dsj1984/mandrel/issues/4919)) ([#4921](https://github.com/dsj1984/mandrel/issues/4921)) ([54cf87c](https://github.com/dsj1984/mandrel/commit/54cf87c1a4e929681f194fc899decb24636bd5f2))
32
+
33
+
34
+ ### Fixed
35
+
36
+ * **baselines:** settle the exit through runAsCli so a piped report is not truncated ([#4932](https://github.com/dsj1984/mandrel/issues/4932)) ([d3670de](https://github.com/dsj1984/mandrel/commit/d3670dec493c2ba60a60e365c1ab809bde58ce43)), closes [#4783](https://github.com/dsj1984/mandrel/issues/4783)
37
+ * close three harness gaps: unbounded CI jobs, divergent runner flags, and inert pretest hooks ([#4936](https://github.com/dsj1984/mandrel/issues/4936)) ([#4942](https://github.com/dsj1984/mandrel/issues/4942)) ([d71040d](https://github.com/dsj1984/mandrel/commit/d71040dc0b558906a42327dcd9bd88ccfc26694c))
38
+ * exclude transpiled-coordinate rows from the 0%-coverage fill, and refuse a baseline that predates provenance stamping ([#4901](https://github.com/dsj1984/mandrel/issues/4901)) ([#4905](https://github.com/dsj1984/mandrel/issues/4905)) ([adc135b](https://github.com/dsj1984/mandrel/commit/adc135b3338550e2911e7139d638be8670eb5bab))
39
+ * **friction:** refuse a quoted --cmd as a usage error and split ENOBUFS from timeout (refs [#4915](https://github.com/dsj1984/mandrel/issues/4915)) ([#4918](https://github.com/dsj1984/mandrel/issues/4918)) ([64989aa](https://github.com/dsj1984/mandrel/commit/64989aa4a5cf746d959b649e760bf93f96f2ead0))
40
+ * **git:** give the git runners an explicit 64 MB stdout ceiling, unblocking Story close ([#4953](https://github.com/dsj1984/mandrel/issues/4953)) ([75160a4](https://github.com/dsj1984/mandrel/commit/75160a491704acc2de127d9135bcfcbc846bdfee))
41
+ * give the raised GitHub write fan-outs transient-retry backoff and single-source their concurrency bound ([#4961](https://github.com/dsj1984/mandrel/issues/4961)) ([#4964](https://github.com/dsj1984/mandrel/issues/4964)) ([459a3f1](https://github.com/dsj1984/mandrel/commit/459a3f12da0280881b757c0855d33d0a89221320))
42
+ * let a dangling-citation anchor count inside a code span (refs [#4906](https://github.com/dsj1984/mandrel/issues/4906)) ([#4911](https://github.com/dsj1984/mandrel/issues/4911)) ([a96ae9f](https://github.com/dsj1984/mandrel/commit/a96ae9fbed4b701ff45d13e268d91e960ebbe595))
43
+ * **lint:** lint docs/ so the gate matches the code-review lens ([#4971](https://github.com/dsj1984/mandrel/issues/4971)) ([b1f4e65](https://github.com/dsj1984/mandrel/commit/b1f4e6551670f145fa6f6e688a16130e8da644d7))
44
+ * make git hooks fire in linked worktrees and stop bypassing pre-push at close ([#4943](https://github.com/dsj1984/mandrel/issues/4943)) ([#4946](https://github.com/dsj1984/mandrel/issues/4946)) ([2ac0597](https://github.com/dsj1984/mandrel/commit/2ac05979241ca2ad061e14fbeda2b71c1b299da7))
45
+ * make the ratchet trend roll-up count what it measures and baseline staleness commit-aware ([#4962](https://github.com/dsj1984/mandrel/issues/4962)) ([#4967](https://github.com/dsj1984/mandrel/issues/4967)) ([fff36bf](https://github.com/dsj1984/mandrel/commit/fff36bf79ab4c75a2f4274c60228ab0942ded65a))
46
+ * **observability:** attribute terminal friction to the emitting CLI ([#4966](https://github.com/dsj1984/mandrel/issues/4966)) ([5aa986a](https://github.com/dsj1984/mandrel/commit/5aa986aaee97e858329ab8f948a1a59db8aeb1c4))
47
+ * **observability:** classify a bare framework-script basename as framework (refs [#4916](https://github.com/dsj1984/mandrel/issues/4916)) ([#4920](https://github.com/dsj1984/mandrel/issues/4920)) ([a8e0bbc](https://github.com/dsj1984/mandrel/commit/a8e0bbc22ad1cc8e0ab3ce117e597c537be96d7c))
48
+ * **plan-persist:** re-carry audit provenance onto dependent Stories (refs [#4935](https://github.com/dsj1984/mandrel/issues/4935)) ([#4939](https://github.com/dsj1984/mandrel/issues/4939)) ([6866b0c](https://github.com/dsj1984/mandrel/commit/6866b0c368de0ff438b565d5049a38273b56ef8c))
49
+ * **quality:** key anonymous CRAP methods by enclosing scope, not escomplex ordinal (refs [#4969](https://github.com/dsj1984/mandrel/issues/4969)) ([#4970](https://github.com/dsj1984/mandrel/issues/4970)) ([ca5da99](https://github.com/dsj1984/mandrel/commit/ca5da9940da57aaf20d34844afa835f6a8b127f0))
50
+ * read prior baseline rows per kind so duplication's diff-scope preservation and epsilon damping actually work ([#4937](https://github.com/dsj1984/mandrel/issues/4937)) ([#4940](https://github.com/dsj1984/mandrel/issues/4940)) ([e7d13b5](https://github.com/dsj1984/mandrel/commit/e7d13b551fb958c38833a7946785929dd17907a3))
51
+ * retune the advisory Spec word budget to 350 and repair its report channel (refs [#4907](https://github.com/dsj1984/mandrel/issues/4907)) ([#4909](https://github.com/dsj1984/mandrel/issues/4909)) ([3c914ba](https://github.com/dsj1984/mandrel/commit/3c914ba9430743f2e5399ded6ac74aee4c4148d1))
52
+ * **scripts:** make --help short-circuit before check-test-temp-hygiene does work ([#4968](https://github.com/dsj1984/mandrel/issues/4968)) ([5e873ac](https://github.com/dsj1984/mandrel/commit/5e873acbc316a37e5a173a94bb2fe52090614493))
53
+ * stop check-baselines silently disabling its compare arm when the base baseline cannot be read ([#4914](https://github.com/dsj1984/mandrel/issues/4914)) ([#4917](https://github.com/dsj1984/mandrel/issues/4917)) ([d62a334](https://github.com/dsj1984/mandrel/commit/d62a334555bccd605bd37bd9d8a544fdcc4e3710))
54
+ * stop the git-cleanup branches phase announcing a DRY RUN before it deletes ([#4898](https://github.com/dsj1984/mandrel/issues/4898)) ([3edad1e](https://github.com/dsj1984/mandrel/commit/3edad1e8c9a8906c1abc5f02e79e7ee4792775b2))
55
+ * **wave-runner:** exempt glob footprints from cross-beat reservation (refs [#4960](https://github.com/dsj1984/mandrel/issues/4960)) ([#4963](https://github.com/dsj1984/mandrel/issues/4963)) ([ca935e2](https://github.com/dsj1984/mandrel/commit/ca935e223f211340c671668a53738de634c12c04))
56
+ * **wave-runner:** reserve in-flight Story footprints in the ready-set guard (refs [#4950](https://github.com/dsj1984/mandrel/issues/4950)) ([#4955](https://github.com/dsj1984/mandrel/issues/4955)) ([7f8ffb3](https://github.com/dsj1984/mandrel/commit/7f8ffb31c523dba01fb9cffe376a5149d92d7c08))
57
+
58
+
59
+ ### Performance
60
+
61
+ * **plan:** run the /plan path's independent gathers and write loops concurrently (refs [#4952](https://github.com/dsj1984/mandrel/issues/4952)) ([#4956](https://github.com/dsj1984/mandrel/issues/4956)) ([8c8ec6c](https://github.com/dsj1984/mandrel/commit/8c8ec6c1134f23fbf8214d138438b3febc6014c8))
62
+
63
+
64
+ ### Changed
65
+
66
+ * **baselines:** migrate the duplication CLI onto refreshBaseline (refs [#4944](https://github.com/dsj1984/mandrel/issues/4944)) ([#4945](https://github.com/dsj1984/mandrel/issues/4945)) ([38e43d2](https://github.com/dsj1984/mandrel/commit/38e43d2362031cfaebbfeeaa8bec75c3c5ecf56b))
67
+ * burn down the cross-gate hotspot clusters and remove the shipped seams nothing calls ([#4926](https://github.com/dsj1984/mandrel/issues/4926)) ([#4934](https://github.com/dsj1984/mandrel/issues/4934)) ([970c7f8](https://github.com/dsj1984/mandrel/commit/970c7f88f5d198b685f90ede5358beea5b2896ce))
68
+
69
+ ## [2.25.0](https://github.com/dsj1984/mandrel/compare/mandrel-v2.24.0...mandrel-v2.25.0) (2026-08-01)
70
+
71
+
72
+ ### Added
73
+
74
+ * enforce the no-rerun verifier: disarm auto-merge on the first red and gate re-arming on the head SHA moving ([#4865](https://github.com/dsj1984/mandrel/issues/4865)) ([#4867](https://github.com/dsj1984/mandrel/issues/4867)) ([b95267f](https://github.com/dsj1984/mandrel/commit/b95267fe49cd27ca7cd42bb3bc7ae0c6ceaa5a3a))
75
+
76
+
77
+ ### Fixed
78
+
79
+ * **crap:** measure coordinate mixing, not ordinary drift, in the preview gate (refs [#4871](https://github.com/dsj1984/mandrel/issues/4871)) ([#4881](https://github.com/dsj1984/mandrel/issues/4881)) ([63bcb66](https://github.com/dsj1984/mandrel/commit/63bcb66ab46471cfc1b8ad957d60dd1526d0f679))
80
+ * escape the remaining raw NUL delimiters so these diffs are reviewable ([#4896](https://github.com/dsj1984/mandrel/issues/4896)) ([992fc46](https://github.com/dsj1984/mandrel/commit/992fc46b7675736c8fc7af546600cc59ef5ec96d))
81
+ * make the sanctioned CI watch survive a late-arriving required context, and pass --repo through as a real gh flag ([#4890](https://github.com/dsj1984/mandrel/issues/4890)) ([#4893](https://github.com/dsj1984/mandrel/issues/4893)) ([00c23f4](https://github.com/dsj1984/mandrel/commit/00c23f45f57f6b9d11c7314b92f337a31dda4c2b))
82
+ * normalize audit severity and labels without re-minting fingerprints, carry dedupe provenance, and give the lenses a built-but-not-wired mandate ([#4877](https://github.com/dsj1984/mandrel/issues/4877)) ([#4888](https://github.com/dsj1984/mandrel/issues/4888)) ([04f8937](https://github.com/dsj1984/mandrel/commit/04f89373e4dcaa0a47d5501381fb02b5e29bcef6))
83
+ * pin the run's base branch at init and fail closed when config changed mid-run (refs [#4891](https://github.com/dsj1984/mandrel/issues/4891)) ([#4894](https://github.com/dsj1984/mandrel/issues/4894)) ([8099020](https://github.com/dsj1984/mandrel/commit/8099020a9ea2e662c19a1c8a3d9e32d6269df863))
84
+ * **plan:** reconcile the persist body contract and stop acceptance-eval burning a round on every read (refs [#4874](https://github.com/dsj1984/mandrel/issues/4874)) ([#4882](https://github.com/dsj1984/mandrel/issues/4882)) ([33512b4](https://github.com/dsj1984/mandrel/commit/33512b48702e50d9ebaf98dffc396a9ce0091290))
85
+ * ratchet context-budget shrinkage and stop the baseline updaters writing on --help (refs [#4872](https://github.com/dsj1984/mandrel/issues/4872)) ([#4879](https://github.com/dsj1984/mandrel/issues/4879)) ([8d445a4](https://github.com/dsj1984/mandrel/commit/8d445a45e8a4c57b65573213042fb4aec557af08))
86
+ * stamp the version cache with the real clock and end unquoted env values at an unescaped comment marker (refs [#4878](https://github.com/dsj1984/mandrel/issues/4878)) ([#4883](https://github.com/dsj1984/mandrel/issues/4883)) ([d73ee10](https://github.com/dsj1984/mandrel/commit/d73ee10c2f15c3698705281a5979c7394eb03fa4))
87
+ * stop close-resume opening duplicate empty PRs and stop the PR watcher reporting green on a blocked PR ([#4873](https://github.com/dsj1984/mandrel/issues/4873)) ([#4884](https://github.com/dsj1984/mandrel/issues/4884)) ([9d12f8d](https://github.com/dsj1984/mandrel/commit/9d12f8dcd4badbe3b657f1526c284f7689bf04ee))
88
+ * stop the CRAP row builder silently mixing transpiled and original line coordinates, and refuse to compare on an unsound basis ([#4866](https://github.com/dsj1984/mandrel/issues/4866)) ([#4868](https://github.com/dsj1984/mandrel/issues/4868)) ([a6c8c88](https://github.com/dsj1984/mandrel/commit/a6c8c8866c72b1372eb5fd5b7232fb3833e33f93))
89
+ * stop the test suite writing fixture friction signals into the real ledger, and stop unresolvable Story ids reaching an auto-filed ticket ([#4892](https://github.com/dsj1984/mandrel/issues/4892)) ([#4895](https://github.com/dsj1984/mandrel/issues/4895)) ([a607de4](https://github.com/dsj1984/mandrel/commit/a607de49a09c778c1fbefe68d4c57d6a75f0b648))
90
+ * warn about the un-waivable light-path verdict up front, de-conflict on actual footprints, and reconcile the concurrency override ([#4875](https://github.com/dsj1984/mandrel/issues/4875)) ([#4880](https://github.com/dsj1984/mandrel/issues/4880)) ([16d4958](https://github.com/dsj1984/mandrel/commit/16d49584227d783695bb36043e29c64a58aaab39))
91
+
92
+
93
+ ### Changed
94
+
95
+ * reclaim workflow-spine byte headroom and move the close-and-land tail to the orchestrator (refs [#4876](https://github.com/dsj1984/mandrel/issues/4876)) ([#4885](https://github.com/dsj1984/mandrel/issues/4885)) ([7d981cd](https://github.com/dsj1984/mandrel/commit/7d981cde4f8c3d1047a09e64d1f193b85fbfd4b6))
96
+ * widen workflow-spine and agent-boot headroom to a real editing margin (refs [#4876](https://github.com/dsj1984/mandrel/issues/4876)) ([#4889](https://github.com/dsj1984/mandrel/issues/4889)) ([45f5482](https://github.com/dsj1984/mandrel/commit/45f548238c9c82a1e36df11320b73c8f9dea27f5))
97
+
5
98
  ## [2.24.0](https://github.com/dsj1984/mandrel/compare/mandrel-v2.23.0...mandrel-v2.24.0) (2026-07-31)
6
99
 
7
100
 
package/lib/cli/update.js CHANGED
@@ -247,10 +247,15 @@ function resolveProjectRoot() {
247
247
  * `mandrel` version via the daily freshness cache (`version-check.js`).
248
248
  *
249
249
  * When `bypassCache` is `true` (the default for an explicit `mandrel update`
250
- * call — Story #4046 A1b), the cache freshness window is effectively zeroed by
251
- * passing `now` as far in the future, which makes `isStale` treat any existing
252
- * cache as stale and always issue exactly one network probe. The cache is still
253
- * written so the post-update `version-current` advisory has a fresh baseline.
250
+ * call — Story #4046 A1b), `isStale` is asked to skip its freshness read via
251
+ * `forceRefresh`, so any existing cache is ignored and exactly one network
252
+ * probe is issued. The cache is still written so the post-update
253
+ * `version-current` advisory has a fresh baseline.
254
+ *
255
+ * The bypass deliberately does **not** shift `now`: the same clock is stamped
256
+ * into the refreshed `checkedAt`, so a shifted clock would persist a
257
+ * ~48h-future timestamp and defeat the 24h window for every later passive
258
+ * check until real time caught up (Story #4878).
254
259
  *
255
260
  * When `bypassCache` is `false` (passive staleness checks only), the normal
256
261
  * 24h-cache semantics apply: a fresh cache returns the cached version with
@@ -281,16 +286,14 @@ async function defaultResolveTargetVersion({
281
286
  bypassCache = false,
282
287
  log = () => {},
283
288
  } = {}) {
284
- // When bypassCache is true, push `now` far enough into the future that any
285
- // cached checkedAt value is guaranteed to be older than the STALE_AFTER_MS
286
- // window, forcing a fresh network probe (Story #4046 A1b).
287
- const effectiveNow = bypassCache
288
- ? new Date(now.getTime() + 48 * 60 * 60 * 1000)
289
- : now;
289
+ // Bypass the cache by telling isStale to skip its freshness read never by
290
+ // shifting `now`, which is also the clock persisted as `checkedAt`
291
+ // (Story #4046 A1b forcing the probe; Story #4878 keeping the stamp honest).
290
292
  const result = await isStale({
291
293
  cachePath,
292
- now: effectiveNow,
294
+ now,
293
295
  runner,
296
+ forceRefresh: bypassCache,
294
297
  fs,
295
298
  log,
296
299
  });
@@ -142,10 +142,17 @@ export function refreshCache({
142
142
  * The freshness comparison uses `>=` so an exactly-24h-old cache counts as
143
143
  * stale (the boundary is treated as "due for refresh").
144
144
  *
145
+ * `forceRefresh` skips the freshness read entirely and always probes. It is
146
+ * the supported way to bypass the cache: callers must **not** fake staleness
147
+ * by shifting `now` forward, because `now` is also the clock stamped into the
148
+ * refreshed `checkedAt` — a shifted clock persists a future timestamp and
149
+ * defeats the freshness window for every later reader (Story #4878).
150
+ *
145
151
  * @param {{
146
152
  * cachePath: string,
147
153
  * now?: Date,
148
154
  * runner: () => (string | Promise<string>),
155
+ * forceRefresh?: boolean,
149
156
  * fs?: typeof import('node:fs'),
150
157
  * log?: (msg: string) => void,
151
158
  * }} opts
@@ -160,10 +167,11 @@ export async function isStale({
160
167
  cachePath,
161
168
  now = new Date(),
162
169
  runner,
170
+ forceRefresh = false,
163
171
  fs = nodeFs,
164
172
  log = () => {},
165
173
  }) {
166
- const cached = readCache({ cachePath, fs });
174
+ const cached = forceRefresh ? null : readCache({ cachePath, fs });
167
175
 
168
176
  if (cached) {
169
177
  const ageMs = now.getTime() - new Date(cached.checkedAt).getTime();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mandrel",
3
- "version": "2.24.0",
3
+ "version": "2.26.0",
4
4
  "description": "Claude Code-first opinionated workflow framework: instructions, skills, rules, and SDLC workflows that govern AI coding assistants.",
5
5
  "files": [
6
6
  ".agents/",
@@ -15,9 +15,7 @@
15
15
  "provenance": true
16
16
  },
17
17
  "scripts": {
18
- "pretest": "node .agents/scripts/test-wrapper.js && node .agents/scripts/validate-skills.js",
19
- "pretest:quick": "node .agents/scripts/test-wrapper.js",
20
- "pretest:integration": "node .agents/scripts/test-wrapper.js",
18
+ "//test-preflight": "There are deliberately NO pretest* scripts (Story #4936). .npmrc sets ignore-scripts=true as supply-chain defence (CWE-1357), and that suppresses every pre*/post* lifecycle script for `npm run` too — so a pretest entry here would look like a gate and never execute. Each tier's preflight is declared once in .agents/scripts/lib/test-runner-contract.js and invoked by the runners themselves, which is why it fires for `npm test`, `npm run test:coverage` and a bare `node .agents/scripts/run-tests.js` alike.",
21
19
  "test": "node .agents/scripts/run-tests.js",
22
20
  "test:quick": "node .agents/scripts/run-tests.js --tier quick",
23
21
  "test:integration": "node .agents/scripts/run-tests.js --tier integration",
@@ -28,6 +26,7 @@
28
26
  "test:coverage": "node .agents/scripts/run-coverage.js",
29
27
  "coverage:check": "node .agents/scripts/check-baselines.js --gate coverage",
30
28
  "coverage:update": "node .agents/scripts/update-coverage-baseline.js",
29
+ "coverage:reanchor": "node .agents/scripts/update-coverage-baseline.js --full-scope",
31
30
  "baseline:agents-loc": "node baselines/agents-loc-baseline.mjs",
32
31
  "lint:md": "markdownlint-cli2 \".agents/**/*.md\" \"*.md\" \"!node_modules/**\" \"!.worktrees/**\"",
33
32
  "lint": "node .agents/scripts/run-lint.js && npm run docs:check",
@@ -39,13 +38,19 @@
39
38
  "format:check": "biome ci .",
40
39
  "maintainability:check": "node .agents/scripts/check-baselines.js --gate maintainability",
41
40
  "maintainability:update": "node .agents/scripts/update-maintainability-baseline.js",
41
+ "maintainability:reanchor": "node .agents/scripts/update-maintainability-baseline.js --full-scope",
42
42
  "check:arch": "node .agents/scripts/check-arch-cycles.js",
43
43
  "check:context-budget": "node .agents/scripts/check-context-budget.js",
44
+ "check:cyclomatic": "node .agents/scripts/check-cyclomatic.js",
45
+ "cyclomatic:update": "node .agents/scripts/check-cyclomatic.js --update",
46
+ "check:schema-refs": "node .agents/scripts/check-schema-references.js",
44
47
  "check:workflow-citations": "node .agents/scripts/check-workflow-citations.js",
45
48
  "crap:check": "node .agents/scripts/check-baselines.js --gate crap",
46
49
  "crap:update": "node .agents/scripts/update-crap-baseline.js",
50
+ "crap:reanchor": "node .agents/scripts/update-crap-baseline.js --full-scope",
47
51
  "duplication:check": "node .agents/scripts/check-baselines.js --gate duplication",
48
52
  "duplication:update": "node .agents/scripts/update-duplication-baseline.js",
53
+ "duplication:reanchor": "node .agents/scripts/update-duplication-baseline.js --full-scope",
49
54
  "quality:preview": "node .agents/scripts/check-dead-exports.js && node .agents/scripts/quality-preview.js",
50
55
  "quality:watch": "node .agents/scripts/quality-watch.js",
51
56
  "sync:commands": "node bin/mandrel.js sync-commands",
@@ -1,56 +0,0 @@
1
- {
2
- "$schema": "http://json-schema.org/draft-07/schema#",
3
- "title": "FrictionEvent",
4
- "type": "object",
5
- "additionalProperties": false,
6
- "properties": {
7
- "eventId": {
8
- "type": "string",
9
- "format": "uuid"
10
- },
11
- "timestamp": {
12
- "type": "string",
13
- "format": "date-time"
14
- },
15
- "sprintId": {
16
- "type": "string"
17
- },
18
- "category": {
19
- "type": "string",
20
- "enum": [
21
- "Prompt Ambiguity",
22
- "Missing Skill",
23
- "Incorrect Persona",
24
- "Tool Limitation",
25
- "Execution Error"
26
- ]
27
- },
28
- "source": {
29
- "type": "object",
30
- "additionalProperties": false,
31
- "properties": {
32
- "tool": {
33
- "type": "string"
34
- },
35
- "command": {
36
- "type": "string"
37
- }
38
- }
39
- },
40
- "details": {
41
- "type": "string",
42
- "description": "Specific error message or observation."
43
- },
44
- "context": {
45
- "type": "object",
46
- "additionalProperties": false,
47
- "properties": {
48
- "protocolFile": {
49
- "type": "string",
50
- "description": "Path to relevant protocol file, e.g., '.agents/skills/core/tdd.md'"
51
- }
52
- }
53
- }
54
- },
55
- "required": ["eventId", "timestamp", "sprintId", "category", "details"]
56
- }