mandrel 2.56.0 → 2.58.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/plan-critic.md +13 -18
- package/.agents/agents/story-worker.md +25 -33
- package/.agents/docs/agentrc-reference.json +0 -30
- package/.agents/docs/configuration.md +8 -28
- package/.agents/docs/execution-reference.md +5 -5
- package/.agents/docs/quality-gates.md +8 -7
- package/.agents/instructions.md +9 -10
- package/.agents/schemas/agentrc.schema.json +9 -185
- package/.agents/schemas/story-deliver-terminal.schema.json +1 -1
- package/.agents/scripts/acceptance-eval.js +107 -17
- package/.agents/scripts/ceremony-derive.js +191 -0
- package/.agents/scripts/check-context-budget.js +28 -33
- package/.agents/scripts/check-cyclomatic.js +4 -3
- package/.agents/scripts/deliver-light.js +31 -94
- package/.agents/scripts/evidence-gate.js +17 -1
- package/.agents/scripts/lib/audit-suite/checklist-threading.js +15 -2
- package/.agents/scripts/lib/baselines/coverage-updater-cli.js +110 -0
- package/.agents/scripts/lib/baselines/crap-preview-scan.js +25 -0
- package/.agents/scripts/lib/baselines/crap-updater-cli.js +223 -0
- package/.agents/scripts/lib/bdd-scenario-budget.js +21 -3
- package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +0 -1
- package/.agents/scripts/lib/close-validation/gates.js +52 -1
- package/.agents/scripts/lib/config/acceptance-eval.js +25 -57
- package/.agents/scripts/lib/config/delivery-routing.js +7 -33
- package/.agents/scripts/lib/config/explain.js +0 -19
- package/.agents/scripts/lib/config/limits.js +18 -78
- package/.agents/scripts/lib/config/quality.js +6 -3
- package/.agents/scripts/lib/config/runners.js +3 -2
- package/.agents/scripts/lib/config-settings-schema-delivery.js +15 -68
- package/.agents/scripts/lib/config-settings-schema-quality.js +0 -14
- package/.agents/scripts/lib/config-settings-schema.js +16 -143
- package/.agents/scripts/lib/crap-engine.js +35 -4
- package/.agents/scripts/lib/crap-utils.js +17 -1
- package/.agents/scripts/lib/cyclomatic-ceiling.js +19 -7
- package/.agents/scripts/lib/generated/agentrc-validator.js +1 -1
- package/.agents/scripts/lib/observability/runtime-friction.js +1 -1
- package/.agents/scripts/lib/observability/source-classifier.js +1 -0
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +5 -4
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +19 -73
- package/.agents/scripts/lib/orchestration/code-review.js +7 -3
- package/.agents/scripts/lib/orchestration/complexity-gate.js +46 -212
- package/.agents/scripts/lib/orchestration/file-assumptions.js +32 -17
- package/.agents/scripts/lib/orchestration/light-escalation.js +3 -3
- package/.agents/scripts/lib/orchestration/light-suitability.js +66 -233
- package/.agents/scripts/lib/orchestration/pinned-identifier-lint.js +137 -0
- package/.agents/scripts/lib/orchestration/plan-context.js +189 -387
- package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +42 -153
- package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +14 -70
- package/.agents/scripts/lib/orchestration/plan-persist/acceptance-handle-repair.js +107 -0
- package/.agents/scripts/lib/orchestration/plan-persist/changes-repair.js +305 -0
- package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +138 -170
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +128 -297
- package/.agents/scripts/lib/orchestration/plan-persist/soft-findings.js +55 -0
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +16 -65
- package/.agents/scripts/lib/orchestration/plan-persist/wave-serialisation.js +22 -35
- package/.agents/scripts/lib/orchestration/plan-text-hygiene.js +36 -135
- package/.agents/scripts/lib/orchestration/planning/memory-pool-advisory.js +61 -223
- package/.agents/scripts/lib/orchestration/review-base-ref.js +138 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +5 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +37 -5
- package/.agents/scripts/lib/orchestration/single-story-close/phases/pre-gate-steps.js +46 -16
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +6 -1
- package/.agents/scripts/lib/orchestration/story-close/context-budget-writeback.js +213 -0
- package/.agents/scripts/lib/orchestration/task-body-validator.js +10 -63
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +33 -539
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +21 -414
- package/.agents/scripts/lib/orchestration/ticket-validator.js +54 -118
- package/.agents/scripts/lib/orchestration/verify-credit.js +69 -24
- package/.agents/scripts/lib/story-body/body-format-lints.js +15 -85
- package/.agents/scripts/lib/story-body/story-body.js +54 -240
- package/.agents/scripts/lib/templates/decomposer-prompts.js +133 -121
- package/.agents/scripts/lib/test-isolate/cli-options.js +93 -0
- package/.agents/scripts/lib/test-isolate/progress-log.js +45 -0
- package/.agents/scripts/lib/test-isolate/render-report.js +97 -0
- package/.agents/scripts/lib/test-isolate/run-isolate.js +87 -0
- package/.agents/scripts/lib/test-run-credit.js +277 -0
- package/.agents/scripts/lib/wave-runner/footprint.js +48 -358
- package/.agents/scripts/lib/wave-runner/ready-set.js +6 -5
- package/.agents/scripts/lib/workers/crap-worker.js +32 -41
- package/.agents/scripts/plan-context.js +7 -9
- package/.agents/scripts/plan-critics.js +28 -54
- package/.agents/scripts/plan-persist.js +25 -68
- package/.agents/scripts/quality-preview.js +51 -0
- package/.agents/scripts/run-tests.js +12 -0
- package/.agents/scripts/stories-wave-tick.js +23 -45
- package/.agents/scripts/test-isolate.js +13 -180
- package/.agents/scripts/update-coverage-baseline.js +25 -70
- package/.agents/scripts/update-crap-baseline.js +19 -123
- package/.agents/skills/core/scope-triage/SKILL.md +3 -3
- package/.agents/workflows/audit-clean-code.md +4 -3
- package/.agents/workflows/helpers/acceptance-self-eval.md +41 -41
- package/.agents/workflows/helpers/code-quality-guardrails.md +4 -4
- package/.agents/workflows/helpers/code-review.md +2 -3
- package/.agents/workflows/helpers/deliver-digest.md +46 -55
- package/.agents/workflows/helpers/deliver-light.md +40 -105
- package/.agents/workflows/helpers/deliver-reference.md +1 -1
- package/.agents/workflows/helpers/deliver-story-reference.md +54 -55
- package/.agents/workflows/helpers/deliver-story.md +10 -13
- package/.agents/workflows/helpers/plan-reference.md +163 -221
- package/.agents/workflows/mandrel-plan.md +31 -40
- package/.agents/workflows/memory-consolidate.md +9 -13
- package/docs/CHANGELOG.md +36 -0
- package/lib/cli/registry.js +98 -2
- package/lib/migrations/index.js +4 -0
- package/lib/migrations/steps/2.57.0-retire-delivery-limit-knobs.js +45 -0
- package/lib/migrations/steps/2.57.0-retire-planning-limit-knobs.js +59 -0
- package/package.json +1 -1
- package/.agents/scripts/lib/framework-version.js +0 -39
- package/.agents/scripts/lib/orchestration/consolidation-precondition.js +0 -223
- package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +0 -97
- package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +0 -26
- package/.agents/scripts/lib/orchestration/spec-budget.js +0 -89
- package/.agents/scripts/lib/orchestration/spec-spill.js +0 -74
- package/.agents/scripts/lib/orchestration/verify-tier-repair.js +0 -107
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# /mandrel-plan — on-demand reference appendix
|
|
2
2
|
|
|
3
3
|
> **Applies when:** you are executing [`/mandrel-plan`](../mandrel-plan.md) and hit one of the
|
|
4
|
-
> situations below — input-mode derivation, the Gate #1
|
|
5
|
-
>
|
|
6
|
-
>
|
|
7
|
-
>
|
|
4
|
+
> situations below — input-mode derivation, the Gate #1 advisory line,
|
|
5
|
+
> tickets-mode supersede authoring, the operator-invoked pre-mortem, a
|
|
6
|
+
> failed persist, or source-id resolution. The spine stays resident; this
|
|
7
|
+
> file is read on demand.
|
|
8
8
|
|
|
9
9
|
## Deriving the input mode
|
|
10
10
|
|
|
@@ -92,140 +92,58 @@ The marker keeps the operator's undelegated decisions findable after the
|
|
|
92
92
|
fact: reviewing a `--yes` plan means scanning its decisions-made-by-default,
|
|
93
93
|
not re-deriving which assumptions were really the agent's to make.
|
|
94
94
|
|
|
95
|
-
## Gate #1 → the
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
`
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
**Under `--yes` the offer is recorded and planning proceeds** — it is *never*
|
|
148
|
-
auto-downgraded to light. An unattended run has nobody to confirm the reroute,
|
|
149
|
-
and a suggestion is not a confirmation. The same rule governs unknown triage
|
|
150
|
-
unattended: AFK unknowns are still researched, but no free-form operator
|
|
151
|
-
question is asked — each HITL unknown lands in Key Assumptions marked a
|
|
152
|
-
decision-made-by-default, so the record shows what was decided for the operator
|
|
153
|
-
rather than pretending it was decided with them.
|
|
154
|
-
|
|
155
|
-
Escalation in the *other* direction — an over-scope prompt on the light path —
|
|
156
|
-
is terminal and requires a fresh session. The rule that separates the two, and
|
|
157
|
-
why it must not be flattened into symmetry:
|
|
158
|
-
[`deliver-light.md` § Why the two directions differ](deliver-light.md).
|
|
159
|
-
|
|
160
|
-
## Gate #1 → the `/prototype` offer (`uiSurface`)
|
|
161
|
-
|
|
162
|
-
`complexitySignals.uiSurface` is the second advisory Gate #1 offer, and the
|
|
163
|
-
weaker of the two on purpose: it carries **no routing authority and adds no
|
|
164
|
-
gate**. Both halves are derived from observables already in the checkout — the
|
|
165
|
-
`hasWebSurface` applicability predicate the `target: "web"` audit lenses gate
|
|
166
|
-
on, and whether any predicted path matches a web lens `filePattern` registered
|
|
167
|
-
in `audit-rules.json`. There is no configuration key to set: a project with no
|
|
168
|
-
rendered frontend resolves falsey and the offer never fires.
|
|
169
|
-
|
|
170
|
-
When it does fire, **name [`/prototype`](../prototype.md) and stop there.**
|
|
171
|
-
`/mandrel-plan` must never invoke it — operator invocation is the entire design, because
|
|
172
|
-
the value is a human looking at a layout before its UI acceptance criteria are
|
|
173
|
-
frozen.
|
|
174
|
-
|
|
175
|
-
**Under `--yes` the offer is recorded and planning proceeds** — no reroute, no
|
|
176
|
-
prototype written, no gate raised. This is exactly how `deliverLightSuggestion`
|
|
177
|
-
behaves unattended, and for the same reason: an unattended run has nobody to
|
|
178
|
-
review an artifact, so recording the offer is the whole of the right behaviour.
|
|
179
|
-
|
|
180
|
-
## Shape-derived complexity routing (`complexitySignals`)
|
|
181
|
-
|
|
182
|
-
Complexity routes on the **objective shape of the authored work**, never on
|
|
183
|
-
seed word count — a detailed prompt can describe trivial work, a terse one
|
|
184
|
-
complex work. The pipeline stages the
|
|
185
|
-
decision:
|
|
186
|
-
|
|
187
|
-
- **Signals, not routing.** The envelope's `complexitySignals` field is
|
|
188
|
-
advisory only (`routingAuthority: false`): enumerated-artifact count (with
|
|
189
|
-
the configured `maxArtifacts` threshold beside it as one input),
|
|
190
|
-
`planning.riskHeuristics` phrases present in the seed, the repo state of
|
|
191
|
-
predicted paths (existing paths predict refactors; missing predict
|
|
192
|
-
creates), and the `audit-rules.json` sensitive-path classes the predicted
|
|
193
|
-
footprint intersects.
|
|
194
|
-
- **You author the verdict.** Judge the signals: a genuinely trivial scope
|
|
195
|
-
(small additive footprint, no risk hits, no sensitive class) earns a `lite`
|
|
196
|
-
claim via `plan-persist.js --route-downgrade-reason "<why>"`. The reason is
|
|
197
|
-
recorded on every created Story's `story-plan-state` checkpoint, making the
|
|
198
|
-
judgment auditable; without a recorded reason the conservative default
|
|
199
|
-
(`full`) stands.
|
|
200
|
-
- **Persist backstops the claim deterministically.** After authoring, the
|
|
201
|
-
work has measurable shape, so persist validates the `lite` claim against
|
|
202
|
-
each Story's own shape — distinct change kinds, declared magnitude,
|
|
203
|
-
uncertainty, deployable/migration span, glob-free footprint, and
|
|
204
|
-
sensitive-path classes, against the framework `STORY_SHAPE_CEILINGS` (effort
|
|
205
|
-
and risk, never artifact counts) — and **fails closed to
|
|
206
|
-
`full`** when any Story exceeds them (the refusal is ledgered on the
|
|
207
|
-
checkpoint too). The lite route is **not** licence to drop a
|
|
208
|
-
non-negotiable — every decision's `preserves` field enumerates what still
|
|
209
|
-
holds: the Story ticket, the PR-to-`main` landing, every repo quality gate,
|
|
210
|
-
and the security baseline. Those gates run in `single-story-close.js`
|
|
211
|
-
regardless of route.
|
|
212
|
-
|
|
213
|
-
**The label is a hint; deliver re-derives.** Persist labels a
|
|
214
|
-
lite cohort's Stories with **`route::lite`** as a *human-visible hint only* —
|
|
215
|
-
`/mandrel-deliver` computes the route from each fetched Story body via the same shape
|
|
216
|
-
function at dispatch, so neither a lost label nor an unread marker can
|
|
217
|
-
misroute delivery: a lite-shaped Story derives `lite` even with the label
|
|
218
|
-
absent, and a sensitive-footprint Story routes `full` and keeps its fresh
|
|
219
|
-
critic even with the label present. The derived route sets ceremony, not
|
|
220
|
-
where the engine runs — sub-agent boots are collapsed by a **single-Story
|
|
221
|
-
run**, never by a trivial shape. The `route::*` axis stays runtime-derived: hand-authored
|
|
222
|
-
`route::*` entries in `labels[]` are dropped by persist.
|
|
223
|
-
|
|
224
|
-
The knobs (`planning.complexityGate.{enabled, maxArtifacts}`) are documented
|
|
225
|
-
in [`.agents/docs/configuration.md`](../../docs/configuration.md) under
|
|
226
|
-
`### planning`; the defaults live on `DEFAULT_COMPLEXITY_GATE` and the shape
|
|
227
|
-
ceilings on `STORY_SHAPE_CEILINGS` in
|
|
228
|
-
[`lib/orchestration/complexity-gate.js`](../../scripts/lib/orchestration/complexity-gate.js).
|
|
95
|
+
## Gate #1 → the one advisory line
|
|
96
|
+
|
|
97
|
+
Gate #1 stops for exactly two things — the sharpened plan intent and any HITL
|
|
98
|
+
unknown — and everything else the envelope surfaced collapses to **one
|
|
99
|
+
advisory line** beneath it (Story #5312). Nothing on that line stops the run,
|
|
100
|
+
reroutes it, or is invoked by `/mandrel-plan`; each item names something the
|
|
101
|
+
operator may prefer to do instead, and the run proceeds either way. Under
|
|
102
|
+
`--yes` the line is recorded and planning continues — an unattended run has
|
|
103
|
+
nobody to take an offer.
|
|
104
|
+
|
|
105
|
+
The line names, in order, whichever of these the envelope carries:
|
|
106
|
+
|
|
107
|
+
- **`duplicates[]`** — open Stories the seed resembles (never Epics). Name
|
|
108
|
+
the top one or two by id and title; a plan that duplicates open work is
|
|
109
|
+
still the operator's call.
|
|
110
|
+
- **Open `intake` rows** (`priorFeedback`) — CI-gap intake filings written by
|
|
111
|
+
[`file-ci-gap.js`](../../scripts/file-ci-gap.js) when a delivery reached an
|
|
112
|
+
Option-2 verdict in [`ci-remediation.md`](../../rules/ci-remediation.md).
|
|
113
|
+
They carry evidence but no `## Spec`, no `acceptance[]` / `verify[]` and no
|
|
114
|
+
`agent::*` label, so `/mandrel-deliver` cannot take one: graduating it is
|
|
115
|
+
exactly **tickets mode** (`/mandrel-plan <issue number>`), and a filing
|
|
116
|
+
that keeps recurring (its `## Occurrences` table is the count) is often the
|
|
117
|
+
better next Story than the seed in front of you. A `platformGaps[]` row is
|
|
118
|
+
the same shape with a different owner.
|
|
119
|
+
- **`memoryPoolAdvisory.recommend`** — name
|
|
120
|
+
[`/memory-consolidate`](../memory-consolidate.md), quoting its
|
|
121
|
+
`reasons[]`. The one arm left measures the `MEMORY.md` index against the
|
|
122
|
+
harness's byte cap; a stale pool degrades recall, it does not make the plan
|
|
123
|
+
wrong.
|
|
124
|
+
- **`complexitySignals.uiSurface`** — name [`/prototype`](../prototype.md)
|
|
125
|
+
and stop there. The signal carries **no routing authority and adds no
|
|
126
|
+
gate**: both halves are derived from observables already in the checkout —
|
|
127
|
+
the `hasWebSurface` applicability predicate the `target: "web"` audit
|
|
128
|
+
lenses gate on, and whether any predicted path matches a web lens
|
|
129
|
+
`filePattern` in `audit-rules.json`; a project with no rendered frontend
|
|
130
|
+
resolves falsey and the offer never fires. `/mandrel-plan` must never invoke
|
|
131
|
+
it — operator invocation is the entire design, because the value is a human
|
|
132
|
+
looking at a layout before its UI acceptance criteria are frozen. Under
|
|
133
|
+
`--yes` the offer is recorded and planning proceeds — no reroute, no
|
|
134
|
+
prototype written, no gate raised.
|
|
135
|
+
|
|
136
|
+
## `complexitySignals` are advisory
|
|
137
|
+
|
|
138
|
+
The envelope's `complexitySignals` field carries the paths the seed predicts,
|
|
139
|
+
their repo state (existing paths predict refactors; missing predict creates)
|
|
140
|
+
and the `audit-rules.json` sensitive-path classes the footprint intersects —
|
|
141
|
+
`routingAuthority: false`, no `route` field. They ground the authoring
|
|
142
|
+
template's pre-resolved `changes[]` and the `/prototype` offer, nothing else.
|
|
143
|
+
Story #5312 deleted the plan-side lite claim that used to read them
|
|
144
|
+
(`--route-downgrade-reason`, the persist shape backstop, the `route::lite`
|
|
145
|
+
hint): every Story lands through the same engine and the same close gates,
|
|
146
|
+
and ceremony is derived from the landed diff at close.
|
|
229
147
|
|
|
230
148
|
## Correct-by-construction authoring template
|
|
231
149
|
|
|
@@ -233,38 +151,43 @@ ceilings on `STORY_SHAPE_CEILINGS` in
|
|
|
233
151
|
**correct-by-construction** skeleton, built from the same repo probe the
|
|
234
152
|
`complexitySignals` ran:
|
|
235
153
|
|
|
236
|
-
- **`verify[]`
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
154
|
+
- **`verify[]` entries are commands.** There is no tier suffix and no
|
|
155
|
+
`manual:<reason>` escape (Story #5312): write the exact command or test
|
|
156
|
+
path the deliverer runs and the acceptance critic reads as evidence.
|
|
157
|
+
- **`changes[]` names what the deliverer authors.** Generated artifacts —
|
|
158
|
+
quality baselines, generated test indexes, migration journals, lockfiles —
|
|
159
|
+
are omitted: the work regenerates them, the refresh is a close-gate concern,
|
|
160
|
+
and a declared shared artifact path reserves a footprint that needlessly
|
|
161
|
+
serializes sibling Stories at dispatch.
|
|
241
162
|
- **`changes[]` arrive pre-resolved to creates-vs-refactors.** Every path
|
|
242
163
|
the seed predicted is probed against the repo: an existing path is
|
|
243
164
|
emitted with `assumption: "refactors-existing"`, a missing one with
|
|
244
|
-
`assumption: "creates"`.
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
actually warns, so the warning marks a real outlier instead of ordinary
|
|
254
|
-
variance. Neither fails the persist. The hard fail-closed ceiling
|
|
255
|
-
(~1500 tokens, `spec-spill.js`) is unchanged.
|
|
165
|
+
`assumption: "creates"`. The persist gates stay authoritative — they probe
|
|
166
|
+
the base branch ref, not the working tree — but a `creates` on a path that
|
|
167
|
+
exists at base, or a `refactors-existing` on one that does not, is a
|
|
168
|
+
dry-run **warning**, not a rejection; only a `deletes` naming an absent
|
|
169
|
+
path is refused. A plain-string bullet or a trailing parenthetical is
|
|
170
|
+
repaired into the object form by probing base, and the repair is reported.
|
|
171
|
+
- **Keep `## Spec` at contract-level prose** — interfaces, invariants,
|
|
172
|
+
load-bearing constraints; no per-file behavior narration — and as long as
|
|
173
|
+
the work needs. There is no word or token budget.
|
|
256
174
|
|
|
257
175
|
A faithfully-filled skeleton — placeholders replaced, pre-resolved entries
|
|
258
|
-
kept
|
|
259
|
-
round-trip.
|
|
176
|
+
kept — passes the persist ticket validators with no round-trip.
|
|
260
177
|
|
|
261
178
|
### Authored entry shape
|
|
262
179
|
|
|
263
180
|
Each `stories.json` entry: `slug` (`^[a-z0-9][a-z0-9-]*$`), `type: "story"`,
|
|
264
181
|
`title`, `body` (`goal`, optional `spec`, `changes[{path, assumption}]` —
|
|
265
182
|
`creates|refactors-existing|deletes`, `non_goals`, `reason_to_exist`),
|
|
266
|
-
top-level `acceptance[]`, `verify[]` (
|
|
267
|
-
`depends_on[]` (a sibling slug, or `#<id>` for an existing
|
|
183
|
+
top-level `acceptance[]`, `verify[]` (each a **bare command** — there is no
|
|
184
|
+
tier suffix), and `depends_on[]` (a sibling slug, or `#<id>` for an existing
|
|
185
|
+
open Story).
|
|
186
|
+
|
|
187
|
+
Author `acceptance[]` **without** the `AC-<n>:` handle: the body renderer
|
|
188
|
+
numbers each checkbox from its array position, so a carried handle renders
|
|
189
|
+
doubled. Persist normalises one off rather than refusing, and names the strip
|
|
190
|
+
on the dry-run's repair list.
|
|
268
191
|
|
|
269
192
|
Nothing in that shape inventories the repo for the author. `changes[]` arrives
|
|
270
193
|
pre-resolved against the working tree, and Phase 8's
|
|
@@ -336,18 +259,28 @@ together, and a path two same-wave Stories both write is exactly where that
|
|
|
336
259
|
promise breaks. Promise and caveat belong on one durable surface — previously
|
|
337
260
|
the caveat was a stderr warning nobody kept.
|
|
338
261
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
`
|
|
262
|
+
Every conflict class is advisory (Story #5312 retired the
|
|
263
|
+
`planning.failOn*` / `requireExplicitCrossStoryDeps` upgrade knobs with the
|
|
264
|
+
registry and fan-out findings): co-editing one file is routine and often
|
|
265
|
+
correct — the delivery scheduler already serializes file-overlapping Stories —
|
|
266
|
+
and a path reference matched by substring can read as a dependency a prose
|
|
267
|
+
mention never meant. A finding names the Stories and the fix (a `depends_on`
|
|
268
|
+
edge, or folding the shared edit into one Story) for the operator to weigh.
|
|
269
|
+
|
|
270
|
+
## Tickets mode — the source ticket is evidence
|
|
271
|
+
|
|
272
|
+
A `--tickets` envelope carries a third author prompt beside
|
|
273
|
+
`systemPrompts.story` and `systemPrompts.storySplitRules`:
|
|
274
|
+
**`systemPrompts.storyTicketsRules`**. It exists because a
|
|
275
|
+
source ticket arrives already in Story shape — rendered `AC-<n>:` checkboxes,
|
|
276
|
+
a `## Verify` list, a `## Changes` footprint — and an author reading it as a
|
|
277
|
+
template carries that shape forward instead of re-deriving it. The addendum
|
|
278
|
+
binds the author to re-derive `acceptance[]` from the goal, to express
|
|
279
|
+
mechanical checks (a refreshed baseline, a lint exiting 0, a regenerated
|
|
280
|
+
index) as `verify[]` commands rather than acceptance items, and to take the
|
|
281
|
+
source's verify entries for the commands they name rather than their shape.
|
|
282
|
+
Read it whenever the mode is `tickets`; the other three modes do not carry
|
|
283
|
+
the field.
|
|
351
284
|
|
|
352
285
|
## Tickets mode — authoring `supersedes[]`
|
|
353
286
|
|
|
@@ -382,67 +315,76 @@ total by default — an authored map is the only thing that can say
|
|
|
382
315
|
`#11-#14 → #20` while `#15 → #21`, which a blanket "superseded by
|
|
383
316
|
this plan-run" reference could not.
|
|
384
317
|
|
|
385
|
-
##
|
|
318
|
+
## The pre-mortem critic — operator-invoked
|
|
319
|
+
|
|
320
|
+
The maker-blind **pre-mortem** critic is not a step of the spine (Story #5312
|
|
321
|
+
retired step 2.5 with the consolidation critic, whose one deterministic input
|
|
322
|
+
was a `## Delivery Slicing` table no Story carries). Run it when the operator
|
|
323
|
+
asks for it, after Author and before Persist — the last point a finding folds
|
|
324
|
+
into a re-author:
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
node .agents/scripts/plan-critics.js \
|
|
328
|
+
--stories temp/plan-<slug>/stories.json \
|
|
329
|
+
[--tech-spec temp/plan-<slug>/techspec.md]
|
|
330
|
+
```
|
|
386
331
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
prerequisite. That third trigger is what gives the default N=1 plan a cheap
|
|
394
|
-
viability check, since the size trigger is unreachable at one ticket and this
|
|
395
|
-
repo's resolved `riskHeuristics` is empty. The probe is conservative — explicit
|
|
396
|
-
markers only, so a plan naming no such artifact dispatches exactly as before.
|
|
332
|
+
It fires on one deterministic trigger: the **external-dependency** probe
|
|
333
|
+
finding an out-of-repo marker — a scoped package the plan names that no repo
|
|
334
|
+
manifest declares, a cross-repo `github.com/<owner>/<repo>` reference, or an
|
|
335
|
+
endpoint named as a service prerequisite. The probe is conservative —
|
|
336
|
+
explicit markers only. It exits 0 on **any** verdict (verdicts route work,
|
|
337
|
+
they do not gate) and exits **1** only on a usage/IO error — no critic ran.
|
|
397
338
|
|
|
398
339
|
```jsonc
|
|
399
340
|
{
|
|
400
|
-
"
|
|
401
|
-
"premortem": { "critic": "pre-mortem", "dispatch": true, "reasons": ["…"] },
|
|
402
|
-
"textHygiene": { "critic": "text-hygiene", "findings": [] }
|
|
341
|
+
"premortem": { "critic": "pre-mortem", "dispatch": true, "reasons": ["…"] }
|
|
403
342
|
}
|
|
404
343
|
```
|
|
405
344
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
`
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
maker-blind invariant, the `consolidation` and `pre-mortem` charters, and the
|
|
420
|
-
output shape standalone. When the kill-switch is off
|
|
421
|
-
(`roleScopedAgents: false`) or the host cannot spawn at this depth, fall back
|
|
422
|
-
to a generic sub-agent and hand it the same charter (the `consolidation` /
|
|
423
|
-
`pre-mortem` definitions in [`plan-critic.md`](../../agents/plan-critic.md)).
|
|
424
|
-
**When both critics fire, dispatch them in a single turn.** Consolidation and
|
|
425
|
-
pre-mortem read the same immutable draft, share no write path, and neither
|
|
426
|
-
consumes the other's verdict — the textbook independent fan-out of
|
|
427
|
-
[`parallel-tooling.md`](parallel-tooling.md) Rule 3. Issue both `Agent` calls
|
|
428
|
-
together in one assistant turn rather than awaiting the first verdict before
|
|
429
|
-
spawning the second; serialized critics double the round's wall clock and buy
|
|
430
|
-
nothing, because you fold both verdicts into the same re-author round anyway.
|
|
431
|
-
|
|
432
|
-
Either way the critic is **maker-blind**: hand it the draft artifacts
|
|
433
|
-
(`stories.json`, and `techspec.md` when present) — never the authoring
|
|
434
|
-
transcript or the reasons the planner believed its own draft is sound. A
|
|
435
|
-
critic that reads the maker's case grades the case, not the draft.
|
|
345
|
+
On `dispatch: true`, dispatch **one fresh-context, maker-blind sub-agent**.
|
|
346
|
+
When `delivery.routing.roleScopedAgents` is enabled (the **default**), use
|
|
347
|
+
`subagent_type: plan-critic` — it boots on the role-scoped
|
|
348
|
+
[`plan-critic`](../../agents/plan-critic.md) context (its own system prompt,
|
|
349
|
+
no `CLAUDE.md` @-closure) that carries the maker-blind invariant, the
|
|
350
|
+
`pre-mortem` charter, and the output shape standalone. When the kill-switch
|
|
351
|
+
is off (`roleScopedAgents: false`) or the host cannot spawn at this depth,
|
|
352
|
+
fall back to a generic sub-agent and hand it the same charter. Either way the
|
|
353
|
+
critic is **maker-blind**: hand it the draft artifacts (`stories.json`, and
|
|
354
|
+
`techspec.md` when present) — never the authoring transcript or the reasons
|
|
355
|
+
the planner believed its own draft is sound. A critic that reads the maker's
|
|
356
|
+
case grades the case, not the draft. Fold surviving findings into Gate #2 or
|
|
357
|
+
a re-author round.
|
|
436
358
|
|
|
437
359
|
## What `--dry-run` actually gates
|
|
438
360
|
|
|
439
361
|
`plan-persist.js --dry-run` is the same command with GitHub writes suppressed,
|
|
440
|
-
and every gate runs before the first `createIssue` would fire
|
|
441
|
-
the
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
362
|
+
and every gate runs before the first `createIssue` would fire. Since
|
|
363
|
+
Story #5312 the gates split two ways, and the dry-run is where the second
|
|
364
|
+
half is read:
|
|
365
|
+
|
|
366
|
+
**Hard — the run refuses:** a body that does not parse, a ticket that is not
|
|
367
|
+
a Story, an empty `acceptance[]` or `verify[]`, an unknown or cyclic
|
|
368
|
+
`depends_on`, the acceptance partition at N>1, the supersede partition, a
|
|
369
|
+
forbidden commit-subject prefix, and a `deletes` entry naming a path absent
|
|
370
|
+
at base.
|
|
371
|
+
|
|
372
|
+
**Warnings — listed, then the persist proceeds:** a `creates` on a path that
|
|
373
|
+
exists at base or a `refactors-existing` on one that does not (including a
|
|
374
|
+
path the base branch deleted or renamed, named with the removing commit), a
|
|
375
|
+
goal or acceptance path absent at base, a `verify[]` command naming an absent
|
|
376
|
+
test file, an `open-question` in a body (`Flag if…`, `TBD`, a trailing `?`),
|
|
377
|
+
and a `pinned-identifier` in an acceptance item — a backticked bare symbol
|
|
378
|
+
that is not a path, a label, a kebab token, a flag or a command, which the
|
|
379
|
+
advisory `changes[]` is free to reshape out from under the criterion. The
|
|
380
|
+
list also names every **repair** the run applied — a plain-string bullet or a
|
|
381
|
+
trailing parenthetical rewritten into `{ path, assumption }` by probing base,
|
|
382
|
+
and an `AC-<n>:` handle normalised off an acceptance item. The same list rides the result
|
|
383
|
+
envelope as `warnings[]` and `repairs[]`, so a `--chain-on-clean` run loses
|
|
384
|
+
nothing.
|
|
385
|
+
|
|
386
|
+
A dry run that comes back clean has paid for every deterministic refusal, so
|
|
387
|
+
the real persist has nothing left to discover except network failure.
|
|
446
388
|
|
|
447
389
|
## The container Epic (Gate #3)
|
|
448
390
|
|
|
@@ -56,11 +56,14 @@ node .agents/scripts/plan-context.js --seed "<seed>" \
|
|
|
56
56
|
and derives source ids from its `sourceTickets[]`; it also writes
|
|
57
57
|
**`stories.template.json`**, step 2's skeleton.
|
|
58
58
|
|
|
59
|
-
The envelope carries docs context, the story-author prompt
|
|
59
|
+
The envelope carries docs context, the story-author prompt (`systemPrompts.story`,
|
|
60
|
+
plus `systemPrompts.storySplitRules` for an N>1 draft and
|
|
61
|
+
`systemPrompts.storyTicketsRules` in tickets mode), `sourceTickets[]`,
|
|
60
62
|
`duplicates[]` (open **Stories**, never Epics), `epicCandidates[]` +
|
|
61
63
|
`dependencyCandidates[]` (Gate #3; path collisions), `priorFeedback` and
|
|
62
|
-
advisory `complexitySignals` (**no routing authority**).
|
|
63
|
-
|
|
64
|
+
advisory `complexitySignals` (**no routing authority**). An envelope over the
|
|
65
|
+
planner-context ceiling is written truncated with a `truncated` note, never
|
|
66
|
+
refused.
|
|
64
67
|
|
|
65
68
|
**Triage each unknown by resolver** ([ref](helpers/plan-reference.md)): an
|
|
66
69
|
**AFK** unknown (research settles it) is resolved before authoring, never
|
|
@@ -68,16 +71,13 @@ assumed; a **HITL** unknown goes to Gate #1. Under `--yes` do not ask free-form
|
|
|
68
71
|
operator questions — AFK unknowns are still researched; only HITL unknowns land
|
|
69
72
|
in Key Assumptions, each a decision-made-by-default.
|
|
70
73
|
|
|
71
|
-
**Gate #1** — STOP
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
filled from this envelope. A truthy `complexitySignals.uiSurface` names
|
|
79
|
-
[`/prototype`](prototype.md); never invoke it here.
|
|
80
|
-
[Both](helpers/plan-reference.md).
|
|
74
|
+
**Gate #1** — STOP for exactly two things: confirm the sharpened plan intent,
|
|
75
|
+
and settle any HITL unknown the operator owns. Everything else the envelope
|
|
76
|
+
surfaced — `duplicates[]`, open `intake` rows, a truthy
|
|
77
|
+
`memoryPoolAdvisory.recommend`, a truthy `complexitySignals.uiSurface` naming
|
|
78
|
+
[`/prototype`](prototype.md) (never invoke it here) — collapses to
|
|
79
|
+
**one advisory line** under the gate; none of it stops the run or reroutes
|
|
80
|
+
it ([ref](helpers/plan-reference.md)). Under `--yes`, auto-proceed.
|
|
81
81
|
|
|
82
82
|
### 2. Author
|
|
83
83
|
|
|
@@ -93,34 +93,22 @@ included. One rescue: a **never-tracked** one normalises to `creates`. Fields:
|
|
|
93
93
|
[ref](helpers/plan-reference.md).
|
|
94
94
|
|
|
95
95
|
Artifacts under `temp/plan-<slug>/`: `stories.json` (**length 1 by default**;
|
|
96
|
-
|
|
96
|
+
a Spec is as long as the work needs, inline, never under `docs/`); optional
|
|
97
97
|
`techspec.md` (**N===1 only**, folded into `## Spec`) and
|
|
98
98
|
`acceptance-manifest.json` (N>1 — `--plan-acceptance`). Use the envelope
|
|
99
|
-
`systemPrompts.story`; split only under the policy above
|
|
99
|
+
`systemPrompts.story`; split only under the policy above, and when you do,
|
|
100
|
+
read `systemPrompts.storySplitRules` too — it carries the schedule and
|
|
101
|
+
partition rules the core omits. In **tickets mode** also read
|
|
102
|
+
`systemPrompts.storyTicketsRules`: the source ticket is evidence, not a
|
|
103
|
+
template — re-derive `acceptance[]` rather than carrying its list, handles
|
|
104
|
+
and tier suffixes forward.
|
|
100
105
|
|
|
101
106
|
**Tickets mode:** every Story authors a top-level `supersedes[]`; persist
|
|
102
107
|
refuses a partial map ([shape](helpers/plan-reference.md)).
|
|
103
108
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
node .agents/scripts/plan-critics.js \
|
|
108
|
-
--stories temp/plan-<slug>/stories.json \
|
|
109
|
-
[--tech-spec temp/plan-<slug>/techspec.md]
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
Run **before** persist — the last point a finding folds into a re-author. It
|
|
113
|
-
exits 0 on **any** verdict (verdicts route work, they do not gate) and exits
|
|
114
|
-
**1** only on a usage/IO error — no critic ran: **do not proceed to Persist**,
|
|
115
|
-
fix and re-run.
|
|
116
|
-
|
|
117
|
-
- **Both `dispatch: false`** — proceed to Persist (each skip is ledgered).
|
|
118
|
-
- **Either `dispatch: true`** — dispatch **one fresh-context, maker-blind
|
|
119
|
-
sub-agent per firing critic** (hand it only the draft artifacts, never the
|
|
120
|
-
authoring transcript), fold findings into Gate #2 or a re-author round, re-run
|
|
121
|
-
this step. Pre-mortem triggers (incl. the external-dependency probe), the
|
|
122
|
-
advisory-only `textHygiene.findings[]` lints and dispatch shape:
|
|
123
|
-
[reference](helpers/plan-reference.md).
|
|
109
|
+
The maker-blind **pre-mortem** critic is not a step of this spine: run
|
|
110
|
+
`plan-critics.js` only when the operator asks for it
|
|
111
|
+
([how](helpers/plan-reference.md)).
|
|
124
112
|
|
|
125
113
|
### 3. Persist
|
|
126
114
|
|
|
@@ -132,7 +120,11 @@ to review (`--force-review`). Under `--yes`, auto-proceed.
|
|
|
132
120
|
`--epic-goal`). Never unasked ([ref](helpers/plan-reference.md)).
|
|
133
121
|
|
|
134
122
|
Run persist `--dry-run` **first** — same command, writes suppressed; every gate
|
|
135
|
-
runs before the first `createIssue
|
|
123
|
+
runs before the first `createIssue`, and the run **lists its warnings**
|
|
124
|
+
(a `creates` / `refactors-existing` the base branch disagrees with, a goal or
|
|
125
|
+
acceptance path absent at base, an open question in a body) and the
|
|
126
|
+
`changes[]` repairs it applied ([list](helpers/plan-reference.md)). Read
|
|
127
|
+
them; they never stop the persist:
|
|
136
128
|
|
|
137
129
|
```bash
|
|
138
130
|
node .agents/scripts/plan-persist.js \
|
|
@@ -144,8 +136,8 @@ node .agents/scripts/plan-persist.js \
|
|
|
144
136
|
[--epic <id> | --epic-title "<name>" --epic-goal "<one paragraph>"]
|
|
145
137
|
```
|
|
146
138
|
|
|
147
|
-
|
|
148
|
-
|
|
139
|
+
`--chain-on-clean` folds a clean dry-run into the persist for **any** plan —
|
|
140
|
+
the dry-run's warning list is the review.
|
|
149
141
|
|
|
150
142
|
Persist creates `type::story` issue(s), a **metadata-only** `plan-run::<id>`
|
|
151
143
|
label, `blocked by #<id>` footers for every `depends_on` edge, and on a Gate #3
|
|
@@ -155,8 +147,7 @@ also comments on and closes each source id ([ref](helpers/plan-reference.md)).
|
|
|
155
147
|
|
|
156
148
|
## Constraints
|
|
157
149
|
|
|
158
|
-
- `/mandrel-plan` starts delivery
|
|
159
|
-
route — never off its Stories, which land via
|
|
150
|
+
- `/mandrel-plan` never starts delivery — its Stories land via
|
|
160
151
|
[`/mandrel-deliver`](mandrel-deliver.md).
|
|
161
152
|
- Duplicate search targets open Stories (`type::story`), not Epics; and
|
|
162
153
|
deterministic gates still fail closed under `--yes`.
|
|
@@ -97,17 +97,14 @@ Then write the receipt to `.consolidation-stamp.json` in the pool root:
|
|
|
97
97
|
```
|
|
98
98
|
|
|
99
99
|
`entryCount` is the surviving non-index `*.md` count **after** the rewrite —
|
|
100
|
-
count the directory, never the plan.
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
The `/mandrel-plan` Phase 0 advisory re-arms on exactly
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
the first two arms. A stamp with no `entryCount` leaves growth unmeasured, and
|
|
109
|
-
only the age and index arms can speak until the next pass writes one; a stamp
|
|
110
|
-
dated in the future reads as no stamp at all.
|
|
100
|
+
count the directory, never the plan. The stamp is the operator's record of
|
|
101
|
+
the pass; nothing in the framework reads it back.
|
|
102
|
+
|
|
103
|
+
The `/mandrel-plan` Phase 0 advisory re-arms on exactly one condition:
|
|
104
|
+
`MEMORY.md` exceeding `planning.memoryPool.indexByteCeiling` (24576) bytes —
|
|
105
|
+
the harness cap past which the index it loads is truncated. Pool size and
|
|
106
|
+
stamp age never trigger it (Story #5312 retired those arms): a pass that
|
|
107
|
+
rewrites long index lines short quiets it without pruning an entry.
|
|
111
108
|
|
|
112
109
|
Write it **only** after Gate #2 — the stamp asserts an operator reviewed the
|
|
113
110
|
pass, so writing it early makes it a lie.
|
|
@@ -116,8 +113,7 @@ Close with counts: entries read, corrected, merged, pruned, the new total, and
|
|
|
116
113
|
**the rewritten `MEMORY.md`'s size in bytes beside that count** — the index is
|
|
117
114
|
truncated at the byte ceiling, so a pass that pruned entries but left the
|
|
118
115
|
index over the cap has not fixed the loss, and the number is the only way the
|
|
119
|
-
operator can see that.
|
|
120
|
-
by hand: when the advisory next fires, and which arm reaches it first.
|
|
116
|
+
operator can see that.
|
|
121
117
|
|
|
122
118
|
## Constraints
|
|
123
119
|
|