mandrel 2.13.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.
- package/.agents/audit-checklists/navigability.md +1 -1
- package/.agents/docs/workflows.md +4 -5
- package/.agents/schemas/story-deliver-terminal.schema.json +60 -6
- package/.agents/scripts/acceptance-eval.js +18 -1
- package/.agents/scripts/agents-bootstrap-github.js +22 -1
- package/.agents/scripts/apply-quality-bootstrap.js +6 -0
- package/.agents/scripts/audit-labels-bootstrap.js +15 -1
- package/.agents/scripts/audit-to-stories.js +26 -1
- package/.agents/scripts/boot-sweep.js +4 -1
- package/.agents/scripts/bootstrap.js +1 -0
- package/.agents/scripts/check-arch-cycles.js +20 -0
- package/.agents/scripts/check-baselines.js +8 -2
- package/.agents/scripts/check-context-budget.js +40 -5
- package/.agents/scripts/check-dead-exports.js +21 -0
- package/.agents/scripts/check-doc-links.js +12 -1
- package/.agents/scripts/check-lifecycle-doc-drift.js +9 -0
- package/.agents/scripts/check-workflow-citations.js +332 -0
- package/.agents/scripts/deliver-light.js +71 -9
- package/.agents/scripts/deliver-recover.js +4 -1
- package/.agents/scripts/diagnose-friction.js +17 -1
- package/.agents/scripts/diagnose.js +20 -14
- package/.agents/scripts/drain-pending-cleanup.js +20 -1
- package/.agents/scripts/evidence-gate.js +20 -1
- package/.agents/scripts/generate-config-docs.js +14 -1
- package/.agents/scripts/generate-lifecycle-docs.js +14 -1
- package/.agents/scripts/generate-workflows-doc.js +14 -1
- package/.agents/scripts/git-cleanup.js +32 -1
- package/.agents/scripts/lib/cli-usage.js +174 -0
- package/.agents/scripts/lib/cli-utils.js +12 -0
- package/.agents/scripts/lib/doc-tiers.js +53 -10
- package/.agents/scripts/lib/orchestration/plan-context.js +16 -11
- package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +122 -6
- package/.agents/scripts/lib/workflow-closure.js +431 -0
- package/.agents/scripts/mandrel-update-preflight.js +9 -0
- package/.agents/scripts/nav-registry-diff.js +13 -0
- package/.agents/scripts/plan-context.js +17 -1
- package/.agents/scripts/plan-critics.js +10 -0
- package/.agents/scripts/plan-persist.js +33 -1
- package/.agents/scripts/plan-run-epilogue.js +12 -1
- package/.agents/scripts/quality-preview.js +17 -1
- package/.agents/scripts/resolve-doc-tiers.js +13 -0
- package/.agents/scripts/resolve-stories.js +1 -0
- package/.agents/scripts/resync-status-column.js +4 -1
- package/.agents/scripts/signals-view.js +11 -0
- package/.agents/scripts/single-story-close.js +24 -0
- package/.agents/scripts/single-story-confirm-merge.js +16 -0
- package/.agents/scripts/single-story-init.js +21 -1
- package/.agents/scripts/stories-wave-tick.js +1 -0
- package/.agents/scripts/sync-agentrc.js +16 -4
- package/.agents/scripts/update-ticket-state.js +23 -2
- package/.agents/workflows/audit-navigability.md +2 -2
- package/.agents/workflows/audit-to-stories.md +1 -1
- package/.agents/workflows/deliver.md +80 -81
- package/.agents/workflows/git-cleanup.md +9 -14
- package/.agents/workflows/helpers/acceptance-self-eval.md +14 -13
- package/.agents/workflows/helpers/audit-lens-core.md +2 -2
- package/.agents/workflows/helpers/code-review.md +11 -11
- package/.agents/workflows/helpers/deliver-digest.md +7 -8
- package/.agents/workflows/helpers/deliver-light.md +196 -0
- package/.agents/workflows/helpers/deliver-reference.md +46 -14
- package/.agents/workflows/helpers/deliver-story-reference.md +55 -63
- package/.agents/workflows/helpers/deliver-story.md +22 -22
- package/.agents/workflows/helpers/mandrel-sync-config.md +2 -2
- package/.agents/workflows/helpers/parallel-tooling.md +1 -2
- package/.agents/workflows/helpers/plan-reference.md +72 -14
- package/.agents/workflows/helpers/worktree-lifecycle.md +1 -4
- package/.agents/workflows/mandrel-update.md +6 -6
- package/.agents/workflows/plan.md +86 -83
- package/docs/CHANGELOG.md +22 -0
- package/package.json +2 -1
- package/.agents/workflows/deliver-light.md +0 -117
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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**
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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**
|
|
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
|
|
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
|
-
(
|
|
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"
|
|
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
|
-
**
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
- `--skip-
|
|
317
|
-
|
|
318
|
-
- `--
|
|
319
|
-
|
|
320
|
-
the GitHub UI.
|
|
321
|
-
- `--wait-merge` — **close-and-land
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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**
|
|
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
|
|
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
|
-
|
|
622
|
-
|
|
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
|
|
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
|
|
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
|
|
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**
|
|
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
|
-
|
|
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
|
|
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
|
|
20
|
-
>
|
|
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
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
49
|
-
|
|
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
|
|
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
|
|
98
|
-
|
|
99
|
-
|
|
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
|
|
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 (
|
|
120
|
-
|
|
121
|
-
|
|
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
|
|
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)
|
|
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
|
|
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
|
|
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
|
-
|
|
28
|
-
|
|
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
|
|
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 —
|
|
5
|
-
>
|
|
6
|
-
>
|
|
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
|
|
12
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
`#
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|