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.
- package/.agents/agents/acceptance-critic.md +129 -0
- package/.agents/agents/retro.md +42 -0
- package/.agents/agents/story-worker.md +162 -0
- package/.agents/docs/configuration.md +7 -1
- package/.agents/docs/execution-reference.md +27 -2
- package/.agents/instructions.md +43 -33
- package/.agents/personas/engineer.md +26 -112
- package/.agents/personas/security-engineer.md +1 -2
- package/.agents/rules/git-conventions-reference.md +225 -0
- package/.agents/rules/git-conventions.md +25 -200
- package/.agents/rules/security-baseline.md +5 -0
- package/.agents/rules/testing-standards.md +106 -13
- package/.agents/schemas/agentrc.schema.json +31 -1
- package/.agents/schemas/lifecycle/slice.end.schema.json +21 -0
- package/.agents/schemas/lifecycle/slice.heartbeat.schema.json +20 -0
- package/.agents/schemas/lifecycle/slice.start.schema.json +17 -0
- package/.agents/scripts/acceptance-eval.js +62 -18
- package/.agents/scripts/agents-bootstrap-github.js +1 -1
- package/.agents/scripts/bookkeeping-reconcile.js +117 -0
- package/.agents/scripts/check-context-budget.js +62 -5
- package/.agents/scripts/diagnose-friction.js +0 -6
- package/.agents/scripts/epic-deliver-prepare.js +272 -10
- package/.agents/scripts/lib/bootstrap/project-bootstrap.js +56 -18
- package/.agents/scripts/lib/close-validation/gates.js +159 -21
- package/.agents/scripts/lib/config/acceptance-eval.js +52 -5
- package/.agents/scripts/lib/config/delivery-routing.js +87 -0
- package/.agents/scripts/lib/config/explain.js +2 -0
- package/.agents/scripts/lib/config-resolver.js +1 -1
- package/.agents/scripts/lib/config-settings-schema-delivery.js +37 -3
- package/.agents/scripts/lib/config-settings-schema-quality.js +9 -0
- package/.agents/scripts/lib/doc-tiers.js +37 -2
- package/.agents/scripts/lib/observability/active-story-env.js +111 -2
- package/.agents/scripts/lib/observability/hook-heartbeat.js +219 -0
- package/.agents/scripts/lib/observability/tool-trace-hook.js +15 -4
- package/.agents/scripts/lib/orchestration/acceptance-clusters.js +111 -0
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +32 -4
- package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +270 -0
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +141 -0
- package/.agents/scripts/lib/orchestration/context-hydration-engine.js +3 -124
- package/.agents/scripts/lib/orchestration/deliver-route.js +173 -0
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/authoring-context.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-run-state-store.js +233 -0
- package/.agents/scripts/lib/orchestration/lifecycle/emit-slice-lifecycle.js +270 -0
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +83 -2
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/checkpoint-pointer-writer.js +6 -0
- package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +3 -2
- package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +1 -0
- package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +1 -0
- package/.agents/scripts/lib/orchestration/ticket-validator.js +1 -1
- package/.agents/scripts/lib/provider-factory.js +1 -1
- package/.agents/scripts/lib/templates/decomposer-prompts.js +1 -1
- package/.agents/scripts/post-structured-comment.js +38 -0
- package/.agents/scripts/slice-phase.js +361 -0
- package/.agents/scripts/sync-claude-agents.js +165 -0
- package/.agents/scripts/update-ticket-state.js +31 -0
- package/.agents/scripts/wave-tick.js +138 -9
- package/.agents/skills/core/api-and-interface-design/SKILL.md +5 -3
- package/.agents/skills/core/code-review-and-quality/SKILL.md +63 -7
- package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +1 -1
- package/.agents/skills/core/epic-plan-consolidate/SKILL.md +5 -5
- package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +8 -8
- package/.agents/skills/core/epic-plan-premortem/SKILL.md +4 -4
- package/.agents/skills/core/epic-plan-spec-author/SKILL.md +26 -56
- package/.agents/skills/core/gates-and-baselines/SKILL.md +149 -0
- package/.agents/skills/core/idea-refinement/SKILL.md +2 -8
- package/.agents/skills/core/qa-coverage-mapping/SKILL.md +7 -7
- package/.agents/skills/skills.index.json +11 -381
- package/.agents/workflows/deliver.md +47 -4
- package/.agents/workflows/helpers/acceptance-self-eval.md +38 -13
- package/.agents/workflows/helpers/deliver-epic-reference.md +18 -5
- package/.agents/workflows/helpers/deliver-epic-single.md +331 -0
- package/.agents/workflows/helpers/deliver-epic.md +51 -8
- package/.agents/workflows/helpers/deliver-stories.md +15 -5
- package/.agents/workflows/helpers/epic-deliver-story.md +12 -3
- package/.agents/workflows/helpers/mandrel-sync-config.md +1 -1
- package/.agents/workflows/helpers/plan-epic.md +25 -23
- package/.agents/workflows/mandrel-update.md +1 -1
- package/docs/CHANGELOG.md +16 -0
- package/lib/cli/registry.js +95 -0
- package/package.json +4 -2
- package/.agents/personas/engineer-mobile.md +0 -120
- package/.agents/personas/engineer-web.md +0 -111
- package/.agents/personas/product.md +0 -94
- package/.agents/personas/refactorer.md +0 -113
- package/.agents/personas/sre.md +0 -86
- package/.agents/personas/ux-designer.md +0 -95
- package/.agents/scripts/epic-plan-decompose.js +0 -54
- package/.agents/scripts/epic-plan-spec.js +0 -64
- package/.agents/scripts/lib/orchestration/skill-capsule-loader.js +0 -109
- package/.agents/scripts/plan-critics.js +0 -199
- package/.agents/skills/core/baseline-refresh/SKILL.md +0 -181
- package/.agents/skills/core/ci-cd-and-automation/SKILL.md +0 -274
- package/.agents/skills/core/ci-cd-and-automation/examples.md +0 -211
- package/.agents/skills/core/code-simplification/SKILL.md +0 -389
- package/.agents/skills/core/context-engineering/SKILL.md +0 -309
- package/.agents/skills/core/context-engineering/examples.md +0 -58
- package/.agents/skills/core/deprecation-and-migration/SKILL.md +0 -250
- package/.agents/skills/core/frontend-ui-engineering/SKILL.md +0 -357
- package/.agents/skills/core/hydrate-context/SKILL.md +0 -123
- package/.agents/skills/core/idea-refinement/examples.md +0 -437
- package/.agents/skills/core/idea-refinement/frameworks.md +0 -135
- package/.agents/skills/core/incremental-implementation/SKILL.md +0 -271
- package/.agents/skills/core/introducing-a-baseline-gate/SKILL.md +0 -213
- package/.agents/skills/core/knowledge-transfer/SKILL.md +0 -180
- package/.agents/skills/core/mutation-survivor-remediation/SKILL.md +0 -117
- package/.agents/skills/core/performance-optimization/SKILL.md +0 -314
- package/.agents/skills/core/planning-and-task-breakdown/SKILL.md +0 -277
- package/.agents/skills/core/property-based-testing/SKILL.md +0 -148
- package/.agents/skills/core/refactoring-discipline/SKILL.md +0 -111
- package/.agents/skills/core/shipping-and-launch/SKILL.md +0 -328
- package/.agents/skills/core/spec-driven-development/SKILL.md +0 -252
- package/.agents/skills/core/test-driven-development/SKILL.md +0 -475
- package/.agents/skills/core/using-agent-skills/SKILL.md +0 -232
- package/.agents/skills/stack/architecture/monorepo-path-strategist/SKILL.md +0 -31
- package/.agents/skills/stack/architecture/structured-output-zod/SKILL.md +0 -51
- package/.agents/skills/stack/architecture/subagent-orchestration/SKILL.md +0 -76
- package/.agents/skills/stack/backend/cloudflare-hono-architect/SKILL.md +0 -31
- package/.agents/skills/stack/backend/cloudflare-hono-architect/examples/route-template.ts +0 -33
- package/.agents/skills/stack/backend/cloudflare-queue-manager/SKILL.md +0 -31
- package/.agents/skills/stack/backend/cloudflare-workers/SKILL.md +0 -51
- package/.agents/skills/stack/backend/highlevel-crm/SKILL.md +0 -54
- package/.agents/skills/stack/backend/sqlite-drizzle-expert/SKILL.md +0 -29
- package/.agents/skills/stack/backend/sqlite-drizzle-expert/examples/schema-template.ts +0 -30
- package/.agents/skills/stack/backend/stripe-integration/SKILL.md +0 -57
- package/.agents/skills/stack/backend/stripe-integration/scripts/listen-stripe.sh +0 -9
- package/.agents/skills/stack/backend/turso-sqlite/SKILL.md +0 -48
- package/.agents/skills/stack/frontend/astro/SKILL.md +0 -62
- package/.agents/skills/stack/frontend/astro-react-island-strategist/SKILL.md +0 -30
- package/.agents/skills/stack/frontend/expo-react-native-developer/SKILL.md +0 -29
- package/.agents/skills/stack/frontend/google-analytics-v4/SKILL.md +0 -50
- package/.agents/skills/stack/frontend/tailwind-v4/SKILL.md +0 -58
- package/.agents/skills/stack/frontend/ui-accessibility-engineer/SKILL.md +0 -34
- package/.agents/skills/stack/qa/audit-accessibility/SKILL.md +0 -51
- package/.agents/skills/stack/qa/lighthouse-baseline/SKILL.md +0 -199
- 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
|
-
|
|
88
|
-
|
|
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
|
|
147
|
-
|
|
148
|
-
|
|
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:
|
|
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).
|
|
342
|
-
|
|
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)
|
|
392
|
-
child
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
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
|
-
|
|
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
|
|
380
|
-
[`core/
|
|
381
|
-
|
|
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
|
-
>
|
|
18
|
-
> the
|
|
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/
|
|
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
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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**
|
|
211
|
-
>
|
|
212
|
-
>
|
|
213
|
-
>
|
|
214
|
-
>
|
|
215
|
-
>
|
|
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/
|
|
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
|
|