mandrel 2.14.0 → 2.15.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 (68) hide show
  1. package/.agents/audit-checklists/navigability.md +1 -1
  2. package/.agents/docs/workflows.md +4 -5
  3. package/.agents/scripts/acceptance-eval.js +18 -1
  4. package/.agents/scripts/agents-bootstrap-github.js +22 -1
  5. package/.agents/scripts/apply-quality-bootstrap.js +6 -0
  6. package/.agents/scripts/audit-labels-bootstrap.js +15 -1
  7. package/.agents/scripts/audit-to-stories.js +26 -1
  8. package/.agents/scripts/boot-sweep.js +4 -1
  9. package/.agents/scripts/bootstrap.js +1 -0
  10. package/.agents/scripts/check-arch-cycles.js +20 -0
  11. package/.agents/scripts/check-baselines.js +8 -2
  12. package/.agents/scripts/check-context-budget.js +40 -5
  13. package/.agents/scripts/check-dead-exports.js +21 -0
  14. package/.agents/scripts/check-doc-links.js +12 -1
  15. package/.agents/scripts/check-lifecycle-doc-drift.js +9 -0
  16. package/.agents/scripts/check-workflow-citations.js +332 -0
  17. package/.agents/scripts/deliver-light.js +1 -0
  18. package/.agents/scripts/deliver-recover.js +4 -1
  19. package/.agents/scripts/diagnose-friction.js +17 -1
  20. package/.agents/scripts/diagnose.js +20 -14
  21. package/.agents/scripts/drain-pending-cleanup.js +20 -1
  22. package/.agents/scripts/evidence-gate.js +20 -1
  23. package/.agents/scripts/generate-config-docs.js +14 -1
  24. package/.agents/scripts/generate-lifecycle-docs.js +14 -1
  25. package/.agents/scripts/generate-workflows-doc.js +14 -1
  26. package/.agents/scripts/git-cleanup.js +32 -1
  27. package/.agents/scripts/lib/cli-usage.js +174 -0
  28. package/.agents/scripts/lib/cli-utils.js +12 -0
  29. package/.agents/scripts/lib/doc-tiers.js +53 -10
  30. package/.agents/scripts/lib/orchestration/plan-context.js +16 -11
  31. package/.agents/scripts/lib/workflow-closure.js +431 -0
  32. package/.agents/scripts/mandrel-update-preflight.js +9 -0
  33. package/.agents/scripts/nav-registry-diff.js +13 -0
  34. package/.agents/scripts/plan-context.js +17 -1
  35. package/.agents/scripts/plan-critics.js +10 -0
  36. package/.agents/scripts/plan-persist.js +33 -1
  37. package/.agents/scripts/plan-run-epilogue.js +12 -1
  38. package/.agents/scripts/quality-preview.js +17 -1
  39. package/.agents/scripts/resolve-doc-tiers.js +13 -0
  40. package/.agents/scripts/resolve-stories.js +1 -0
  41. package/.agents/scripts/resync-status-column.js +4 -1
  42. package/.agents/scripts/signals-view.js +11 -0
  43. package/.agents/scripts/single-story-close.js +24 -0
  44. package/.agents/scripts/single-story-confirm-merge.js +16 -0
  45. package/.agents/scripts/single-story-init.js +21 -1
  46. package/.agents/scripts/stories-wave-tick.js +1 -0
  47. package/.agents/scripts/sync-agentrc.js +16 -4
  48. package/.agents/scripts/update-ticket-state.js +23 -2
  49. package/.agents/workflows/audit-navigability.md +2 -2
  50. package/.agents/workflows/audit-to-stories.md +1 -1
  51. package/.agents/workflows/deliver.md +80 -81
  52. package/.agents/workflows/git-cleanup.md +9 -14
  53. package/.agents/workflows/helpers/acceptance-self-eval.md +14 -13
  54. package/.agents/workflows/helpers/audit-lens-core.md +2 -2
  55. package/.agents/workflows/helpers/code-review.md +11 -11
  56. package/.agents/workflows/helpers/deliver-digest.md +7 -8
  57. package/.agents/workflows/{deliver-light.md → helpers/deliver-light.md} +72 -24
  58. package/.agents/workflows/helpers/deliver-reference.md +46 -14
  59. package/.agents/workflows/helpers/deliver-story-reference.md +55 -63
  60. package/.agents/workflows/helpers/deliver-story.md +22 -22
  61. package/.agents/workflows/helpers/mandrel-sync-config.md +2 -2
  62. package/.agents/workflows/helpers/parallel-tooling.md +1 -2
  63. package/.agents/workflows/helpers/plan-reference.md +72 -14
  64. package/.agents/workflows/helpers/worktree-lifecycle.md +1 -4
  65. package/.agents/workflows/mandrel-update.md +6 -6
  66. package/.agents/workflows/plan.md +86 -83
  67. package/docs/CHANGELOG.md +15 -0
  68. package/package.json +2 -1
@@ -23,7 +23,7 @@ caller: helpers/deliver-story.md
23
23
 
24
24
  ## Step 0 — Lease preflight and merged-sweep
25
25
 
26
- ### Lease preflight (Story #3483)
26
+ ### Lease preflight
27
27
 
28
28
  Before any git mutation, init takes an exclusive, time-bounded **lease** on
29
29
  the Story ticket via the assignee-as-lease primitive
@@ -33,7 +33,7 @@ Epic-scoped dispatch manifest to serialise two operators driving the same
33
33
  Story, so this lease is the only guard against a concurrent
34
34
  `single-story-init` clobbering an in-flight run.
35
35
 
36
- **Fail-closed (audit #3513).** Unlike `/deliver`, the standalone path
36
+ **Fail-closed.** Unlike `/deliver`, the standalone path
37
37
  has **no Epic-scoped lifecycle ledger** to read a per-owner
38
38
  `story.heartbeat` from, so there is no live-heartbeat source to decide
39
39
  whether a foreign claim is stale. Rather than silently reclaim every
@@ -49,7 +49,7 @@ foreign assignee (which would leave the guard inert), the standalone lease
49
49
  `--dry-run` skips the lease (no assignee mutation). The matching release
50
50
  runs in `single-story-close.js` (Step 3).
51
51
 
52
- ### Branch reuse (Story #3483)
52
+ ### Branch reuse
53
53
 
54
54
  When a `story-<id>` branch already exists locally, init **reuses** it rather
55
55
  than re-creating it (re-running `git branch` on an existing ref throws
@@ -69,7 +69,7 @@ matching `origin/` ref, and stale tracking refs for any merged sibling
69
69
  stories are reaped in one pass. The sweep never blocks init — failures
70
70
  are logged and the new story is initialized regardless.
71
71
 
72
- The sweep applies two hardening layers (Story #2011):
72
+ The sweep applies two hardening layers:
73
73
 
74
74
  - **Per-candidate protection.** Each merged-PR candidate is filtered
75
75
  through three guards before reaching `executeCleanup`:
@@ -105,7 +105,7 @@ checkout** if it resolves a main-checkout absolute path. To stay in the
105
105
  worktree you MUST prefix **every Edit/Write/Read path with the absolute
106
106
  worktree root** (the `workCwd` value from Step 0), not merely `cd` into it.
107
107
  Never edit files under the bare main-checkout root. `single-story-close.js`
108
- runs a **wrong-tree guard** (Story #3364) that aborts close and posts a
108
+ runs a **wrong-tree guard** that aborts close and posts a
109
109
  `friction` comment if it finds uncommitted tracked-path edits in the main
110
110
  checkout while the worktree is the active work tree — but that is a backstop,
111
111
  not a substitute for prefixing paths correctly.
@@ -121,11 +121,11 @@ The v2 engine's trait table:
121
121
  | Ticket type | `type::story` only |
122
122
  | Branch | `story-<id>` seeded from `project.baseBranch` (`main`) |
123
123
  | Merge target | `main` via PR (squash + required checks) |
124
- | Epic integration branch | **None** — no `epic/<id>`, no `--no-ff` wave merge |
125
124
  | Spec / slices | Folded `## Spec` + optional `## Slicing` checkpoints in-session |
126
125
  | Ceremony | Per-Story, routed off the derived change level via `ceremony-routing.js` |
127
126
 
128
- **Ceremony-lite Stories still land through this engine unchanged (Story #4683).** A lite-routed Story collapses only the *advisory* plan/deliver
127
+ **Ceremony-lite Stories still land through this engine unchanged.** A
128
+ lite-routed Story collapses only the *advisory* plan/deliver
129
129
  ceremony — the fresh-critic / Tech-Spec authoring a one-artifact scope does
130
130
  not earn. It does **not** get a cheaper landing: the close-validation gates
131
131
  (lint / test / format / coverage / CRAP / maintainability), the PR to `main`,
@@ -133,7 +133,7 @@ and the `rules/security-baseline.md` MUSTs all run exactly as for a
133
133
  full-ceremony Story. The lite route's `preserves` field is the machine-readable
134
134
  record of those non-negotiables; there is no lite-specific gate bypass.
135
135
 
136
- **Deliver derives the route from the Story body's shape (Story #4722).**
136
+ **Deliver derives the route from the Story body's shape.**
137
137
  Persist stamps a lite cohort's Stories with the `route::lite` label as a
138
138
  *human-visible hint only* (and ledgers the authored verdict — recorded
139
139
  reason plus per-Story shape evidence — on the `story-plan-state`
@@ -167,7 +167,7 @@ demand. See [`.agents/instructions.md` § 3](../../instructions.md).
167
167
  write and self-check as you author. When absent, lens-aware coverage still
168
168
  runs maker-blind at Story-scope review inside the close subprocess. The
169
169
  dispatch step produces `checklistPath` from the Story's predicted footprint
170
- before it spawns the worker (Story #4627) — see [`/deliver`](../deliver.md).
170
+ before it spawns the worker — see [`/deliver`](../deliver.md).
171
171
 
172
172
  **Pre-eval full-suite discipline (spine step 5).** Repo-invariant guards —
173
173
  drift-guard and schema tests living outside the Story's scoped greps — are
@@ -181,7 +181,7 @@ directly.
181
181
 
182
182
  ### Step 1a — self-eval mechanics
183
183
 
184
- **One verdict-owner per cluster (Story #4723).** The ceremony routing's
184
+ **One verdict-owner per cluster.** The ceremony routing's
185
185
  resolved decision names each cluster's single verdict owner
186
186
  (`verdictOwner: 'fresh-critic' | 'inline-self-eval'` from
187
187
  `resolveCeremonyForRisk`): the fresh maker-blind critic when sensitivity
@@ -195,7 +195,7 @@ round cap, proceed / redraft / block — not an independent additional pass
195
195
  over the criteria. The M4-B floor holds: one verdict per cluster, the
196
196
  cluster count owned by `acceptance-clusters.js` alone.
197
197
 
198
- **Critic evidence-share (Story #4250).** When the critic runs a `verify[]`
198
+ **Critic evidence-share.** When the critic runs a `verify[]`
199
199
  command that is byte-identical to a close gate (`lint` / `typecheck`), it
200
200
  records the pass into the Story evidence keyspace via `--standalone` so
201
201
  close short-circuits the gate at unchanged HEAD. Run it in the **Story
@@ -220,7 +220,7 @@ node .agents/scripts/update-ticket-state.js --ticket <storyId> --state agent::bl
220
220
 
221
221
  ## Step 2 — Ceremony detail
222
222
 
223
- **Compute the change set once** (Story #4593) with the shared enumerator —
223
+ **Compute the change set once** with the shared enumerator —
224
224
  the same module close uses — and reuse that one list downstream:
225
225
 
226
226
  ```bash
@@ -247,7 +247,7 @@ Resolve fresh-vs-inline acceptance critics per AC-cluster with
247
247
  floor forces `fresh`). Review depth reads the same derived level via
248
248
  `review-depth.js` inside close, so the two decisions cannot disagree.
249
249
 
250
- **Lite-route override (Story #4722).** When the Story's body derives the
250
+ **Lite-route override.** When the Story's body derives the
251
251
  lite shape (`resolveStoryDispatchMode` → `inline`), run every
252
252
  acceptance critic **inline** — do not spawn fresh-context critic sub-agents
253
253
  regardless of what the profile would otherwise resolve. The self-eval rigor
@@ -261,7 +261,7 @@ removed. Hard gates are untouched.
261
261
 
262
262
  **What close does internally.** The script runs the close-validation gates
263
263
  against `baseBranch`, syncs the Story branch from `origin/<baseBranch>`
264
- (Story #2580 — the parallel-race defence), pushes `story-<id>`, opens (or
264
+ (the parallel-race defence), pushes `story-<id>`, opens (or
265
265
  reuses) a PR against `baseBranch` with a `Closes #<storyId>` footer, enables
266
266
  GitHub native auto-merge (`--auto --squash --delete-branch`) **when
267
267
  `delivery.ci.autoMerge` is `"trust-ci"` (the default)**, flips the Story to
@@ -284,7 +284,8 @@ The wait probes the checks every poll: a red required check fails fast as
284
284
  `checks-failed` instead of burning the budget, and a PR that falls behind its
285
285
  base is brought up to date within `updateAttempts` tries.
286
286
 
287
- **Async merge-confirm mode (`delivery.mergeWatch.mode: "async"`, Story #4698).** Under the default `"sync"` the merge wait runs in the foreground as
287
+ **Async merge-confirm mode (`delivery.mergeWatch.mode: "async"`).** Under
288
+ the default `"sync"` the merge wait runs in the foreground as
288
289
  described above. When a consumer's CI routinely takes longer than the host
289
290
  tool ceiling (~10 min) can hold a single close invocation, the foreground
290
291
  wait almost always expires `pending` after burning ~5 minutes of the slot —
@@ -309,29 +310,25 @@ checks pass. Under `"strict"`, the close **does not arm auto-merge** — the
309
310
  PR opens and waits for an **operator merge**, exactly as `--no-auto-merge`
310
311
  does per-run.
311
312
 
312
- **Close flags:**
313
-
314
- - `--skip-validation` bypass the gates. Use only when re-running close
315
- after a fixed gate failure that's already known to pass.
316
- - `--skip-sync` — bypass the base-sync (Story #2580). Use only after a
317
- hand-resolved sync, or in tests.
318
- - `--no-auto-merge` — disable auto-merge. Use when the PR materially changes
319
- behaviour and warrants a pre-merge eyeball; the operator then merges via
320
- the GitHub UI.
321
- - `--wait-merge` — **close-and-land** (Story #4428). Forces close to poll
322
- the armed PR to merge confirmation and flip `agent::done` itself. When
323
- neither land flag is passed, close defaults from
324
- `delivery.routing.closeAndLand` (**true**): attended and headless delivers
325
- share the land-in-one-close happy path.
326
- - `--no-wait-merge` — explicit opt-out that always wins. Use when the
327
- operator wants the PR left at `agent::closing` for a human land (or a
328
- wrapper that will invoke `single-story-confirm-merge.js` itself). Reports
329
- `pending` — the work is not done, nothing is broken, and one named command
330
- finishes it.
331
- - `--max-wait-seconds <n>` — raise the merge wait's per-invocation bound for
332
- this run (Story #4543). Use from a headless caller with no host
333
- tool-invocation ceiling to keep single-block semantics without editing the
334
- consumer's config.
313
+ **When to reach for a close flag.** What each one *does* is in
314
+ `node .agents/scripts/single-story-close.js --help`; below is only the
315
+ judgment that help text cannot carry.
316
+
317
+ - `--skip-validation` — only when re-running close after a fixed gate
318
+ failure that's already known to pass.
319
+ - `--skip-sync` — only after a hand-resolved sync, or in tests.
320
+ - `--no-auto-merge` when the PR materially changes behaviour and warrants a
321
+ pre-merge eyeball; the operator then merges via the GitHub UI.
322
+ - `--wait-merge` — **close-and-land**. When neither land flag
323
+ is passed, close defaults from `delivery.routing.closeAndLand` (**true**):
324
+ attended and headless delivers share the land-in-one-close happy path.
325
+ - `--no-wait-merge` — the explicit opt-out always wins. Use when the operator
326
+ wants the PR left at `agent::closing` for a human land (or a wrapper that
327
+ will invoke `single-story-confirm-merge.js` itself). Reports `pending`
328
+ the work is not done, nothing is broken, and one named command finishes it.
329
+ - `--max-wait-seconds <n>` from a headless caller with no host
330
+ tool-invocation ceiling, to keep single-block semantics
331
+ without editing the consumer's config.
335
332
 
336
333
  ---
337
334
 
@@ -341,14 +338,14 @@ The `single-story-close.js` script, in order:
341
338
 
342
339
  1. Runs the close-validation gates against `baseBranch` as the baseline.
343
340
  On any gate failure it throws — the operator fixes and re-runs close.
344
- **Gate output is captured, not streamed (Story #4736).** Every gate line
341
+ **Gate output is captured, not streamed.** Every gate line
345
342
  goes to `temp/orchestration/close-gates-<storyId>.log`; a clean run reports
346
343
  one digest line naming that artifact, and a **failed** gate replays its
347
344
  captured tail inline so the evidence is in front of you without opening a
348
345
  file. Read the artifact when you need the full text — or re-run under
349
346
  `AGENT_LOG_LEVEL=verbose` for live streaming.
350
- 1a. **Syncs the Story branch from `origin/<baseBranch>`** before push
351
- (Story #2580). Runs `git fetch origin <baseBranch>` followed by
347
+ 1a. **Syncs the Story branch from `origin/<baseBranch>`** before push.
348
+ Runs `git fetch origin <baseBranch>` followed by
352
349
  `git merge --no-edit origin/<baseBranch>` inside the worktree. This
353
350
  defends against the parallel-`/deliver-story` race: when
354
351
  multiple sessions run in parallel, the Story that auto-merges first
@@ -385,7 +382,7 @@ The `single-story-close.js` script, in order:
385
382
  GitHub UI. Pass `--no-auto-merge` to opt out when the PR needs a
386
383
  pre-merge eyeball.
387
384
  4. Flips the Story to **`agent::closing`** (NOT `agent::done`) and leaves
388
- the GitHub issue **OPEN** (Story #3385). Auto-merge completes
385
+ the GitHub issue **OPEN**. Auto-merge completes
389
386
  asynchronously *after* this script exits, so closing the issue here
390
387
  would strand a CLOSED issue with no merged work if the PR later failed
391
388
  CI, went `BEHIND` base, or was closed without merging. The Story rests
@@ -397,7 +394,7 @@ The `single-story-close.js` script, in order:
397
394
  only reaches `agent::done` once its PR to `main` is confirmed merged.
398
395
  5. Reaps the worktree when `delivery.worktreeIsolation.reapOnSuccess`
399
396
  is enabled.
400
- 6. **Releases the Story lease** (Story #3483). Clears the Story assignment
397
+ 6. **Releases the Story lease.** Clears the Story assignment
401
398
  that init claimed so the next `/deliver-story` run sees an
402
399
  unclaimed ticket. The release is a no-op when the operator no longer
403
400
  holds the claim (a later run took over via reclaim/steal), so a late
@@ -436,7 +433,7 @@ armed across retries, so you do not re-arm — then resume the land with the
436
433
  envelope's `nextCommand`.
437
434
 
438
435
  To watch the checks on the red path, drive `pr-watch-with-update.js` — the
439
- **single CI-watch mechanism** (Story #4358). It polls the required checks to a
436
+ **single CI-watch mechanism**. It polls the required checks to a
440
437
  terminal state and auto-recovers from `mergeStateStatus: BEHIND`; do **not**
441
438
  fall back to a bare `gh pr checks` watch invocation:
442
439
 
@@ -485,7 +482,7 @@ where a worker most often misbehaves: it delivers up to arming auto-merge,
485
482
  then ends its turn with **free-form prose** — e.g. "I'll wait for the
486
483
  background watch task to complete" or "the next event will be its completion
487
484
  notification" — leaving the merge unconfirmed and the Story stranded at
488
- `agent::closing` (observed on Story #1553 / PR #1554). **Do not do this.**
485
+ `agent::closing`. **Do not do this.**
489
486
  `pr-watch-with-update.js --pr <prNumber>` *blocks the current turn* until CI
490
487
  resolves — that is the mechanism by which you wait. You MUST keep your turn alive
491
488
  across the wait: watch → (fix + push + re-watch on red) → confirm the merge
@@ -593,14 +590,13 @@ when `--pr` is omitted) and:
593
590
  - **Story already `agent::done` / issue already closed** → idempotent
594
591
  `{ action: 'noop', reason: 'already-done' }`.
595
592
 
596
- The issue closes exactly when the work has merged, never at PR-open
597
- (#2155).
593
+ The issue closes exactly when the work has merged, never at PR-open.
598
594
 
599
595
  ---
600
596
 
601
597
  ## Step 5.5 — Re-assert Status column detail
602
598
 
603
- > **The land tail already ran this** (Story #4543) — it is `tail.statusResync`
599
+ > **The land tail already ran this** — it is `tail.statusResync`
604
600
  > in the terminal envelope. Run it by hand only when that step reported
605
601
  > `false`, or after a manual merge on a `--no-wait-merge` run.
606
602
 
@@ -609,7 +605,7 @@ node .agents/scripts/resync-status-column.js --story <storyId>
609
605
  ```
610
606
 
611
607
  The helper re-fires the `ColumnSync` mutation and **polls for ~15 s** to win the
612
- race against the bot's late write (Story #2876). It is idempotent and
608
+ race against the bot's late write. It is idempotent and
613
609
  no-op-safe (`no-project` / `not-on-project` exit 0).
614
610
 
615
611
  The GitHub Projects v2 built-in workflows `Pull request merged` and
@@ -617,9 +613,9 @@ The GitHub Projects v2 built-in workflows `Pull request merged` and
617
613
  and fire ~minutes *after* auto-merge lands. They overwrite the Status
618
614
  field as a side-effect, clobbering the `Done` value
619
615
  `single-story-confirm-merge.js` set at the `agent::done` flip in Step 5
620
- and leaving closed Stories stuck at `In Progress` on the board
621
- (reproduced on Story #2813). The confirmation step has already exited by
622
- then, so the bot gets the last write.
616
+ and leaving closed Stories stuck at `In Progress` on the board. The
617
+ confirmation step has already exited by then, so the bot gets the last
618
+ write.
623
619
 
624
620
  `resync-status-column.js`:
625
621
 
@@ -627,21 +623,17 @@ then, so the bot gets the last write.
627
623
  - Re-fires the same `ColumnSync` mutation `transitionTicketState` used
628
624
  at close, overwriting the bot's late write.
629
625
  - **Polls the live Status for ~15 s after the initial write** and
630
- re-fires on drift (Story #2876). Without this loop, a one-shot
626
+ re-fires on drift. Without this loop, a one-shot
631
627
  mutation routinely lost the race against the bot's asynchronous
632
- fire (reproduced on Story #2871 / PR #2872).
628
+ fire.
633
629
  - Prints a single-line JSON envelope:
634
630
  `{ ticketId, status, column?, reason?, attempts? }`. `attempts > 1`
635
631
  means the helper had to fight a bot overwrite; `status: 'drifted'`
636
632
  means the bot won every attempt in the poll budget (rare; usually
637
633
  signals operator should reap the conflicting workflows).
638
634
 
639
- Tuning flags (rarely needed):
640
-
641
- - `--poll-attempts <n>` — total mutation attempts including the
642
- initial sync. Default `4`. Pass `1` to disable the poll loop
643
- (fastest, matches pre-#2876 behaviour).
644
- - `--poll-delay-ms <ms>` — delay between drift checks. Default `5000`.
635
+ Tuning flags are rarely needed; the script enumerates them itself
636
+ (`node .agents/scripts/resync-status-column.js --help`).
645
637
 
646
638
  Idempotent: re-running on a ticket whose Status already matches the
647
639
  target returns the same envelope. No-op skips (`no-project`,
@@ -658,7 +650,7 @@ defense-in-depth against re-enabled or future workflows.
658
650
 
659
651
  ## Step 6 — Local branch cleanup detail
660
652
 
661
- > **The land tail already ran this** (Story #4543) — it is `tail.refCleanup` and
653
+ > **The land tail already ran this** — it is `tail.refCleanup` and
662
654
  > `tail.baseFastForward` in the terminal envelope, done in-process against the
663
655
  > same planners this command drives. Run it by hand only when either step
664
656
  > reported `false` (a dirty shared checkout is the common, benign cause), or
@@ -727,7 +719,7 @@ up").
727
719
 
728
720
  The field-level contract is the shipped schema
729
721
  [`story-deliver-terminal.schema.json`](../../schemas/story-deliver-terminal.schema.json)
730
- (Story #4543) — not this file, and not
722
+ — not this file, and not
731
723
  [`agents/story-worker.md`](../../agents/story-worker.md). All three used to
732
724
  carry their own prose version; the schema is now the only definition. What
733
725
  follows is the *judgement* around it, which a schema cannot express.
@@ -744,7 +736,7 @@ tool-invocation ceiling, which would otherwise park the Story at
744
736
  budget is anchored at the PR's `createdAt`, so resuming does not restart the
745
737
  clock and the give-up bound still means something.
746
738
  - It is **not** a park. Returning `pending` because you would rather not wait
747
- is the Story #1553 / PR #1554 failure mode wearing a schema. Return it only
739
+ is the no-park failure mode wearing a schema. Return it only
748
740
  when the bound genuinely expired, or a human owns the merge.
749
741
 
750
742
  The no-park rule holds: a turn that ends with prose ("I'll wait for the watch
@@ -3,6 +3,7 @@ description:
3
3
  Execute one Story end-to-end. Creates story-<id> from main, implements in a
4
4
  worktree (optional ## Slicing checkpoints), runs derived-level ceremony,
5
5
  opens a PR against main, and lands.
6
+ mandatoryReads: [deliver-digest.md]
6
7
  ---
7
8
 
8
9
  # /deliver-story #[Story ID]
@@ -16,14 +17,13 @@ description:
16
17
  > one bundled read of what every delivery needs — dispatch decision, engine
17
18
  > invariants, the change-set/ceremony incantation, the acceptance-eval gate,
18
19
  > and the terminal-envelope contract — replacing the per-session re-reads of
19
- > the helper set and `story-deliver-terminal.schema.json` (Story #4736). The
20
- > steps below cite it as "digest § N" rather than restating it.
20
+ > the helper set and `story-deliver-terminal.schema.json`. The steps below
21
+ > cite it as "digest § N" rather than restating it.
21
22
 
22
23
  ## Overview
23
24
 
24
25
  The **one** delivery engine in v2 — every Story (a lite-**shaped** Story
25
- runs inline with inline critics, #4722; engine, gates, envelope
26
- byte-identical):
26
+ runs inline with inline critics; engine, gates, envelope byte-identical):
27
27
 
28
28
  ```text
29
29
  single-story-init.js → implement + commits → derived-level ceremony
@@ -31,10 +31,10 @@ single-story-init.js → implement + commits → derived-level ceremony
31
31
  → CI watch + merge → single-story-confirm-merge.js (agent::done)
32
32
  ```
33
33
 
34
- An `Epic: #N` reference marks a v1 ticket — **stop** and re-plan.
35
- There is no `epic/<id>` branch, no `--no-ff` wave merge (trait table:
36
- reference § Engine invariants). Prerequisites: a `type::story` issue,
37
- clean `gh auth status`, and `project.baseBranch` on local and `origin`.
34
+ An `Epic: #N` reference marks a v1 ticket — **stop** and re-plan. The
35
+ engine's trait table is reference § Engine invariants. Prerequisites: a
36
+ `type::story` issue, clean `gh auth status`, and `project.baseBranch` on
37
+ local and `origin`.
38
38
 
39
39
  ## Step 0 — Initialize (`single-story-init.js`)
40
40
 
@@ -45,14 +45,14 @@ timeout — the per-tree install can take minutes; never `run_in_background`:
45
45
  node .agents/scripts/single-story-init.js --story <storyId>
46
46
  ```
47
47
 
48
- Flags: `--dry-run` (no mutations; skips lease + sweep), `--steal` (transfer
49
- a foreign lease). It validates `type::story`, **acquires the Story lease**
48
+ Every script below documents its own flags run it with `--help`.
49
+ It validates `type::story`, **acquires the Story lease**
50
50
  (fails closed on a foreign assignee), fetches `origin`, seeds `story-<id>`
51
51
  from `baseBranch` (idempotent reuse), materializes a worktree, runs a
52
52
  guarded merged-`story-*` sweep, and flips `agent::executing` (reference
53
53
  § Step 0). Capture `workCwd` from the result envelope.
54
54
 
55
- **Land or block (issue #4483).** `remoteVerified: false` → flip
55
+ **Land or block.** `remoteVerified: false` → flip
56
56
  `agent::blocked` quoting `remoteProbe.detail` and stop. Implementing outside
57
57
  the worktree/branch/PR path or committing to local `main` is forbidden —
58
58
  close's push is the only sanctioned landing.
@@ -94,9 +94,9 @@ mechanics live in the single-homed include
94
94
  ## Step 2 — Ceremony (profile + derived level)
95
95
 
96
96
  Ceremony is `delivery.routing.ceremonyProfile` × the **derived change
97
- level** — never a planner-authored verdict (Story #4542). **Digest § 3** is
98
- the incantation: compute the change set once (Story #4593), derive the level,
99
- resolve fresh-vs-inline critics with `ceremony-routing.js`; a lite Story runs
97
+ level** — never a planner-authored verdict. **Digest § 3** is the
98
+ incantation: compute the change set once, derive the level, resolve
99
+ fresh-vs-inline critics with `ceremony-routing.js`; a lite Story runs
100
100
  inline regardless (routing edge cases: reference § Step 2). Hard gates always
101
101
  run in Step 3 — the derived level never disables them; do **not** pre-run the
102
102
  close chain here.
@@ -113,12 +113,12 @@ post-land tail in one process. Run it and **branch on the terminal envelope's
113
113
  `nextCommand`; `blocked`/`checks-failed` → Step 4; `failed` → diagnose and
114
114
  re-run). Gate output is captured to
115
115
  `temp/orchestration/close-gates-<storyId>.log` — a clean run prints a digest
116
- line, a red gate replays its tail inline (Story #4736).
116
+ line, a red gate replays its tail inline.
117
117
 
118
118
  Internals (gate order, base-sync, auto-merge arming), the merge-wait
119
- budgets, the slow-CI **async** confirm mode (Story #4698 — launch the
120
- `pending` envelope's `nextCommand` as **background** Bash, never a
121
- foreground poll), the `autoMerge` policy, and every close flag: reference
119
+ budgets, the slow-CI **async** confirm mode (launch the `pending`
120
+ envelope's `nextCommand` as **background** Bash, never a foreground
121
+ poll), the `autoMerge` policy, and every close flag: reference
122
122
  § Step 3 — Merge wait, async mode, and flags.
123
123
 
124
124
  ## Steps 4–6 — Recovery router (**recovery-only**)
@@ -130,7 +130,7 @@ recovery path **only** when the envelope routes you there:
130
130
  armed), resume via `nextCommand`; triage per
131
131
  [`rules/ci-remediation.md`](../../rules/ci-remediation.md). The watch is
132
132
  internally blocking — never end a turn with prose and an unconfirmed
133
- merge (Story #1553). Reference § Step 4.
133
+ merge. Reference § Step 4.
134
134
  - **`pending`** → run `nextCommand` (`single-story-confirm-merge.js`) until
135
135
  resolved. Reference § Step 5.
136
136
  - **`tail.statusResync: false`** → reference § Step 5.5;
@@ -142,9 +142,9 @@ End your turn by relaying the validated envelope close emits between its
142
142
  `--- STORY DELIVER TERMINAL ---` markers — never free-form prose, never a
143
143
  hand-composed object. Statuses, exits, and required fields: **digest § 5**
144
144
  (whose SSOT is the shipped
145
- [schema](../../schemas/story-deliver-terminal.schema.json), Story #4543).
145
+ [schema](../../schemas/story-deliver-terminal.schema.json)).
146
146
  `pending` is the only sanctioned no-merge ending, returned only when your own
147
- budget is exhausted (Story #1553). Reference § Step 7.
147
+ budget is exhausted. Reference § Step 7.
148
148
 
149
149
  ## Recovering a stranded Story {#recover}
150
150
 
@@ -162,7 +162,7 @@ reuses an open PR).
162
162
  - **Never** push the Story branch directly to `main` — the PR is the only
163
163
  merge surface.
164
164
  - **Always** prefix path-based tools with the absolute `workCwd` root
165
- (Step 0.5); close's wrong-tree guard (Story #3364) is a backstop.
165
+ (Step 0.5); close's wrong-tree guard is a backstop.
166
166
  - **Report state, not process** — mirror the close envelope's fields; no
167
167
  step narration.
168
168
  - Drive every `agent::*` transition through
@@ -24,8 +24,8 @@ description: >-
24
24
 
25
25
  ## Overview
26
26
 
27
- Story #1995 replaced the previous template-merge behaviour with a
28
- **default-aware** reconciliation: the helper never adds optional keys to the
27
+ The helper performs a **default-aware** reconciliation rather than a
28
+ template merge: it never adds optional keys to the
29
29
  project config, because the runtime already layers framework defaults
30
30
  underneath the project's values at read time. The only thing the helper
31
31
  writes today is a structured report. The mechanical work is delegated to
@@ -77,8 +77,7 @@ prefer the higher-numbered rule — the parallelism gain compounds the
77
77
  background-shell gain. Concretely: dispatch the `Agent` calls in one turn
78
78
  (Rule 3), and **inside** each sub-agent let it apply Rule 2 to its own
79
79
  long-running shells — and, within the supported nesting depth budget
80
- (verified depth 2, announced max depth 5; see
81
- [#2870](https://github.com/dsj1984/mandrel/issues/2870)), let it apply
80
+ (verified depth 2, announced max depth 5), let it apply
82
81
  **Rule 3** to its own independent sub-units as well, not only Rule 2
83
82
  background shells. A sub-agent is a full orchestrator at its own level:
84
83
  recursive `Agent` fan-out is available to it, so the host does not need to
@@ -1,15 +1,73 @@
1
1
  # /plan — on-demand reference appendix
2
2
 
3
3
  > **Applies when:** you are executing [`/plan`](../plan.md) and hit one of the
4
- > situations below — shape-derived complexity routing, `--tickets` supersede
5
- > authoring, critic dispatch detail, a failed persist, or source-id
6
- > resolution. The spine stays resident; this file is read on demand.
4
+ > situations below — input-mode derivation, the Gate #1 light handoff,
5
+ > shape-derived complexity routing, tickets-mode supersede authoring, critic
6
+ > dispatch detail, a failed persist, or source-id resolution. The spine stays
7
+ > resident; this file is read on demand.
8
+
9
+ ## Deriving the input mode
10
+
11
+ `/plan` has no operator-facing flags; the CLIs below still take every flag they
12
+ always did. Read the invocation, **announce what you derived**, then fill in
13
+ the flag — the same derive-then-announce contract `/git-deliver` uses for its
14
+ terminal level.
15
+
16
+ | What was typed | Mode | You pass |
17
+ | --- | --- | --- |
18
+ | nothing | ask | — (ask what to plan) |
19
+ | prose | seed | `--seed "<text>"` |
20
+ | an argument resolving to an existing file | seed-file | `--seed-file <path>` |
21
+ | ids, none of them a delivered Story | tickets | `--tickets <ids>` |
22
+ | one id that is an `agent::done` Story | amends | `--amends '#<id>'` |
23
+ | "…but let me review before you file" | (any) | `--force-review` |
24
+
25
+ **Order matters.** Test *file exists* before *looks like prose*, or a bare
26
+ `notes.md` becomes a one-word seed. Test *all args are `^#?\d+$`* before
27
+ either, or a ticket list becomes prose.
28
+
29
+ **The one genuinely ambiguous case** is a bare id, between `amends` and
30
+ `tickets`. Resolve it from live state — `agent::done` can only be amended, an
31
+ open unplanned issue can only be planned — and ask only when the id is an open
32
+ Story already at `agent::ready`, where both readings are live. Do not ask in
33
+ the cases state already answers; an unnecessary question is the friction this
34
+ whole surface exists to remove.
35
+
36
+ Mixed ids and prose in one invocation is a **hard error**: refuse and ask which
37
+ was meant, rather than guessing a mode and doing the wrong work.
38
+
39
+ ## Gate #1 → the light path (in-session handoff)
40
+
41
+ On a confirmed `deliverLightSuggestion`, `/plan` routes into
42
+ [`deliver-light.md`](deliver-light.md) **without ending the session**. Two
43
+ things make that safe, and both are worth understanding before changing it:
44
+
45
+ 1. **The handoff carries the envelope, not the seed.** Gate #1 already holds a
46
+ codebase snapshot and `complexitySignals`; fill the light gate's `--creates`
47
+ / `--refactors` / `--acceptance` / `--reason` from those. Re-deriving from
48
+ raw seed text throws away the better signal and can disagree with the
49
+ suggestion that routed you.
50
+ 2. **The gate still runs.** The suggestion is read against seed-time ceilings
51
+ (`DELIVER_LIGHT_SUGGESTION_CEILINGS` — artifacts, risk hits, sensitive-path
52
+ classes); the light gate is read against a predicted shape
53
+ (`STORY_SHAPE_CEILINGS` — `maxChanges`, `maxAcceptance`). Two different
54
+ checks on purpose, so a confirm is not a bypass.
55
+
56
+ **When the light gate answers `ask-operator`**, the two ceiling sets disagreed.
57
+ Resume `/plan` at step 2 (Author) **in this same session** — the interrogation
58
+ is still valid and re-paying for it buys nothing. This bounce-back is not an
59
+ escalation.
60
+
61
+ Escalation in the *other* direction — an over-scope prompt on the light path —
62
+ is terminal and requires a fresh session. The rule that separates the two, and
63
+ why it must not be flattened into symmetry:
64
+ [`deliver-light.md` § Why the two directions differ](deliver-light.md).
7
65
 
8
66
  ## Shape-derived complexity routing (`complexitySignals`)
9
67
 
10
68
  Complexity routes on the **objective shape of the authored work**, never on
11
- seed word count (Story #4722 — a detailed prompt can describe trivial work, a
12
- terse one complex work; `maxSeedWords` is removed). The pipeline stages the
69
+ seed word count — a detailed prompt can describe trivial work, a terse one
70
+ complex work. The pipeline stages the
13
71
  decision:
14
72
 
15
73
  - **Signals, not routing.** The envelope's `complexitySignals` field is
@@ -37,7 +95,7 @@ decision:
37
95
  and the security baseline. Those gates run in `single-story-close.js`
38
96
  regardless of route.
39
97
 
40
- **The label is a hint; deliver re-derives (Story #4722).** Persist labels a
98
+ **The label is a hint; deliver re-derives.** Persist labels a
41
99
  lite cohort's Stories with **`route::lite`** as a *human-visible hint only* —
42
100
  `/deliver` computes the route from each fetched Story body via the same shape
43
101
  function at dispatch, so neither a lost label nor an unread marker can
@@ -53,7 +111,7 @@ in [`.agents/docs/configuration.md`](../../docs/configuration.md) under
53
111
  ceilings on `STORY_SHAPE_CEILINGS` in
54
112
  [`lib/orchestration/complexity-gate.js`](../../scripts/lib/orchestration/complexity-gate.js).
55
113
 
56
- ## Correct-by-construction authoring template (Story #4723)
114
+ ## Correct-by-construction authoring template
57
115
 
58
116
  `plan-context.js --out` writes `stories.template.json` as a
59
117
  **correct-by-construction** skeleton, built from the same repo snapshot the
@@ -73,7 +131,7 @@ ceilings on `STORY_SHAPE_CEILINGS` in
73
131
  authoritative: they probe the base branch ref, not the working tree.
74
132
  - **Keep `## Spec` near contract-level prose.** Persist emits an
75
133
  **advisory** warning past ~250 words (`SPEC_SOFT_WORD_BUDGET`) — it never
76
- fails the persist, but it is the nudge toward the #4707 contract-level
134
+ fails the persist, but it is the nudge toward a contract-level
77
135
  Spec (interfaces, invariants, load-bearing constraints; no per-file
78
136
  behavior narration). The hard fail-closed ceiling (~1500 tokens,
79
137
  `spec-spill.js`) is unchanged.
@@ -112,7 +170,7 @@ Story (mirroring `assertAcceptancePartition`): every id passed to
112
170
  `--tickets` must be claimed by **exactly one** Story, and no Story may
113
171
  claim an id that was not a source ticket. With N>1 the mapping is not
114
172
  total by default — an authored map is the only thing that can say
115
- `#4525-#4528 → #4530` while `#4529 → #4531`, which a blanket "superseded by
173
+ `#11-#14 → #20` while `#15 → #21`, which a blanket "superseded by
116
174
  this plan-run" reference could not.
117
175
 
118
176
  ## Critic dispatch detail
@@ -120,7 +178,7 @@ this plan-run" reference could not.
120
178
  The **pre-mortem** critic fires on any of three deterministic triggers: the
121
179
  draft ticket count reaching half the reviewability budget, a
122
180
  `planning.riskHeuristics` phrase matching the plan text, or the
123
- **external-dependency** probe (Story #4700) finding an out-of-repo marker — a
181
+ **external-dependency** probe finding an out-of-repo marker — a
124
182
  scoped package the plan names that no repo manifest declares, a cross-repo
125
183
  `github.com/<owner>/<repo>` reference, or an endpoint named as a service
126
184
  prerequisite. That third trigger is what gives the default N=1 plan a cheap
@@ -136,7 +194,7 @@ markers only, so a plan naming no such artifact dispatches exactly as before.
136
194
  }
137
195
  ```
138
196
 
139
- The verdict's third entry, `textHygiene`, is advisory-only (Story #4599): it
197
+ The verdict's third entry, `textHygiene`, is advisory-only: it
140
198
  carries deterministic body lints (`dangling-citation` / `open-question` /
141
199
  `slicing-mass`) with no dispatch semantics — it spawns nothing and never
142
200
  gates the run. Fold `textHygiene.findings[]` into the re-author round the
@@ -161,8 +219,8 @@ critic that reads the maker's case grades the case, not the draft.
161
219
 
162
220
  ## Ready means fully persisted
163
221
 
164
- `agent::ready` is the **terminal** step, not part of the creating POST
165
- (Story #4541). The order is: create unlabelled → upsert `story-plan-state` on
222
+ `agent::ready` is the **terminal** step, not part of the creating POST.
223
+ The order is: create unlabelled → upsert `story-plan-state` on
166
224
  every Story → upsert `plan-summary` on the primary → flip every Story to
167
225
  `agent::ready`.
168
226
 
@@ -196,7 +254,7 @@ gates, and abandoned authoring sessions do not accumulate under `temp/`.
196
254
  ## How the source ids reach persist
197
255
 
198
256
  In `--tickets` mode persist needs to know which ids were fetched. It resolves
199
- them **envelope-first** (Story #4554):
257
+ them **envelope-first**:
200
258
 
201
259
  | Channel | When it wins |
202
260
  | --- | --- |
@@ -108,10 +108,7 @@ PowerShell `Get-CimInstance Win32_Process`, terminating them with
108
108
 
109
109
  > **Not a slash command (decision overturned).** The drain is **not** a
110
110
  > `/drain-pending-cleanup` slash command — it was demoted to a
111
- > directly-runnable script (Story #3706, overturning the
112
- > `docs/decisions.md` matrix row that originally kept it as a command).
113
- > The wave-era automatic callers were deleted with the epic-runner in
114
- > the v2 cutover, so the drain is now operator-driven:
111
+ > directly-runnable script. The drain is operator-driven:
115
112
  > `node .agents/scripts/drain-pending-cleanup.js`.
116
113
 
117
114
  ### When to run it manually