mandrel 1.93.0 → 1.94.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 (135) hide show
  1. package/.agents/agents/acceptance-critic.md +129 -0
  2. package/.agents/agents/retro.md +42 -0
  3. package/.agents/agents/story-worker.md +162 -0
  4. package/.agents/docs/configuration.md +7 -1
  5. package/.agents/docs/execution-reference.md +27 -2
  6. package/.agents/instructions.md +43 -33
  7. package/.agents/personas/engineer.md +26 -112
  8. package/.agents/personas/security-engineer.md +1 -2
  9. package/.agents/rules/git-conventions-reference.md +225 -0
  10. package/.agents/rules/git-conventions.md +25 -200
  11. package/.agents/rules/security-baseline.md +5 -0
  12. package/.agents/rules/testing-standards.md +106 -13
  13. package/.agents/schemas/agentrc.schema.json +31 -1
  14. package/.agents/schemas/lifecycle/slice.end.schema.json +21 -0
  15. package/.agents/schemas/lifecycle/slice.heartbeat.schema.json +20 -0
  16. package/.agents/schemas/lifecycle/slice.start.schema.json +17 -0
  17. package/.agents/scripts/acceptance-eval.js +62 -18
  18. package/.agents/scripts/agents-bootstrap-github.js +1 -1
  19. package/.agents/scripts/bookkeeping-reconcile.js +117 -0
  20. package/.agents/scripts/check-context-budget.js +62 -5
  21. package/.agents/scripts/diagnose-friction.js +0 -6
  22. package/.agents/scripts/epic-deliver-prepare.js +272 -10
  23. package/.agents/scripts/lib/bootstrap/project-bootstrap.js +56 -18
  24. package/.agents/scripts/lib/close-validation/gates.js +159 -21
  25. package/.agents/scripts/lib/config/acceptance-eval.js +52 -5
  26. package/.agents/scripts/lib/config/delivery-routing.js +87 -0
  27. package/.agents/scripts/lib/config/explain.js +2 -0
  28. package/.agents/scripts/lib/config-resolver.js +1 -1
  29. package/.agents/scripts/lib/config-settings-schema-delivery.js +37 -3
  30. package/.agents/scripts/lib/config-settings-schema-quality.js +9 -0
  31. package/.agents/scripts/lib/doc-tiers.js +37 -2
  32. package/.agents/scripts/lib/observability/active-story-env.js +111 -2
  33. package/.agents/scripts/lib/observability/hook-heartbeat.js +219 -0
  34. package/.agents/scripts/lib/observability/tool-trace-hook.js +15 -4
  35. package/.agents/scripts/lib/orchestration/acceptance-clusters.js +111 -0
  36. package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +32 -4
  37. package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +270 -0
  38. package/.agents/scripts/lib/orchestration/ceremony-routing.js +141 -0
  39. package/.agents/scripts/lib/orchestration/context-hydration-engine.js +3 -124
  40. package/.agents/scripts/lib/orchestration/deliver-route.js +173 -0
  41. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/authoring-context.js +1 -1
  42. package/.agents/scripts/lib/orchestration/epic-run-state-store.js +233 -0
  43. package/.agents/scripts/lib/orchestration/lifecycle/emit-slice-lifecycle.js +270 -0
  44. package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +83 -2
  45. package/.agents/scripts/lib/orchestration/lifecycle/listeners/checkpoint-pointer-writer.js +6 -0
  46. package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +3 -2
  47. package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +1 -0
  48. package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +1 -0
  49. package/.agents/scripts/lib/orchestration/ticket-validator.js +1 -1
  50. package/.agents/scripts/lib/provider-factory.js +1 -1
  51. package/.agents/scripts/lib/templates/decomposer-prompts.js +1 -1
  52. package/.agents/scripts/post-structured-comment.js +38 -0
  53. package/.agents/scripts/slice-phase.js +361 -0
  54. package/.agents/scripts/sync-claude-agents.js +165 -0
  55. package/.agents/scripts/update-ticket-state.js +31 -0
  56. package/.agents/scripts/wave-tick.js +138 -9
  57. package/.agents/skills/core/api-and-interface-design/SKILL.md +5 -3
  58. package/.agents/skills/core/code-review-and-quality/SKILL.md +63 -7
  59. package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +1 -1
  60. package/.agents/skills/core/epic-plan-consolidate/SKILL.md +5 -5
  61. package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +8 -8
  62. package/.agents/skills/core/epic-plan-premortem/SKILL.md +4 -4
  63. package/.agents/skills/core/epic-plan-spec-author/SKILL.md +26 -56
  64. package/.agents/skills/core/gates-and-baselines/SKILL.md +149 -0
  65. package/.agents/skills/core/idea-refinement/SKILL.md +2 -8
  66. package/.agents/skills/core/qa-coverage-mapping/SKILL.md +7 -7
  67. package/.agents/skills/skills.index.json +11 -381
  68. package/.agents/workflows/deliver.md +47 -4
  69. package/.agents/workflows/helpers/acceptance-self-eval.md +38 -13
  70. package/.agents/workflows/helpers/deliver-epic-reference.md +18 -5
  71. package/.agents/workflows/helpers/deliver-epic-single.md +331 -0
  72. package/.agents/workflows/helpers/deliver-epic.md +51 -8
  73. package/.agents/workflows/helpers/deliver-stories.md +15 -5
  74. package/.agents/workflows/helpers/epic-deliver-story.md +12 -3
  75. package/.agents/workflows/helpers/mandrel-sync-config.md +1 -1
  76. package/.agents/workflows/helpers/plan-epic.md +25 -23
  77. package/.agents/workflows/mandrel-update.md +1 -1
  78. package/docs/CHANGELOG.md +16 -0
  79. package/lib/cli/registry.js +95 -0
  80. package/package.json +4 -2
  81. package/.agents/personas/engineer-mobile.md +0 -120
  82. package/.agents/personas/engineer-web.md +0 -111
  83. package/.agents/personas/product.md +0 -94
  84. package/.agents/personas/refactorer.md +0 -113
  85. package/.agents/personas/sre.md +0 -86
  86. package/.agents/personas/ux-designer.md +0 -95
  87. package/.agents/scripts/epic-plan-decompose.js +0 -54
  88. package/.agents/scripts/epic-plan-spec.js +0 -64
  89. package/.agents/scripts/lib/orchestration/skill-capsule-loader.js +0 -109
  90. package/.agents/scripts/plan-critics.js +0 -199
  91. package/.agents/skills/core/baseline-refresh/SKILL.md +0 -181
  92. package/.agents/skills/core/ci-cd-and-automation/SKILL.md +0 -274
  93. package/.agents/skills/core/ci-cd-and-automation/examples.md +0 -211
  94. package/.agents/skills/core/code-simplification/SKILL.md +0 -389
  95. package/.agents/skills/core/context-engineering/SKILL.md +0 -309
  96. package/.agents/skills/core/context-engineering/examples.md +0 -58
  97. package/.agents/skills/core/deprecation-and-migration/SKILL.md +0 -250
  98. package/.agents/skills/core/frontend-ui-engineering/SKILL.md +0 -357
  99. package/.agents/skills/core/hydrate-context/SKILL.md +0 -123
  100. package/.agents/skills/core/idea-refinement/examples.md +0 -437
  101. package/.agents/skills/core/idea-refinement/frameworks.md +0 -135
  102. package/.agents/skills/core/incremental-implementation/SKILL.md +0 -271
  103. package/.agents/skills/core/introducing-a-baseline-gate/SKILL.md +0 -213
  104. package/.agents/skills/core/knowledge-transfer/SKILL.md +0 -180
  105. package/.agents/skills/core/mutation-survivor-remediation/SKILL.md +0 -117
  106. package/.agents/skills/core/performance-optimization/SKILL.md +0 -314
  107. package/.agents/skills/core/planning-and-task-breakdown/SKILL.md +0 -277
  108. package/.agents/skills/core/property-based-testing/SKILL.md +0 -148
  109. package/.agents/skills/core/refactoring-discipline/SKILL.md +0 -111
  110. package/.agents/skills/core/shipping-and-launch/SKILL.md +0 -328
  111. package/.agents/skills/core/spec-driven-development/SKILL.md +0 -252
  112. package/.agents/skills/core/test-driven-development/SKILL.md +0 -475
  113. package/.agents/skills/core/using-agent-skills/SKILL.md +0 -232
  114. package/.agents/skills/stack/architecture/monorepo-path-strategist/SKILL.md +0 -31
  115. package/.agents/skills/stack/architecture/structured-output-zod/SKILL.md +0 -51
  116. package/.agents/skills/stack/architecture/subagent-orchestration/SKILL.md +0 -76
  117. package/.agents/skills/stack/backend/cloudflare-hono-architect/SKILL.md +0 -31
  118. package/.agents/skills/stack/backend/cloudflare-hono-architect/examples/route-template.ts +0 -33
  119. package/.agents/skills/stack/backend/cloudflare-queue-manager/SKILL.md +0 -31
  120. package/.agents/skills/stack/backend/cloudflare-workers/SKILL.md +0 -51
  121. package/.agents/skills/stack/backend/highlevel-crm/SKILL.md +0 -54
  122. package/.agents/skills/stack/backend/sqlite-drizzle-expert/SKILL.md +0 -29
  123. package/.agents/skills/stack/backend/sqlite-drizzle-expert/examples/schema-template.ts +0 -30
  124. package/.agents/skills/stack/backend/stripe-integration/SKILL.md +0 -57
  125. package/.agents/skills/stack/backend/stripe-integration/scripts/listen-stripe.sh +0 -9
  126. package/.agents/skills/stack/backend/turso-sqlite/SKILL.md +0 -48
  127. package/.agents/skills/stack/frontend/astro/SKILL.md +0 -62
  128. package/.agents/skills/stack/frontend/astro-react-island-strategist/SKILL.md +0 -30
  129. package/.agents/skills/stack/frontend/expo-react-native-developer/SKILL.md +0 -29
  130. package/.agents/skills/stack/frontend/google-analytics-v4/SKILL.md +0 -50
  131. package/.agents/skills/stack/frontend/tailwind-v4/SKILL.md +0 -58
  132. package/.agents/skills/stack/frontend/ui-accessibility-engineer/SKILL.md +0 -34
  133. package/.agents/skills/stack/qa/audit-accessibility/SKILL.md +0 -51
  134. package/.agents/skills/stack/qa/lighthouse-baseline/SKILL.md +0 -199
  135. package/.agents/skills/stack/security/backend-security-patterns/SKILL.md +0 -68
@@ -84,8 +84,13 @@ performance ceiling. See `agentrc-reference.json`
84
84
  ### Sub-agent dispatch capability
85
85
 
86
86
  `Agent` calls emit no `model:` argument by default — children inherit from
87
- the `general-purpose` sub-agent definition and the parent's worktree
88
- context. No `--dangerously-skip-permissions` (no subprocess is spawned).
87
+ their sub-agent definition and the parent's worktree context. With
88
+ `delivery.routing.roleScopedAgents` on (the default, Epic #4478), a Story child
89
+ boots on the role-scoped [`story-worker`](../../agents/story-worker.md)
90
+ definition — its own system prompt, no `CLAUDE.md` @-closure — instead of the
91
+ full-closure `general-purpose` definition the kill-switch (`roleScopedAgents:
92
+ false`) falls back to. No `--dangerously-skip-permissions` (no subprocess is
93
+ spawned).
89
94
  Per [`.agents/instructions.md` § 4](../../instructions.md)'s cost-aware
90
95
  spawning heuristic, this optional per-call `model:` is the escape hatch: if
91
96
  a specific call would run better on a cheaper or faster capability the host
@@ -143,9 +148,17 @@ The `--check-idle <minutes>` mode scans the per-Epic lifecycle ledger
143
148
  `story.dispatch.start` without a matching `story.dispatch.end` (the
144
149
  canonical in-flight list — see § 2a's `nextAction['in-flight']`), and
145
150
  compares each in-flight Story's most recent ledger event (any
146
- `story.*` event, notably the `story.heartbeat` records emitted by
147
- `story-phase.js` at each Story-level phase transition) against the
148
- threshold. **Before flagging a stall, it also checks the last commit on
151
+ `story.*` event, notably the `story.heartbeat` records) against the
152
+ threshold. **As of Epic #4476 (M5) those `story.heartbeat` records are
153
+ emitted OFF the token stream by the PostToolUse hook** (a throttled
154
+ heartbeat per tool call, keyed off the active-Story env) rather than by a
155
+ per-transition `story-phase.js` LLM turn — the signal the watchdog reads is
156
+ unchanged; only its emission mechanism moved. Single-delivery runs have no
157
+ Story fan-out; their liveness signal is `slice.heartbeat` (same hook, keyed
158
+ off the active-slice env), and the watchdog reads it via the additive
159
+ `inFlightSlices` / `stalledSlices` envelope fields — a slice that has gone
160
+ silent past the threshold with no recent commit on `epic-<id>` is flagged
161
+ exactly as a stalled Story is. **Before flagging a stall, it also checks the last commit on
149
162
  `story-<id>` via `git log` (Story #3900): a Story whose branch carries a
150
163
  commit newer than the threshold is making forward progress and is left
151
164
  in-flight, never stalled — deterministic protection against the
@@ -0,0 +1,331 @@
1
+ ---
2
+ description: >-
3
+ Single-delivery Epic path (invoked by /deliver when the route resolves to
4
+ `single`). Collapses a spec-only Epic — one that authored NO Story tickets —
5
+ into ONE guarded in-session slice walk on `epic/<id>`: prepare `--single`,
6
+ walk the Epic body's `## Delivery Slicing` table in order (implement + commit
7
+ each slice, flip its checkpoint marker), run per-AC-cluster acceptance
8
+ critics, then hand off to `deliver-epic.md` Phase 3 for the merge tail
9
+ (Phases 3–9 reused byte-for-byte). Not a second engine — deliver-epic with
10
+ Phases 1–2 replaced by the slice walk.
11
+ ---
12
+
13
+ # helpers/deliver-epic-single — single-delivery Epic path (invoked by /deliver)
14
+
15
+ > **Runtime core.** The single-delivery executor for Epic #4475. The
16
+ > [`/deliver`](../deliver.md) router dispatches here when
17
+ > [`resolveEpicDeliveryRoute`](../../scripts/lib/orchestration/deliver-route.js)
18
+ > returns `single` (the Epic carries `delivery::single` or its
19
+ > `epic-plan-state` checkpoint has `decompose.shape === "single"`), unless the
20
+ > `delivery.routing.singleDelivery=false` kill-switch forces fan-out.
21
+
22
+ ## Overview
23
+
24
+ A spec-only plan (authored by `/plan`'s single-delivery variant) creates **no
25
+ Story tickets** — the Epic body's `## Delivery Slicing` table is the whole
26
+ work breakdown and the audit trail. There is nothing to fan out, so this path
27
+ does not run the [`deliver-epic.md`](deliver-epic.md) Phase 1–2 wave loop.
28
+ Instead it walks the slicing table **in order** inside one guarded session on
29
+ `epic/<id>`.
30
+
31
+ The **least-surface insight** (adopted design): in `deliver-epic.md` only
32
+ **Phases 1–2 are Story-tier-specific**. **Phases 3–9 are Epic-scope** — they
33
+ operate on `epic/<id>` + the Epic body's `## Acceptance Table` and dereference
34
+ **no** Story ticket (close-validation, epic-audit lens roster, code-review over
35
+ the cumulative `main..epic/<id>` diff, retro, integration gate, finalize →
36
+ reconcile → the merge machine). So:
37
+
38
+ ```text
39
+ single-delivery = deliver-epic with Phases 1–2 replaced by an in-session
40
+ slice walk (S1/S2/S2a below), and Phases 3–9 reused
41
+ byte-for-byte (S2b handoff).
42
+ ```
43
+
44
+ ```text
45
+ /deliver <epicId> (route: single)
46
+ → S1 Prepare (epic-deliver-prepare.js --single — slice map, ONE worktree)
47
+ → S2 Slice walk (walk ## Delivery Slicing in order; commit + flip each slice on epic/<id>)
48
+ → S2a Acceptance (per-AC-cluster fresh-context critics over the cumulative diff)
49
+ → S2b Handoff → deliver-epic.md Phase 3 … Phase 9 (merge tail, unchanged)
50
+ ```
51
+
52
+ ---
53
+
54
+ ## Contract
55
+
56
+ - **One long guarded session.** No Story fan-out, no `Agent` dispatch for
57
+ implementation — you (the LLM running this helper) implement each slice
58
+ in-session on `epic/<id>`. The per-AC-cluster acceptance critics (S2a) are
59
+ the **only** `Agent` spawns, and they are read-only maker-blind critics.
60
+ - **Idempotent by checkpoint.** The `epic-run-state` slice map
61
+ (`slices[id].status ∈ {pending, done, blocked, failed}`) is the resume
62
+ target: a re-run **skips `done` slices** — the work already sits on
63
+ `epic/<id>`, so it is never re-paid.
64
+ - **Single pause point.** Only `agent::blocked` halts. If a slice or an
65
+ acceptance cluster cannot be satisfied, flip the Epic to `agent::blocked`,
66
+ post a friction comment, and park — never fall silent, never proceed to
67
+ close with an unmet AC.
68
+ - **No Story-ticket dereference (audit receipt).** Nothing in this path reads,
69
+ enumerates, or transitions a `type::story` ticket. The leaf unit is a
70
+ `## Delivery Slicing` slice; the acceptance unit is an `## Acceptance Table`
71
+ AC cluster; the merge tail is Epic-scope. This is the load-bearing property
72
+ that lets Phases 3–9 be reused unchanged.
73
+ - **Land or block (issue #4483).** The worktree / `epic/<id>` branch / PR path
74
+ is the ONLY sanctioned delivery mechanism. Executing slices inline in the
75
+ main checkout or committing to local `main` is forbidden regardless of how
76
+ the environment looks. Prepare surfaces `remoteVerified`; on `false` flip to
77
+ `agent::blocked` quoting `remoteProbe.detail` and halt.
78
+
79
+ ---
80
+
81
+ ## S1 — Prepare the single run
82
+
83
+ ```bash
84
+ node .agents/scripts/epic-deliver-prepare.js --epic <epicId> --single [--steal] [--as <handle>]
85
+ ```
86
+
87
+ `--single` short-circuits Story enumeration
88
+ ([`runEpicDeliverPrepareSingle`](../../scripts/epic-deliver-prepare.js)). It:
89
+
90
+ 1. **Refuses `acceptance::n-a`** (fail-closed front gate). Under single
91
+ delivery the non-waivable epic-level acceptance reconcile is the ONLY
92
+ acceptance gate, so an Epic declaring "no acceptance criteria" is
93
+ structurally incoherent — prepare throws. (The back gate is S2b's
94
+ reconcile; see [Non-waivable reconcile](#non-waivable-acceptance-reconcile).)
95
+ 2. Runs the same fail-closed preflight guards as the fan-out prepare
96
+ (checkout-safety + Epic lease).
97
+ 3. Seeds `epic/<id>` and materializes **ONE** worktree at
98
+ `.worktrees/epic-<epicId>/` on it.
99
+ 4. Parses the Epic body's `## Delivery Slicing` table and writes the
100
+ `epic-run-state` **slice map** (`deliveryShape: "single"`, `storyCount: 0`,
101
+ `concurrencyCap: 1`) — idempotent + resume-preserving (a re-run keeps every
102
+ already-`done` slice).
103
+ 5. Writes the per-Epic docs digest.
104
+
105
+ Treat the printed JSON as `state`:
106
+ `{ epicId, deliveryShape, storyCount: 0, sliceCount, slices, epicBranch, workCwd, worktreeCreated, checkpointInitializedAt, docsDigestPath }`.
107
+ Flip the Epic to `agent::executing` (idempotent) after the CLI returns.
108
+
109
+ > **`sliceCount === 0`.** A missing / unparseable `## Delivery Slicing` table
110
+ > yields an empty slice map — there is nothing to walk. That is a plan-quality
111
+ > defect: post a friction comment, flip to `agent::blocked`, and halt (do NOT
112
+ > proceed to an empty close).
113
+
114
+ ### S2 — `cd` into the worktree (absolute-path discipline)
115
+
116
+ ```bash
117
+ cd "<workCwd from S1>"
118
+ ```
119
+
120
+ All subsequent commands run from this directory.
121
+
122
+ > **Worktree scope is not just the Bash cwd.** `cd <workCwd>` steers the Bash
123
+ > tool's cwd but does **not** scope the path-based Edit/Write/Read tools — you
124
+ > MUST prefix every such path with the absolute `workCwd` root or risk
125
+ > silently editing the main checkout (same discipline as
126
+ > [`single-story-deliver.md`](single-story-deliver.md) Step 0.5).
127
+
128
+ ---
129
+
130
+ ## S2 — Walk the Delivery Slicing table in order
131
+
132
+ Read the Epic body's `## Delivery Slicing` table. Walk it **strictly in
133
+ order** as an in-session checklist. The ordered slicing **is** the audit trail
134
+ (the plan authored no tickets), and the ordering encodes the dependency chain
135
+ — never reorder or parallelize.
136
+
137
+ For each slice `slice-<n>` (1-based, matching the checkpoint's slice-map keys),
138
+ **skip it when `slices[slice-<n>].status === "done"`** (resume: the work is
139
+ already on `epic/<id>`). Otherwise:
140
+
141
+ 1. **Announce the slice.** Emit `slice.start` on the ledger. This same
142
+ boundary call also exports the active-slice env
143
+ (`CC_EPIC_ID` / `CC_SLICE_ID`) into the worktree's `.env.local`, which
144
+ arms the PostToolUse hook to emit `slice.heartbeat` for the rest of this
145
+ slice (below):
146
+
147
+ ```bash
148
+ node <main-repo>/.agents/scripts/slice-phase.js \
149
+ --epic <epicId> --slice slice-<n> --event start --slice-index <n-1> --title "<slice label>"
150
+ ```
151
+
152
+ 2. **Implement the slice** on `epic/<id>` in the worktree. Apply the relevant
153
+ [skills](../../instructions.md) and local-lens concerns for the slice's
154
+ footprint. You do **NOT** run a per-step `slice.heartbeat` CLI (Epic
155
+ #4476): once `--event start` armed the active-slice env, the PostToolUse
156
+ hook emits a throttled `slice.heartbeat` off the token stream as a free
157
+ byproduct of every tool call, so the `/deliver` §2e Idle Watchdog can tell
158
+ this one long session from a dead one without a dedicated bookkeeping turn.
159
+ (The `slice-phase.js --event heartbeat` CLI remains available for an
160
+ explicit beat if you deliberately want one, e.g. a long non-tool wait.)
161
+
162
+ 3. **Commit** the slice to `epic/<id>` before the next slice
163
+ (conventional subject, `(refs #<epicId>)`). Run the advisory quick gates
164
+ (`lint`, `typecheck`) while iterating so drift surfaces early.
165
+
166
+ 4. **Flip the marker + close the slice.** After the commit lands, flip the
167
+ checkpoint marker `pending → done` and emit `slice.end`:
168
+
169
+ ```bash
170
+ node <main-repo>/.agents/scripts/slice-phase.js \
171
+ --epic <epicId> --slice slice-<n> --event end --outcome done --record done
172
+ ```
173
+
174
+ (`--record done` splices `slices[slice-<n>].status = "done"` via
175
+ [`recordSliceStatus`](../../scripts/lib/orchestration/epic-run-state-store.js)
176
+ so a resumed run skips this slice.)
177
+
178
+ If a slice cannot be completed, emit `slice.end --outcome blocked
179
+ --record blocked`, post a friction comment, flip the Epic to `agent::blocked`,
180
+ and park.
181
+
182
+ When every slice is `done`, proceed to S2a.
183
+
184
+ ---
185
+
186
+ ## S2a — Per-AC-cluster acceptance critics (the acceptance-dilution guard)
187
+
188
+ **This is the load-bearing risk mitigation.** In the fan-out shape each Story
189
+ ran its own fresh-context acceptance self-eval critic, so acceptance coverage
190
+ was distributed across the Story tree for free. Collapsing to one session would
191
+ degrade that to two redraft rounds scoring every AC at once — the "acceptance
192
+ dilution" risk the design calls out as blocking. The fix restores the
193
+ distributed critic count.
194
+
195
+ 1. **Cluster the ACs.** Parse the Epic body's `## Acceptance Table` AC ids
196
+ (`AC-<n>`), ordered by their associated Delivery-Slicing slice, then split
197
+ into clusters of at most `delivery.acceptanceEval.clusterCeiling` (default
198
+ **4**, hard-clamped to `[1, 8]`) via
199
+ [`clusterAcceptanceCriteria`](../../scripts/lib/orchestration/acceptance-clusters.js).
200
+ The result is **exactly `ceil(totalACs / clusterCeiling)`** clusters — the
201
+ fan-out width.
202
+
203
+ 2. **One maker-blind critic per cluster.** For **each** cluster author **one**
204
+ maker-blind verdict — NOT a continuation of your implementing turn, so the
205
+ critic does not grade its own homework. There MUST be
206
+ **exactly `ceil(totalACs / clusterCeiling)`** critic passes — **one per
207
+ cluster** — never a single critic over all ACs (that is the collapse this
208
+ guard forecloses). Each critic runs the shared
209
+ [`acceptance-self-eval.md`](acceptance-self-eval.md) mechanic scoped to its
210
+ cluster: it scores **that cluster's ACs** against the cumulative
211
+ `git diff main...epic/<epicId>` diff (plus any `verify[]` evidence the ACs
212
+ reference) and writes a verdict file under `temp/` conforming to
213
+ [`acceptance-eval-verdict.schema.json`](../../schemas/acceptance-eval-verdict.schema.json).
214
+
215
+ > **Fresh-vs-inline is risk-routed PER CLUSTER — the count is fixed
216
+ > (Epic #4478, M7-B).** Whether a given cluster's critic runs as a
217
+ > fresh-context spawn or inline is resolved per cluster by
218
+ > `resolveCeremonyForRisk`
219
+ > ([`ceremony-routing.js`](../../scripts/lib/orchestration/ceremony-routing.js))
220
+ > from the Epic's `planningRisk.overallLevel` and
221
+ > `delivery.routing.freshCriticSampleRate`: **`high`/`medium` → fresh spawn**,
222
+ > **`low` → inline** (except the sampling-floor fraction forced fresh),
223
+ > **missing/unknown → fresh + full ceremony**. This is **strictly** a
224
+ > fresh-vs-inline choice **per cluster**: the number of clusters — hence the
225
+ > number of critic passes and verdicts — stays **exactly**
226
+ > `ceil(totalACs / clusterCeiling)` under **every** risk level. Risk routing
227
+ > NEVER re-slices, merges, or drops a cluster.
228
+ >
229
+ > - **Fresh** → emit a **separate** `Agent` tool call
230
+ > (`subagent_type: acceptance-critic` when
231
+ > `delivery.routing.roleScopedAgents` is on — the default — else
232
+ > `general-purpose`), maker-blind, scoped to that cluster's ACs.
233
+ > - **Inline** → author that cluster's verdict inline in a deliberately
234
+ > scoped self-critical pass (re-read only the diff + that cluster's ACs,
235
+ > treat the implementation reasoning as untrusted). The gate, schema, and
236
+ > proceed/redraft/block decision are identical.
237
+ > - **Nesting-absent fallback.** If the host cannot spawn a nested `Agent` at
238
+ > this depth at all, author **every** cluster's verdict inline regardless of
239
+ > the risk verdict. Note the fallback in any block/friction comment.
240
+
241
+ 3. **Gate each cluster (Epic-scoped).** Run the gate per cluster:
242
+
243
+ ```bash
244
+ node <main-repo>/.agents/scripts/acceptance-eval.js \
245
+ --epic <epicId> --cluster <clusterId> --verdict <verdict-path>
246
+ ```
247
+
248
+ The Epic-scoped invocation (no `--story`) scopes the redraft-round count
249
+ **per cluster** off the Epic's `signals.ndjson`. It exits:
250
+ - **`proceed`** (every AC in the cluster `met`) → this cluster is clear.
251
+ - **`redraft`** (some `partial`/`unmet`, rounds remain) → redraft the
252
+ flagged ACs on `epic/<id>`, commit, and re-run this cluster's critic.
253
+ - **`block`** (round cap reached, ACs still unmet) → exit non-zero. **STOP
254
+ the whole run.** Flip the Epic to `agent::blocked` and post a friction
255
+ comment naming the unmet ACs (with their cluster) and their evidence.
256
+ **Never proceed to close (S2b) with any cluster blocked.**
257
+
258
+ 4. Only when **every** cluster returns `proceed` do you proceed to S2b.
259
+
260
+ ---
261
+
262
+ ## S2b — Hand off to deliver-epic.md Phase 3 (merge tail, reused unchanged)
263
+
264
+ Everything downstream of the acceptance gate is Epic-scope and reused
265
+ **byte-for-byte** — there is exactly one home for the merge machinery. Continue
266
+ at [`deliver-epic.md`](deliver-epic.md) **Phase 3** and run **Phases 3–9
267
+ verbatim**:
268
+
269
+ - Phase 3 — close-validation (lint + test + ratchets on `epic/<id>`)
270
+ - Phase 4 — epic-close lens roster
271
+ - Phase 5 — code-review (the ONE maker-blind cumulative-diff review — the
272
+ north-star's "review is a fresh-context critic")
273
+ - Phase 6 — retro
274
+ - Phase 6.5 — integration gate
275
+ - Phase 7 — finalize (branch-sync → `epic.close.end` → reconcile → open PR)
276
+ - Phase 8 — watch-and-iterate to green CI
277
+ - Phase 8.5 — auto-merge gate
278
+ - Phase 9 — cleanup
279
+
280
+ Do **not** copy those phases here. Read `deliver-epic.md` from Phase 3 and run
281
+ them exactly as the fan-out route does — they never touch a Story ticket, so
282
+ they compose unchanged over the slice-walked `epic/<id>` branch.
283
+
284
+ ### Non-waivable acceptance reconcile
285
+
286
+ Phase 7's `epic.close.end` fires the bus-owned `AcceptanceReconciler`. Under
287
+ single delivery this listener is **non-waivable** (design §2c, the back gate):
288
+ [`acceptance-reconciler.js`](../../scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js)
289
+ resolves the delivery shape (off `epic-run-state.deliveryShape` or the
290
+ `delivery::single` label) and treats a `status: "waived"` reconcile as
291
+ **`failed`** (`acceptance.reconcile.failed` → `epic.blocked`) instead of
292
+ passing it through. Combined with the S1 front gate (prepare refusing
293
+ `acceptance::n-a`), this structurally forecloses shipping a single Epic with
294
+ its sole acceptance gate waived.
295
+
296
+ ---
297
+
298
+ ## Idempotence and resume
299
+
300
+ Re-runs resume from the slice map: `done` slices are skipped, `pending` slices
301
+ resume in order. The per-cluster acceptance signals on the Epic stream let a
302
+ resumed run pick up the correct redraft round per cluster. The PR from Phase 7
303
+ is updated in place. `slice.start` / `slice.end` / `slice.heartbeat` on the
304
+ ledger give the resume path and the idle watchdog an inspectable forward-
305
+ progress signal for the single long session.
306
+
307
+ ---
308
+
309
+ ## Constraints
310
+
311
+ - **Never** fan out `Agent` calls for slice implementation — the slice walk is
312
+ in-session. The only `Agent` spawns are the S2a maker-blind acceptance
313
+ critics (read-only), and only for clusters the risk router sends `fresh`.
314
+ - **Never** dereference, enumerate, or transition a `type::story` ticket —
315
+ this path has none.
316
+ - **Never** collapse the S2a critics to fewer than
317
+ `ceil(totalACs / clusterCeiling)` **critic passes** — one maker-blind verdict
318
+ per cluster (fresh spawn or inline; the risk router chooses the mode, never
319
+ the count) — and **never** proceed to S2b with a blocked cluster.
320
+ - **Never** merge `epic/<epicId>` to `main` outside Phase 8.5 — the merge tail
321
+ is `deliver-epic.md`'s, unchanged.
322
+ - **Always** flip the slice marker `→ done` after each slice commits, and
323
+ **always** post a friction comment before any non-`complete` outcome.
324
+
325
+ ## See also
326
+
327
+ - [`deliver.md`](../deliver.md) — the router that dispatches here.
328
+ - [`deliver-epic.md`](deliver-epic.md) — the fan-out path; its Phases 3–9 are
329
+ this helper's merge tail.
330
+ - [`acceptance-self-eval.md`](acceptance-self-eval.md) — the per-cluster critic
331
+ mechanic.
@@ -105,7 +105,9 @@ Every other runtime modifier is sourced from the Epic's labels or from
105
105
  clarifying questions — if stuck, flip to `agent::blocked`, post a
106
106
  friction comment, park.
107
107
  - **Flat Story dispatch by design.** Host LLM fans out per-Story Agent
108
- calls directly with `subagent_type: general-purpose`. Keeping Story
108
+ calls directly with `subagent_type: story-worker` when
109
+ `delivery.routing.roleScopedAgents` is on (the default; § 2b), else
110
+ `general-purpose`. Keeping Story
109
111
  dispatch flat — the host owns the single fan-out level — is a
110
112
  **design choice**, not a harness constraint: the wave aggregator, idle
111
113
  watchdog, and merge-lock all assume one host-owned dispatch level. As of
@@ -338,8 +340,18 @@ span-tree view.
338
340
  `helpers/epic-deliver-story` yourself. Emit **one `Agent` tool call per
339
341
  Story** in `nextAction.stories` (even when `length === 1` — the
340
342
  parent-child boundary keeps the return-parser uniform). The *children*
341
- run [`helpers/epic-deliver-story`](epic-deliver-story.md). Use
342
- `subagent_type: general-purpose`.
343
+ run [`helpers/epic-deliver-story`](epic-deliver-story.md).
344
+
345
+ **Sub-agent type (Epic #4478, M7-B).** When
346
+ `delivery.routing.roleScopedAgents` is enabled (the **default**), each Story
347
+ child is dispatched with `subagent_type: story-worker` — it boots on the
348
+ role-scoped [`story-worker`](../../agents/story-worker.md) context (its own
349
+ system prompt, no `CLAUDE.md` @-closure) that carries every load-bearing
350
+ delivery MUST standalone, so the spawn stops re-paying the always-loaded
351
+ context. When the kill-switch is **off**
352
+ (`delivery.routing.roleScopedAgents: false`), fall back to
353
+ `subagent_type: general-purpose` — the instant, code-rollback-free revert and
354
+ the escape for hosts that ignore `.claude/agents/`.
343
355
 
344
356
  Emit **one assistant turn** with **N parallel `Agent` calls** where
345
357
  `N === nextAction.stories.length` (the ready set is already capped at
@@ -388,11 +400,15 @@ transition to `agent::blocked` and exit if stuck), (4) tell the child to
388
400
  suppress per-Story chat relay and instead relay **one line per phase
389
401
  transition** (e.g. `Story #<id>: implementing → closing`) — the child's
390
402
  authoritative progress lands in the `story-run-progress` snapshot the
391
- `story-phase.js` CLI upserts, not in a verbatim body dump, (5) require the
392
- child to emit a `story.heartbeat` lifecycle event at least once per
393
- Story-level phase transition via `node .agents/scripts/story-phase.js` (or
394
- whenever it stalls on a long-running step), and if it cannot make progress
395
- to transition to `agent::blocked` rather than fall silent, and (6) pass the
403
+ `story-phase.js` CLI upserts, not in a verbatim body dump, (5) remind the
404
+ child that its `story.heartbeat` liveness signal is emitted **off the token
405
+ stream** by the PostToolUse hook (Epic #4476) — a throttled heartbeat lands as
406
+ a free byproduct of every tool call, keyed off the active-Story env
407
+ `story-init.js` exported so it does **not** run `story-phase.js` per step
408
+ just to heartbeat (a `story-phase.js` call at a genuine Story-level phase
409
+ transition still renders the snapshot and stamps an operator-bearing
410
+ heartbeat), and if it cannot make progress it must transition to
411
+ `agent::blocked` rather than fall silent, and (6) pass the
396
412
  **docs digest path** — the `docsDigestPath` field from the
397
413
  `epic-deliver-prepare.js` envelope (§ Phase 1 main), which points at
398
414
  `temp/epic-<epicId>/docs-digest.md`. Instruct the child to read that
@@ -730,6 +746,33 @@ accessor (default `true`); do not read `delivery.ci.earlyPr` directly.
730
746
  In both modes the PR title/body contract (`feat: Epic #<epicId>` /
731
747
  `Closes #<epicId>`) is identical.
732
748
 
749
+ **Drain the bookkeeping outbox (Epic #4476 M5).** When this run was invoked
750
+ with `--yes` (headless), the mechanical bookkeeping — non-urgent
751
+ structured-comment upserts and intermediate `agent::*` label flips — was
752
+ **buffered to a local per-Epic outbox** instead of posting live per
753
+ transition (see the box below). Reconcile it to GitHub once, now, after the PR
754
+ is open:
755
+
756
+ ```bash
757
+ node .agents/scripts/bookkeeping-reconcile.js --epic <epicId>
758
+ ```
759
+
760
+ This drains `temp/epic-<epicId>/bookkeeping-outbox.ndjson` (FIFO,
761
+ idempotent) so GitHub is the source of truth at rest. It exits non-zero and
762
+ **retains** the outbox if any op fails (crash-recovery: a later reconcile
763
+ re-drains the remainder). Attended runs buffered nothing, so this is a no-op.
764
+
765
+ > **Headless buffering, not silence (§1.H / §1.J).** In `--yes` mode the
766
+ > comment/label CLIs accept `--buffer --epic <epicId>` to enqueue a mutation
767
+ > instead of a live round-trip
768
+ > ([`post-structured-comment.js`](../../scripts/post-structured-comment.js),
769
+ > [`update-ticket-state.js`](../../scripts/update-ticket-state.js)). The
770
+ > `agent::blocked` HITL gate is **never** buffered — a genuine blocker flips
771
+ > the label live and immediately, exactly as before, so the operator can see
772
+ > and resume it. `agent::done` is also never buffered (its cascade runs live).
773
+ > Attended runs pass no `--buffer` and behave byte-for-byte as they always
774
+ > have.
775
+
733
776
  See
734
777
  [`deliver-epic-reference.md` § Phase 7 — Finalize](deliver-epic-reference.md#phase-7--finalize-close-tail-listener-chain)
735
778
  for the branch-sync outcome table (conflict / fetch-failed recovery) and the
@@ -249,7 +249,17 @@ Each Agent call:
249
249
  4. Requests the child suppress per-phase chat relay and include its
250
250
  **terminal** `renderedBody` in the JSON return.
251
251
 
252
- Use `subagent_type: general-purpose`.
252
+ **Sub-agent type (Epic #4478, M7-B).** When `delivery.routing.roleScopedAgents`
253
+ is enabled (the **default**), dispatch each Story child with
254
+ `subagent_type: story-worker` — it boots on the role-scoped
255
+ [`story-worker`](../../agents/story-worker.md) context (its own system prompt,
256
+ no `CLAUDE.md` @-closure) which carries every load-bearing delivery MUST
257
+ standalone, so the spawn stops re-paying the always-loaded context. When the
258
+ kill-switch is **off** (`delivery.routing.roleScopedAgents: false`), fall back
259
+ to `subagent_type: general-purpose` — the instant, code-rollback-free revert and
260
+ the universal escape for hosts that ignore `.claude/agents/`. The child still
261
+ runs [`helpers/epic-deliver-story`](epic-deliver-story.md) either way; only the
262
+ boot context differs.
253
263
 
254
264
  ### 2b′. Collect each return and re-tick
255
265
 
@@ -376,10 +386,10 @@ Story's suite is green and the close-validation gates already pass — and
376
386
  key is unset or `false`, story-deliver behaves exactly as documented above
377
387
  and this stage is skipped entirely.
378
388
 
379
- The stage adopts the [`refactorer`](../../personas/refactorer.md) persona and the
380
- [`core/refactoring-discipline`](../../skills/core/refactoring-discipline/SKILL.md)
381
- skill to drive a behaviour-preserving pass that lowers CRAP and removes
382
- duplication on the files the Story already touched:
389
+ The stage applies the post-green refactor discipline in
390
+ [`core/code-review-and-quality`](../../skills/core/code-review-and-quality/SKILL.md)
391
+ Post-Green Refactor Pass) to drive a behaviour-preserving pass that lowers
392
+ CRAP and removes duplication on the files the Story already touched:
383
393
 
384
394
  - **Post-green only.** It runs after the suite is green and the gates pass —
385
395
  never from red, and never to make a failing test pass.
@@ -13,9 +13,10 @@ caller: epic-deliver.md
13
13
 
14
14
  > **Not a slash command.** This file lives in `helpers/` and is not projected
15
15
  > into the mandrel plugin command tree. It is invoked exclusively by the
16
- > [`/deliver`](deliver-epic.md) per-wave fan-out via an `Agent` tool
17
- > call (`subagent_type: general-purpose`). Run `/deliver <epicId>` from
18
- > the operator surface, not this helper directly.
16
+ > [`/deliver`](deliver-epic.md) per-wave fan-out via an `Agent` tool call —
17
+ > `subagent_type: story-worker` when `delivery.routing.roleScopedAgents` is on
18
+ > (the default), else `subagent_type: general-purpose` (Epic #4478, M7-B). Run
19
+ > `/deliver <epicId>` from the operator surface, not this helper directly.
19
20
 
20
21
  ## Overview
21
22
 
@@ -161,6 +162,14 @@ Run a single Story-implementation phase against the inline `acceptance[]`
161
162
  --phase implementing
162
163
  ```
163
164
 
165
+ > **Liveness between transitions is free (Epic #4476).** These
166
+ > `story-phase.js` calls fire only at genuine phase transitions (a few per
167
+ > Story) and each renders the snapshot + stamps an operator-bearing
168
+ > `story.heartbeat`. You do **not** add extra `story-phase.js` calls
169
+ > mid-`implementing` just to prove you are alive: the PostToolUse hook emits
170
+ > a throttled `story.heartbeat` off the token stream on every tool call, so
171
+ > the §2e Idle Watchdog sees continuous forward progress for free.
172
+
164
173
  2. Read the Story body's inline `acceptance[]` and `verify[]` arrays
165
174
  from the `story-init` structured comment (`context.acceptance`,
166
175
  `context.verify`). Treat the acceptance items as the contract and
@@ -55,7 +55,7 @@ keeps that file aligned with the schema and the runtime accessors so the
55
55
  > `applyDefaults`. Writing those defaults into `.agentrc.json` only bloats
56
56
  > the consumer repo's config diff without changing runtime behaviour.
57
57
  >
58
- > **Persona**: `devops-engineer` · **Skills**: `core/ci-cd-and-automation`,
58
+ > **Persona**: `devops-engineer` · **Skills**: `core/gates-and-baselines`,
59
59
  > `core/documentation-and-adrs`
60
60
 
61
61
  ## Procedure
@@ -189,31 +189,33 @@ markdown joins the same batch). Never write them one-per-turn.
189
189
 
190
190
  ### Conditional critics (between authoring and gate #2)
191
191
 
192
- The evaluation is folded into `plan-persist.js` as a deterministic
193
- pre-write phase (#4496 fix 6) — the persist prints both verdicts, returns
194
- them on its result envelope, and ledger-logs every skip. **Attended runs**
195
- evaluate the dispatch conditions before gate #2 so the verdicts fold into
196
- its view one git-local CLI call, zero GitHub reads, via the thin shim
197
- (kept one release over the shared evaluator):
198
-
199
- ```bash
200
- node .agents/scripts/plan-critics.js --epic [Epic_ID]
201
- ```
202
-
203
- (ideation: pass `--tech-spec`/`--risk-verdict`/`--tickets` explicitly). The
204
- verdict names each critic with `dispatch: true|false` and reasons. Dispatch
205
- a sub-agent ONLY for a critic with `dispatch: true`; surface each skip as a
206
- one-line note. Every skip decision is appended to the plan-metrics ledger
207
- (`kind: "critic-skip"`, with reasons) so under-firing is auditable — the
192
+ The dispatch decision is a **deterministic** function of the authored
193
+ artifacts, evaluated in exactly one place — the evaluation is
194
+ folded into `plan-persist.js` as a deterministic pre-write phase
195
+ (#4496 fix 6). The persist prints both verdicts, returns them on its
196
+ result envelope, and ledger-logs every skip: that fold is the
197
+ single authoritative record, headless or attended. **Attended runs**
198
+ apply the same deterministic conditions (below) to the authored
199
+ artifacts before gate #2 so a `dispatch: true` critic runs and its
200
+ findings fold into the gate's view; the persist fold then re-checks
201
+ and ledger-logs the identical verdict. No separate CLI turn and zero
202
+ GitHub reads — the conditions are mechanical thresholds over
203
+ `techspec.md` / `risk-verdict.json` / `tickets.json`, and are
204
+ never judged inline as an opinion. The verdict names each critic
205
+ with `dispatch: true|false` and reasons.
206
+
207
+ For each critic whose conditions hold (`dispatch: true`), dispatch a
208
+ sub-agent; surface each skip as a one-line note. Every skip decision
209
+ is appended to the plan-metrics ledger (`kind: "critic-skip"`, with
210
+ reasons) so under-firing is auditable — the
208
211
  persist validators remain unchanged hard gates either way.
209
212
 
210
- > **`--yes` (headless).** Do **not** run the standalone CLI and do **not**
211
- > dispatch critic sub-agents: the critics' findings would fold into a gate
212
- > that auto-proceeds, so a report nobody reviews is pure spend. The
213
- > persist's folded pre-write evaluation is the audit record — its verdicts
214
- > print in the persist output, a `dispatch: true` verdict surfaces as a
215
- > one-line advisory note in the run summary, and every skip still lands on
216
- > the plan-metrics ledger.
213
+ > **`--yes` (headless).** Do **not** dispatch critic sub-agents: the
214
+ > critics' findings would fold into a gate that auto-proceeds, so a report
215
+ > nobody reviews is pure spend. The persist's folded pre-write evaluation is
216
+ > the audit record — its verdicts print in the persist output, a
217
+ > `dispatch: true` verdict surfaces as a one-line advisory note in the run
218
+ > summary, and every skip still lands on the plan-metrics ledger.
217
219
 
218
220
  Both critics are **fresh-context sub-agents** (`Agent` tool,
219
221
  `subagent_type: general-purpose`) — never inline skill activations, so they
@@ -32,7 +32,7 @@ generated `.claude/commands/` tree is
32
32
  the CLI's sync step.
33
33
 
34
34
  > **Persona**: `devops-engineer` · **Skills**:
35
- > `core/ci-cd-and-automation`, `core/documentation-and-adrs`
35
+ > `core/gates-and-baselines`, `core/documentation-and-adrs`
36
36
 
37
37
  ## Step 0 — Detect the install state and pick the invocation form
38
38
 
package/docs/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [1.94.0](https://github.com/dsj1984/mandrel/compare/mandrel-v1.93.0...mandrel-v1.94.0) (2026-07-13)
6
+
7
+
8
+ ### Added
9
+
10
+ * context diet — extract instructions core + inert role-agent scaffolding (refs [#4478](https://github.com/dsj1984/mandrel/issues/4478)) ([#4509](https://github.com/dsj1984/mandrel/issues/4509)) ([2796e57](https://github.com/dsj1984/mandrel/commit/2796e57b7a0fb0a0651b1e184d6945546b9da22c))
11
+ * **deliver:** move bookkeeping off the token stream — hook heartbeats + buffered finalize sync ([#4476](https://github.com/dsj1984/mandrel/issues/4476)) ([#4508](https://github.com/dsj1984/mandrel/issues/4508)) ([378da81](https://github.com/dsj1984/mandrel/commit/378da818e18f4b09aa9380a4413b753748661aa2))
12
+ * **deliver:** single-delivery executor as the live default (refs [#4475](https://github.com/dsj1984/mandrel/issues/4475)) ([#4503](https://github.com/dsj1984/mandrel/issues/4503)) ([c532d41](https://github.com/dsj1984/mandrel/commit/c532d412f785c212223a3ac9af08fce7dad284aa))
13
+ * **deliver:** single-delivery routing + kill-switch + prepare --single (refs [#4475](https://github.com/dsj1984/mandrel/issues/4475)) ([#4501](https://github.com/dsj1984/mandrel/issues/4501)) ([b481caf](https://github.com/dsj1984/mandrel/commit/b481cafc618a78b80d099cf697e2c094544835b6))
14
+ * flip role-scoped spawns behind kill-switch + risk-routed ceremony (refs [#4478](https://github.com/dsj1984/mandrel/issues/4478)) ([#4510](https://github.com/dsj1984/mandrel/issues/4510)) ([adb9d52](https://github.com/dsj1984/mandrel/commit/adb9d52d7c7be213d32afbe86ec2edad88e01619))
15
+
16
+
17
+ ### Fixed
18
+
19
+ * **close-validate:** probe the check-baselines consumer contract before failing (refs [#4495](https://github.com/dsj1984/mandrel/issues/4495)) ([#4499](https://github.com/dsj1984/mandrel/issues/4499)) ([49b430f](https://github.com/dsj1984/mandrel/commit/49b430fe2811000b69e97201efc83bff64651726))
20
+
5
21
  ## [1.93.0](https://github.com/dsj1984/mandrel/compare/mandrel-v1.92.0...mandrel-v1.93.0) (2026-07-12)
6
22
 
7
23