mandrel 2.10.0 → 2.12.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/docs/configuration.md +35 -33
- package/.agents/rules/orchestration-error-handling.md +9 -1
- package/.agents/schemas/agentrc.schema.json +13 -8
- package/.agents/scripts/acceptance-eval.js +9 -5
- package/.agents/scripts/lib/audit-suite/audit-rules-reader.js +48 -0
- package/.agents/scripts/lib/audit-suite/selector.js +1 -26
- package/.agents/scripts/lib/baselines/env-overrides.js +33 -0
- package/.agents/scripts/lib/baselines/git-base.js +0 -0
- package/.agents/scripts/lib/baselines/preview-gates.js +5 -0
- package/.agents/scripts/lib/config/gates/maintainability.schema.js +10 -1
- package/.agents/scripts/lib/config/quality.js +13 -0
- package/.agents/scripts/lib/config-settings-schema.js +12 -16
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +45 -0
- package/.agents/scripts/lib/orchestration/check-baselines/phases/evaluate.js +97 -4
- package/.agents/scripts/lib/orchestration/check-baselines/phases/parse-args.js +7 -0
- package/.agents/scripts/lib/orchestration/complexity-gate.js +561 -184
- package/.agents/scripts/lib/orchestration/plan-context.js +69 -10
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +117 -60
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +21 -15
- package/.agents/scripts/lib/orchestration/resolve-stories.js +28 -7
- package/.agents/scripts/lib/orchestration/review-depth.js +9 -4
- package/.agents/scripts/lib/orchestration/single-story-close/gate-log.js +186 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +21 -3
- package/.agents/scripts/lib/orchestration/spec-budget.js +78 -0
- package/.agents/scripts/lib/orchestration/story-body-gate.js +72 -0
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +6 -0
- package/.agents/scripts/lib/orchestration/ticket-validator.js +18 -62
- package/.agents/scripts/plan-context.js +23 -5
- package/.agents/scripts/resolve-stories.js +2 -0
- package/.agents/workflows/deliver.md +28 -28
- package/.agents/workflows/helpers/acceptance-self-eval.md +16 -5
- package/.agents/workflows/helpers/deliver-digest.md +126 -0
- package/.agents/workflows/helpers/deliver-reference.md +30 -5
- package/.agents/workflows/helpers/deliver-story-reference.md +38 -12
- package/.agents/workflows/helpers/deliver-story.md +34 -37
- package/.agents/workflows/helpers/plan-reference.md +79 -44
- package/.agents/workflows/plan.md +11 -10
- package/docs/CHANGELOG.md +31 -0
- package/lib/cli/registry.js +31 -14
- package/lib/migrations/index.js +2 -0
- package/lib/migrations/steps/2.11.0-retire-max-seed-words.js +92 -0
- package/package.json +1 -1
|
@@ -10,7 +10,9 @@ description:
|
|
|
10
10
|
> **Lean spine.** Happy path + gate list. Sequencing edge cases, dispatch
|
|
11
11
|
> mechanics, lite-route inline execution, checklist threading, ceremony, and
|
|
12
12
|
> the per-run epilogue live in the on-demand
|
|
13
|
-
> [`helpers/deliver-reference.md`](helpers/deliver-reference.md).
|
|
13
|
+
> [`helpers/deliver-reference.md`](helpers/deliver-reference.md). What every
|
|
14
|
+
> delivery always needs is bundled into one read:
|
|
15
|
+
> [`helpers/deliver-digest.md`](helpers/deliver-digest.md) (Story #4736).
|
|
14
16
|
|
|
15
17
|
## Role
|
|
16
18
|
|
|
@@ -20,18 +22,22 @@ owns input resolution and sequencing only — every Story runs through
|
|
|
20
22
|
`epic/<id>` integration branch, no `--no-ff` wave merges.
|
|
21
23
|
|
|
22
24
|
The dependency graph is **discovered, not declared**: `resolve-stories.js`
|
|
23
|
-
reads it from live state (body edges ∪ native GitHub `blocked_by` edges,
|
|
24
|
-
blocker resolved against its real issue state). You never hand it a graph
|
|
25
|
+
reads it from live state (body edges ∪ native GitHub `blocked_by` edges, each
|
|
26
|
+
blocker resolved against its real issue state). You never hand it a graph and
|
|
25
27
|
there is no batch label — which is what lets you deliver Stories **across plan
|
|
26
|
-
runs and over time**.
|
|
27
|
-
|
|
28
|
+
runs and over time**. `plan-run::<id>` is filter metadata, never a resolution
|
|
29
|
+
input.
|
|
30
|
+
Per-Story routes are **body-derived** too (#4722); `route::lite` is a hint
|
|
31
|
+
only. Ahead of that: a **single-Story run runs the engine inline** whatever the
|
|
32
|
+
shape (#4736) — sub-agent isolation only earns its cost against a concurrent
|
|
33
|
+
sibling.
|
|
28
34
|
|
|
29
35
|
## Inputs
|
|
30
36
|
|
|
31
37
|
| Invocation | Behavior |
|
|
32
38
|
| --- | --- |
|
|
33
|
-
| `/deliver <storyId>` | Deliver one Story via `helpers/deliver-story.md
|
|
34
|
-
| `/deliver <storyId> <storyId> ...` | Resolve the set with `resolve-stories.js`, then sequence by the discovered graph via `stories-wave-tick.js
|
|
39
|
+
| `/deliver <storyId>` | Deliver one Story via `helpers/deliver-story.md`, executed **inline in this session** — no `story-worker` spawn. |
|
|
40
|
+
| `/deliver <storyId> <storyId> ...` | Resolve the set with `resolve-stories.js`, then sequence by the discovered graph via `stories-wave-tick.js`, dispatching role-scoped sub-agents. Default concurrency is **3**. |
|
|
35
41
|
|
|
36
42
|
Any named ticket that is not `type::story`, or still carrying an `Epic: #N`
|
|
37
43
|
footer, is a **hard error** naming the id and the fix (close or re-plan as a v2
|
|
@@ -41,18 +47,16 @@ Story). Resolution refuses the whole set rather than silently under-delivering.
|
|
|
41
47
|
|
|
42
48
|
| Flag | Meaning |
|
|
43
49
|
| --- | --- |
|
|
44
|
-
| `--concurrency <n>` | **Optional** per-run override of the fan-out cap. Omit it to honor `delivery.deliverRunner.concurrencyCap` (config default **3**,
|
|
50
|
+
| `--concurrency <n>` | **Optional** per-run override of the fan-out cap. Omit it to honor `delivery.deliverRunner.concurrencyCap` (config default **3**, incl. any `.agentrc.local.json` override); pass **only** for a one-run cap. `1` = sequential. |
|
|
45
51
|
| `--yes` | Suppress the multi-Story confirmation gate. |
|
|
46
52
|
| `--steal` | Forwarded to `single-story-init.js` / lease steal. |
|
|
47
53
|
| `--wait-merge` | Force close-and-land (the default; `delivery.routing.closeAndLand`). |
|
|
48
54
|
| `--no-wait-merge` | Opt out; stop at `agent::closing` for a human land. |
|
|
49
55
|
|
|
50
56
|
**Operator-merge implies no-wait.** `--no-auto-merge` and
|
|
51
|
-
`delivery.ci.autoMerge: "strict"`
|
|
52
|
-
`agent::
|
|
53
|
-
(
|
|
54
|
-
still waits and still blocks, because that is a fault to report, not an operator
|
|
55
|
-
decision to respect.
|
|
57
|
+
`delivery.ci.autoMerge: "strict"` rest the Story at `agent::closing`, not
|
|
58
|
+
`agent::blocked` — a genuine *arm failure* still waits and still blocks
|
|
59
|
+
([`helpers/deliver-reference.md` § Operator-merge](helpers/deliver-reference.md)).
|
|
56
60
|
|
|
57
61
|
## Procedure
|
|
58
62
|
|
|
@@ -62,8 +66,8 @@ decision to respect.
|
|
|
62
66
|
present the order in step 2. You do **not** thread them into step 3 — the
|
|
63
67
|
tick re-resolves the graph itself every beat. Resolution hard-errors
|
|
64
68
|
(exit 1) on a named id that is not a Story, carries an `Epic: #N` footer, or
|
|
65
|
-
whose native edges cannot be read — a missing gate would co-dispatch
|
|
66
|
-
|
|
69
|
+
whose native edges cannot be read — a missing gate would co-dispatch against
|
|
70
|
+
an unlanded blocker.
|
|
67
71
|
|
|
68
72
|
2. **Confirm (N>1).** Present the order; wait unless `--yes`.
|
|
69
73
|
|
|
@@ -76,10 +80,8 @@ decision to respect.
|
|
|
76
80
|
```
|
|
77
81
|
|
|
78
82
|
**Do not add `--concurrency` unless the operator explicitly asked for a
|
|
79
|
-
per-run cap
|
|
80
|
-
|
|
81
|
-
override. An explicit `--concurrency <n>` wins over config for that run, so a
|
|
82
|
-
filled-in literal (e.g. `3`) silently defeats the operator's override.
|
|
83
|
+
per-run cap** — an explicit value wins over config, so a filled-in literal
|
|
84
|
+
silently defeats a `.agentrc.local.json` override (see Flags).
|
|
83
85
|
|
|
84
86
|
Each beat re-probes live state to derive done / in-flight itself; you never
|
|
85
87
|
compute them (Story #4594). `--dispatched` is the one thing you must supply —
|
|
@@ -127,10 +129,10 @@ review depth reading the same level) and the mechanism table:
|
|
|
127
129
|
|
|
128
130
|
## Reading a Story's outcome
|
|
129
131
|
|
|
130
|
-
Each Story's delivery ends in exactly one schema-validated terminal envelope
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
132
|
+
Each Story's delivery ends in exactly one schema-validated terminal envelope —
|
|
133
|
+
`landed` | `pending` | `blocked` | `failed`. Statuses, exits, and fields:
|
|
134
|
+
[`helpers/deliver-digest.md`](helpers/deliver-digest.md) § 5, over the shipped
|
|
135
|
+
[schema](../schemas/story-deliver-terminal.schema.json) (Story #4543).
|
|
134
136
|
|
|
135
137
|
`pending` is **not** a failure: the bounded merge wait expired with the PR
|
|
136
138
|
healthy (or a human owns the merge), nothing was mutated, and the
|
|
@@ -144,11 +146,9 @@ For a Story in an unclear state — including the merged-but-label-stale one a
|
|
|
144
146
|
|
|
145
147
|
## Constraints
|
|
146
148
|
|
|
147
|
-
- **Land or block — never a silent local build
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
(`delivery.routing.closeAndLand: true`); use `--no-wait-merge` only when a
|
|
151
|
-
human lands the PR.
|
|
149
|
+
- **Land or block — never a silent local build** (digest § 2). Attended
|
|
150
|
+
delivers default to close-and-land (`delivery.routing.closeAndLand: true`);
|
|
151
|
+
use `--no-wait-merge` only when a human lands the PR.
|
|
152
152
|
- `/deliver` never plans — tickets come from [`/plan`](plan.md). The router
|
|
153
153
|
performs no git/label mutations; `deliver-story` owns every script.
|
|
154
154
|
|
|
@@ -30,10 +30,19 @@ mid-delivery, and evaluates the actual work product.
|
|
|
30
30
|
|
|
31
31
|
## Per round
|
|
32
32
|
|
|
33
|
-
1. **Eval pass
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
1. **Eval pass — one verdict-owner per cluster (Story #4723).** Exactly
|
|
34
|
+
**one** pass authors each cluster's verdict: the **fresh-context critic**
|
|
35
|
+
when the ceremony routing below resolves `fresh` (a sub-agent via the
|
|
36
|
+
`Agent` tool, *not* a continuation of your implementing turn — the
|
|
37
|
+
evaluator does not grade its own homework), or the **inline self-eval**
|
|
38
|
+
when it resolves `inline`. The resolved decision names the owner
|
|
39
|
+
explicitly (`verdictOwner: 'fresh-critic' | 'inline-self-eval'` from
|
|
40
|
+
`resolveCeremonyForRisk`). **Never run both**, and never run a
|
|
41
|
+
preliminary self-assessment pass before dispatching the fresh critic —
|
|
42
|
+
the redundant pre-pass buys no measurable quality and roughly triples
|
|
43
|
+
the acceptance-block cost. Step 2's gate is the deterministic **scorer**
|
|
44
|
+
of the one authored verdict, not a second (or third) pass over the
|
|
45
|
+
criteria.
|
|
37
46
|
|
|
38
47
|
> **Sub-agent type + derived-level ceremony (Epic #4478, M7-B).** When
|
|
39
48
|
> `delivery.routing.roleScopedAgents` is enabled (the **default**), dispatch
|
|
@@ -129,7 +138,9 @@ mid-delivery, and evaluates the actual work product.
|
|
|
129
138
|
one `{ index, criterion, verdict: met|partial|unmet, evidence,
|
|
130
139
|
verifyEvidence[] }` record per acceptance item.
|
|
131
140
|
2. **Decide.** Run the gate against the verdict (the caller's Step 1a names the
|
|
132
|
-
exact invocation — omit `--epic`)
|
|
141
|
+
exact invocation — omit `--epic`). The gate **scores the single verdict
|
|
142
|
+
the round's owner authored** — schema validation, round cap, decision —
|
|
143
|
+
and never re-scores the criteria itself (Story #4723):
|
|
133
144
|
|
|
134
145
|
```bash
|
|
135
146
|
node <main-repo>/.agents/scripts/acceptance-eval.js \
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: >-
|
|
3
|
+
The deliver path's one bundled framework read (Story #4736). Carries what
|
|
4
|
+
every Story delivery always needs — dispatch decision, engine invariants,
|
|
5
|
+
the change-set/ceremony incantation, the acceptance-eval gate, and the
|
|
6
|
+
terminal envelope contract — so the engine reads one file instead of
|
|
7
|
+
re-reading the helper/schema set each session.
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Deliver digest (read once per session)
|
|
11
|
+
|
|
12
|
+
> **Bundle, not a procedure.** [`deliver-story.md`](deliver-story.md) is still
|
|
13
|
+
> the steps. This file is the material those steps referenced across five
|
|
14
|
+
> separate files and a JSON schema — bundled so one read covers the whole happy
|
|
15
|
+
> path. Situational material (lease preflight, recovery routers, merge-wait
|
|
16
|
+
> budgets, CI remediation) stays on demand in
|
|
17
|
+
> [`deliver-story-reference.md`](deliver-story-reference.md) and
|
|
18
|
+
> [`deliver-reference.md`](deliver-reference.md); read those **only** when an
|
|
19
|
+
> envelope or a failure routes you there.
|
|
20
|
+
|
|
21
|
+
## 1. Dispatch — where the engine runs
|
|
22
|
+
|
|
23
|
+
Read `stories[].dispatchMode` from the `resolve-stories.js` envelope. Two
|
|
24
|
+
rules produce it, in order:
|
|
25
|
+
|
|
26
|
+
1. **Run topology (#4736).** A run resolving **one** Story is `inline`
|
|
27
|
+
whatever its shape — sub-agent isolation is load-bearing only against a
|
|
28
|
+
*concurrent* sibling racing the same checkout, and a one-Story run has none.
|
|
29
|
+
2. **Body shape (#4722).** In a multi-Story run, a lite-shaped body is
|
|
30
|
+
`inline`; a full-shaped body, an unparseable one, or a footprint touching a
|
|
31
|
+
sensitive-path class is `subagent`. The `route::lite` label is a
|
|
32
|
+
human-visible hint, never the control signal.
|
|
33
|
+
|
|
34
|
+
`inline` removes model-side fan-out only — no `story-worker` boot, no fresh
|
|
35
|
+
acceptance-critic spawn. **`subagent` and `inline` run the same engine**: same
|
|
36
|
+
gates, same PR to `main`, same terminal envelope, byte for byte.
|
|
37
|
+
|
|
38
|
+
## 2. Engine invariants
|
|
39
|
+
|
|
40
|
+
| Trait | Contract |
|
|
41
|
+
| --- | --- |
|
|
42
|
+
| Ticket type | `type::story` only; an `Epic: #N` footer means **stop and re-plan** |
|
|
43
|
+
| Branch | `story-<id>`, seeded from `project.baseBranch` (`main`) |
|
|
44
|
+
| Merge target | `main` via PR (squash + required checks) — never a direct push |
|
|
45
|
+
| Integration branch | **None** — no `epic/<id>`, no `--no-ff` wave merge |
|
|
46
|
+
| Gates | Every close gate runs regardless of route; no route bypasses one |
|
|
47
|
+
| State | Only via `update-ticket-state.js --ticket <id> --state <state>` |
|
|
48
|
+
| Paths | Prefix every path-based tool with the absolute `workCwd` — `cd` does not scope them |
|
|
49
|
+
|
|
50
|
+
**Land or block.** Worktree → `story-<id>` → close-validation → PR to `main` is
|
|
51
|
+
the only sanctioned landing. A silent local build is not a delivery.
|
|
52
|
+
|
|
53
|
+
## 3. Change set — computed once, handed to everyone
|
|
54
|
+
|
|
55
|
+
One enumeration per Story (#4593). A critic that re-runs its own `git diff`
|
|
56
|
+
can score a different set than the one that routed it:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
node --input-type=module -e '
|
|
60
|
+
import { computeChangeSet } from "<main-repo>/.agents/scripts/lib/orchestration/change-set.js";
|
|
61
|
+
const { files } = computeChangeSet({ baseRef: "main", headRef: "story-<storyId>" });
|
|
62
|
+
console.log(JSON.stringify(files));
|
|
63
|
+
'
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Derive the level with `deriveChangeLevel`
|
|
67
|
+
([`review-depth.js`](../../scripts/lib/orchestration/review-depth.js)) over
|
|
68
|
+
that one list: a sensitive path registered in `audit-rules.json` → `high`, none
|
|
69
|
+
→ `low`, an unenumerable diff (`files === null`) → `null`. Resolve
|
|
70
|
+
fresh-vs-inline critics with `resolveCeremonyForRisk`
|
|
71
|
+
([`ceremony-routing.js`](../../scripts/lib/orchestration/ceremony-routing.js)):
|
|
72
|
+
`minimal` → always inline, `strict` → always fresh, `standard` → `high`/`null`
|
|
73
|
+
→ fresh and `low` → inline unless the `freshCriticSampleRate` floor forces
|
|
74
|
+
fresh. An `inline` dispatch mode overrides all of it to inline critics. Close's
|
|
75
|
+
`review-depth.js` reads the same derived level, so the two cannot disagree.
|
|
76
|
+
|
|
77
|
+
## 4. Acceptance self-eval (Step 1a, required)
|
|
78
|
+
|
|
79
|
+
**One verdict-owner per cluster** (#4723) — the fresh critic *or* the inline
|
|
80
|
+
self-eval, named by `verdictOwner`, never both and never a warm-up pass. It
|
|
81
|
+
scores each `acceptance[]` item against the change set above, with `verify[]`
|
|
82
|
+
output as evidence. Bounded by `delivery.acceptanceEval.maxRounds` (default 2).
|
|
83
|
+
Then score the authored verdict:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
node <main-repo>/.agents/scripts/acceptance-eval.js \
|
|
87
|
+
--story <storyId> --verdict <verdict-path>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
`proceed` → close. `redraft` → one more round inside the cap. `block` → **do
|
|
91
|
+
not close**: post a `friction` comment and flip `agent::blocked`.
|
|
92
|
+
Per-round mechanics: [`acceptance-self-eval.md`](acceptance-self-eval.md).
|
|
93
|
+
|
|
94
|
+
## 5. Terminal envelope — the return contract
|
|
95
|
+
|
|
96
|
+
`single-story-close.js` emits exactly one envelope on stdout between
|
|
97
|
+
`--- STORY DELIVER TERMINAL ---` markers, schema-validated against
|
|
98
|
+
[`story-deliver-terminal.schema.json`](../../schemas/story-deliver-terminal.schema.json)
|
|
99
|
+
(#4543 — the SSOT; read the JSON only when you need a field this table omits).
|
|
100
|
+
Relay it verbatim; never hand-compose one, never substitute prose.
|
|
101
|
+
|
|
102
|
+
| `status` | Exit | Meaning | You do |
|
|
103
|
+
| --- | --- | --- | --- |
|
|
104
|
+
| `landed` | 0 | PR merged, `agent::done`, tail ran (`tail.*: false` degrades the report, not the land) | Relay it. Done. |
|
|
105
|
+
| `pending` | 3 | **Resumable, not a failure** — the bounded wait expired healthy, or a human owns the merge. Nothing was mutated. | Run `nextCommand`. |
|
|
106
|
+
| `blocked` | 1 | Hard block; `blocked.blockClass` names it | `checks-failed` → fix + resume; else relay |
|
|
107
|
+
| `failed` | 1 | A phase crashed; `phase` names which | Diagnose, fix, re-run close |
|
|
108
|
+
|
|
109
|
+
Required fields: `kind` (`story-deliver-terminal`), `storyId`, `status`,
|
|
110
|
+
`phase`, `elapsedSeconds`, `nextCommand`. `phase` is one of `init`,
|
|
111
|
+
`wrong-tree-guard`, `close-validation`, `base-sync`, `push`, `pull-request`,
|
|
112
|
+
`code-review`, `auto-merge`, `confirm-merge`, `post-land`, `done`. `gates`
|
|
113
|
+
reports every gate as `passed` / `failed` / `skipped` — a skipped gate is
|
|
114
|
+
reported, never omitted, so a missing gate is never read as a passing one.
|
|
115
|
+
|
|
116
|
+
**Gate output is captured, not streamed (#4736).** Close writes gate lines to
|
|
117
|
+
`temp/orchestration/close-gates-<storyId>.log` and reports a one-line digest on
|
|
118
|
+
success; a failed gate replays its tail inline. `AGENT_LOG_LEVEL=verbose`
|
|
119
|
+
restores live streaming.
|
|
120
|
+
|
|
121
|
+
## 6. When to leave this file
|
|
122
|
+
|
|
123
|
+
- Unclear state / a re-run refusal → `deliver-recover.js --story <id>` (read-only).
|
|
124
|
+
- Lease, sweep, worktree-scope detail → [`deliver-story-reference.md`](deliver-story-reference.md).
|
|
125
|
+
- CI red after the PR opens → [`rules/ci-remediation.md`](../../rules/ci-remediation.md).
|
|
126
|
+
- Sequencing, epilogue, checklist threading → [`deliver-reference.md`](deliver-reference.md).
|
|
@@ -51,19 +51,33 @@ probe logs a warning and leans on init's lease refusal alone.
|
|
|
51
51
|
|
|
52
52
|
## Dispatch mechanics (role-scoped by default)
|
|
53
53
|
|
|
54
|
-
**
|
|
54
|
+
**A single-Story run executes inline (Story #4736).** Sub-agent isolation is
|
|
55
|
+
load-bearing only for **concurrent** dispatch — two workers sharing a checkout
|
|
56
|
+
would race on worktrees and branch refs — so a run resolving exactly one Story
|
|
57
|
+
has no sibling to isolate from and pays the spawn premium for nothing (a boot is
|
|
58
|
+
a cache write at full rate; an inline continuation is a cache read at ~10%).
|
|
59
|
+
`resolve-stories.js` already reports it: a one-id run comes back with
|
|
60
|
+
`dispatchMode: "inline"` whatever the Story's shape. Role-scoped spawning is
|
|
61
|
+
retained in full for multi-Story waves, and the rule changes **where** the
|
|
62
|
+
engine runs, never what runs — gates, PR, and terminal envelope are identical.
|
|
63
|
+
|
|
64
|
+
**Lite-shaped Stories execute inline (Story #4722).** Before spawning anything,
|
|
55
65
|
read the Story's `dispatchMode` from the resolver envelope
|
|
56
66
|
(`stories[].dispatchMode`, derived by `resolveStoryDispatchMode` in
|
|
57
|
-
`lib/orchestration/complexity-gate.js` from the
|
|
58
|
-
|
|
67
|
+
`lib/orchestration/complexity-gate.js` **from the fetched Story body's own
|
|
68
|
+
shape** — `changes[]` count, acceptance count, creates-vs-refactors mix, and
|
|
69
|
+
sensitive-path classes; the `route::lite` label is a human-visible hint only,
|
|
70
|
+
never the control signal, so a lost or never-written label cannot misroute
|
|
71
|
+
delivery): a Story with `dispatchMode: "inline"` executes
|
|
59
72
|
[`deliver-story.md`](deliver-story.md) **inline in this session** — no
|
|
60
73
|
`story-worker` sub-agent boot and no fresh acceptance-critic sub-agents
|
|
61
74
|
(sub-agent boots are the dominant deliver-phase token cost at trivial scope) —
|
|
62
75
|
threading the same `docsDigestPath` / `checklistPath` / change-set discipline
|
|
63
76
|
as a spawned worker. Inline removes model-side fan-out only: every
|
|
64
77
|
`single-story-close.js` gate, the PR to `main`, and the terminal envelope are
|
|
65
|
-
identical.
|
|
66
|
-
|
|
78
|
+
identical. Everything else — a full-shaped body, a missing/unparseable body,
|
|
79
|
+
or a footprint intersecting a sensitive-path class (sensitivity wins and
|
|
80
|
+
keeps the fresh acceptance critic) — takes the standard sub-agent path.
|
|
67
81
|
|
|
68
82
|
**Dispatch each `ready` Story (role-scoped by default).** When
|
|
69
83
|
`delivery.routing.roleScopedAgents` is enabled (the **default**) and the host
|
|
@@ -110,6 +124,17 @@ execute it directly, in this turn, threading the same `docsDigestPath` /
|
|
|
110
124
|
content, execute directly without a re-read turn. The engine, gates, and
|
|
111
125
|
terminal envelope are identical either way — only the isolation differs.
|
|
112
126
|
|
|
127
|
+
## Operator-merge implies no-wait
|
|
128
|
+
|
|
129
|
+
`--no-auto-merge` and `delivery.ci.autoMerge: "strict"` leave the PR
|
|
130
|
+
deliberately un-armed: there is nothing for close to land, so the Story rests
|
|
131
|
+
at `agent::closing` for the human merge and is **not** flipped to
|
|
132
|
+
`agent::blocked` — `--wait-merge` does not override this, because the operator
|
|
133
|
+
owning the merge is a decision to respect, not a fault to report. A genuine
|
|
134
|
+
*arm failure* is the opposite case: nobody chose it, so close still waits and
|
|
135
|
+
still blocks. That asymmetry is what keeps the must-land contract intact
|
|
136
|
+
without misfiling deliberate human merges as blocks.
|
|
137
|
+
|
|
113
138
|
## Per-run epilogue (N>1)
|
|
114
139
|
|
|
115
140
|
Once the sequence reports `epilogueDue: true` (every Story done), keyed on the
|
|
@@ -125,8 +125,7 @@ The v2 engine's trait table:
|
|
|
125
125
|
| Spec / slices | Folded `## Spec` + optional `## Slicing` checkpoints in-session |
|
|
126
126
|
| Ceremony | Per-Story, routed off the derived change level via `ceremony-routing.js` |
|
|
127
127
|
|
|
128
|
-
**Ceremony-lite Stories still land through this engine unchanged (Story #4683).** A Story
|
|
129
|
-
`complexityRoute.route === "lite"`) collapses only the *advisory* plan/deliver
|
|
128
|
+
**Ceremony-lite Stories still land through this engine unchanged (Story #4683).** A lite-routed Story collapses only the *advisory* plan/deliver
|
|
130
129
|
ceremony — the fresh-critic / Tech-Spec authoring a one-artifact scope does
|
|
131
130
|
not earn. It does **not** get a cheaper landing: the close-validation gates
|
|
132
131
|
(lint / test / format / coverage / CRAP / maintainability), the PR to `main`,
|
|
@@ -134,15 +133,22 @@ and the `rules/security-baseline.md` MUSTs all run exactly as for a
|
|
|
134
133
|
full-ceremony Story. The lite route's `preserves` field is the machine-readable
|
|
135
134
|
record of those non-negotiables; there is no lite-specific gate bypass.
|
|
136
135
|
|
|
137
|
-
**
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
136
|
+
**Deliver derives the route from the Story body's shape (Story #4722).**
|
|
137
|
+
Persist stamps a lite cohort's Stories with the `route::lite` label as a
|
|
138
|
+
*human-visible hint only* (and ledgers the authored verdict — recorded
|
|
139
|
+
reason plus per-Story shape evidence — on the `story-plan-state`
|
|
140
|
+
checkpoint); the label is never the control signal. `/deliver` computes the
|
|
141
|
+
route from the fetched Story body via `resolveStoryDispatchMode`
|
|
142
|
+
(`lib/orchestration/complexity-gate.js`) — the same shape taxonomy
|
|
143
|
+
`deriveChangeLevel` applies to the landed diff at close: `changes[]` count,
|
|
144
|
+
acceptance count, creates-vs-refactors mix, sensitive-path classes. A
|
|
145
|
+
lite-shaped Story executes **inline in the deliver session** — even when the
|
|
146
|
+
label is absent or its write failed — with no `story-worker` sub-agent boot,
|
|
147
|
+
and the Step 1a acceptance self-eval runs its critics **inline** (no
|
|
144
148
|
fresh-context acceptance-critic sub-agent dispatch; sub-agent boots are the
|
|
145
|
-
dominant deliver-phase token cost at trivial scope).
|
|
149
|
+
dominant deliver-phase token cost at trivial scope). A footprint
|
|
150
|
+
intersecting a sensitive-path class derives `full` — sensitivity wins, and
|
|
151
|
+
the Story keeps its fresh acceptance critic. Inline execution
|
|
146
152
|
changes the isolation only: the engine, every script gate, and the
|
|
147
153
|
terminal envelope are byte-identical either way.
|
|
148
154
|
|
|
@@ -175,6 +181,20 @@ directly.
|
|
|
175
181
|
|
|
176
182
|
### Step 1a — self-eval mechanics
|
|
177
183
|
|
|
184
|
+
**One verdict-owner per cluster (Story #4723).** The ceremony routing's
|
|
185
|
+
resolved decision names each cluster's single verdict owner
|
|
186
|
+
(`verdictOwner: 'fresh-critic' | 'inline-self-eval'` from
|
|
187
|
+
`resolveCeremonyForRisk`): the fresh maker-blind critic when sensitivity
|
|
188
|
+
routes the cluster `fresh`, the contract-identical inline self-eval when it
|
|
189
|
+
routes `inline`. Exactly one pass authors the verdict — never both, and
|
|
190
|
+
never a preliminary self-assessment pass before dispatching the fresh
|
|
191
|
+
critic (the redundant pre-pass buys no measurable quality and roughly
|
|
192
|
+
triples the acceptance-block cost). `acceptance-eval.js` is the
|
|
193
|
+
deterministic **scorer** of that one authored verdict — schema validation,
|
|
194
|
+
round cap, proceed / redraft / block — not an independent additional pass
|
|
195
|
+
over the criteria. The M4-B floor holds: one verdict per cluster, the
|
|
196
|
+
cluster count owned by `acceptance-clusters.js` alone.
|
|
197
|
+
|
|
178
198
|
**Critic evidence-share (Story #4250).** When the critic runs a `verify[]`
|
|
179
199
|
command that is byte-identical to a close gate (`lint` / `typecheck`), it
|
|
180
200
|
records the pass into the Story evidence keyspace via `--standalone` so
|
|
@@ -227,8 +247,8 @@ Resolve fresh-vs-inline acceptance critics per AC-cluster with
|
|
|
227
247
|
floor forces `fresh`). Review depth reads the same derived level via
|
|
228
248
|
`review-depth.js` inside close, so the two decisions cannot disagree.
|
|
229
249
|
|
|
230
|
-
**Lite-route override (Story #
|
|
231
|
-
|
|
250
|
+
**Lite-route override (Story #4722).** When the Story's body derives the
|
|
251
|
+
lite shape (`resolveStoryDispatchMode` → `inline`), run every
|
|
232
252
|
acceptance critic **inline** — do not spawn fresh-context critic sub-agents
|
|
233
253
|
regardless of what the profile would otherwise resolve. The self-eval rigor
|
|
234
254
|
(scoring each `acceptance[]` item against the one computed change set, with
|
|
@@ -321,6 +341,12 @@ The `single-story-close.js` script, in order:
|
|
|
321
341
|
|
|
322
342
|
1. Runs the close-validation gates against `baseBranch` as the baseline.
|
|
323
343
|
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
|
|
345
|
+
goes to `temp/orchestration/close-gates-<storyId>.log`; a clean run reports
|
|
346
|
+
one digest line naming that artifact, and a **failed** gate replays its
|
|
347
|
+
captured tail inline so the evidence is in front of you without opening a
|
|
348
|
+
file. Read the artifact when you need the full text — or re-run under
|
|
349
|
+
`AGENT_LOG_LEVEL=verbose` for live streaming.
|
|
324
350
|
1a. **Syncs the Story branch from `origin/<baseBranch>`** before push
|
|
325
351
|
(Story #2580). Runs `git fetch origin <baseBranch>` followed by
|
|
326
352
|
`git merge --no-edit origin/<baseBranch>` inside the worktree. This
|
|
@@ -11,11 +11,19 @@ description:
|
|
|
11
11
|
> reference detail lives in
|
|
12
12
|
> [`deliver-story-reference.md`](deliver-story-reference.md) ("reference"
|
|
13
13
|
> below); consult on demand. Invoked by [`/deliver`](../deliver.md).
|
|
14
|
+
>
|
|
15
|
+
> **Read [`deliver-digest.md`](deliver-digest.md) once, first.** It is the
|
|
16
|
+
> one bundled read of what every delivery needs — dispatch decision, engine
|
|
17
|
+
> invariants, the change-set/ceremony incantation, the acceptance-eval gate,
|
|
18
|
+
> 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.
|
|
14
21
|
|
|
15
22
|
## Overview
|
|
16
23
|
|
|
17
|
-
The **one** delivery engine in v2 — every Story (
|
|
18
|
-
inline with inline critics; engine, gates, envelope
|
|
24
|
+
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):
|
|
19
27
|
|
|
20
28
|
```text
|
|
21
29
|
single-story-init.js → implement + commits → derived-level ceremony
|
|
@@ -73,15 +81,10 @@ One branch, one PR to `main`, commits against the inline `acceptance[]` /
|
|
|
73
81
|
|
|
74
82
|
### Step 1a — Bounded acceptance self-eval loop (**required**)
|
|
75
83
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
```bash
|
|
82
|
-
node <main-repo>/.agents/scripts/acceptance-eval.js \
|
|
83
|
-
--story <storyId> --verdict <verdict-path>
|
|
84
|
-
```
|
|
84
|
+
Run the loop and score it with `acceptance-eval.js` — **digest § 4** carries
|
|
85
|
+
the invocation and the proceed / redraft / block contract; per-round critic
|
|
86
|
+
mechanics live in the single-homed include
|
|
87
|
+
[`acceptance-self-eval.md`](acceptance-self-eval.md).
|
|
85
88
|
|
|
86
89
|
**`proceed`** → Step 2 then Step 3. **`block`** → **do not close**: post a
|
|
87
90
|
`friction` comment and flip `agent::blocked` — commands and the
|
|
@@ -91,16 +94,12 @@ node <main-repo>/.agents/scripts/acceptance-eval.js \
|
|
|
91
94
|
## Step 2 — Ceremony (profile + derived level)
|
|
92
95
|
|
|
93
96
|
Ceremony is `delivery.routing.ceremonyProfile` × the **derived change
|
|
94
|
-
level** — never a planner-authored verdict (Story #4542).
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
level
|
|
99
|
-
|
|
100
|
-
regardless (exact incantation and routing rules: reference § Step 2).
|
|
101
|
-
Hard gates (lint / test / format / coverage / CRAP / maintainability)
|
|
102
|
-
always run in Step 3 — the derived level never disables them; do **not**
|
|
103
|
-
pre-run the full close chain here.
|
|
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
|
|
100
|
+
inline regardless (routing edge cases: reference § Step 2). Hard gates always
|
|
101
|
+
run in Step 3 — the derived level never disables them; do **not** pre-run the
|
|
102
|
+
close chain here.
|
|
104
103
|
|
|
105
104
|
## Step 3 — Close and land (`single-story-close.js`)
|
|
106
105
|
|
|
@@ -109,15 +108,12 @@ node <main-repo>/.agents/scripts/single-story-close.js --story <storyId> --cwd <
|
|
|
109
108
|
```
|
|
110
109
|
|
|
111
110
|
**The whole delivery tail** — gates, PR, merge wait, `agent::done` flip,
|
|
112
|
-
post-land tail in one process. Run it and **branch on the terminal
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
| `pending` | 3 | **Resumable, not a failure** — wait expired healthy, or a human owns the merge. | Run `nextCommand` until resolved. |
|
|
119
|
-
| `blocked` | 1 | Hard block; `blocked.blockClass` names it. | `checks-failed` → Step 4; else relay. |
|
|
120
|
-
| `failed` | 1 | A phase crashed; `phase` names which. | Diagnose, fix, re-run close. |
|
|
111
|
+
post-land tail in one process. Run it and **branch on the terminal envelope's
|
|
112
|
+
`status`** per the table in **digest § 5** (`landed` → Step 7; `pending` → run
|
|
113
|
+
`nextCommand`; `blocked`/`checks-failed` → Step 4; `failed` → diagnose and
|
|
114
|
+
re-run). Gate output is captured to
|
|
115
|
+
`temp/orchestration/close-gates-<storyId>.log` — a clean run prints a digest
|
|
116
|
+
line, a red gate replays its tail inline (Story #4736).
|
|
121
117
|
|
|
122
118
|
Internals (gate order, base-sync, auto-merge arming), the merge-wait
|
|
123
119
|
budgets, the slow-CI **async** confirm mode (Story #4698 — launch the
|
|
@@ -142,13 +138,13 @@ recovery path **only** when the envelope routes you there:
|
|
|
142
138
|
|
|
143
139
|
## Step 7 — Return contract (**required as a sub-agent**) {#return-contract}
|
|
144
140
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
the only sanctioned no-merge ending, returned only when your own
|
|
151
|
-
exhausted (Story #1553). Reference § Step 7.
|
|
141
|
+
End your turn by relaying the validated envelope close emits between its
|
|
142
|
+
`--- STORY DELIVER TERMINAL ---` markers — never free-form prose, never a
|
|
143
|
+
hand-composed object. Statuses, exits, and required fields: **digest § 5**
|
|
144
|
+
(whose SSOT is the shipped
|
|
145
|
+
[schema](../../schemas/story-deliver-terminal.schema.json), Story #4543).
|
|
146
|
+
`pending` is the only sanctioned no-merge ending, returned only when your own
|
|
147
|
+
budget is exhausted (Story #1553). Reference § Step 7.
|
|
152
148
|
|
|
153
149
|
## Recovering a stranded Story {#recover}
|
|
154
150
|
|
|
@@ -174,6 +170,7 @@ reuses an open PR).
|
|
|
174
170
|
|
|
175
171
|
## See also
|
|
176
172
|
|
|
173
|
+
- [`deliver-digest.md`](deliver-digest.md) — the one bundled framework read.
|
|
177
174
|
- [`/deliver`](../deliver.md) — unified entry point.
|
|
178
175
|
- [`deliver-story-reference.md`](deliver-story-reference.md) — all on-demand
|
|
179
176
|
detail.
|