mandrel 2.8.0 → 2.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/agents/.markdownlint.json +4 -0
- package/.agents/agents/acceptance-critic.md +30 -5
- package/.agents/agents/auditor.md +36 -19
- package/.agents/agents/plan-critic.md +31 -5
- package/.agents/agents/story-worker.md +91 -100
- package/.agents/docs/configuration.md +39 -1
- package/.agents/docs/execution-reference.md +13 -0
- package/.agents/docs/workflows.md +1 -1
- package/.agents/instructions.md +131 -265
- package/.agents/rules/git-conventions.md +47 -83
- package/.agents/rules/orchestration-error-handling.md +28 -0
- package/.agents/schemas/agentrc.schema.json +44 -1
- package/.agents/schemas/validation-evidence.schema.json +3 -1
- package/.agents/scripts/acceptance-eval.js +1 -1
- package/.agents/scripts/apply-quality-bootstrap.js +1 -1
- package/.agents/scripts/audit-to-stories.js +51 -0
- package/.agents/scripts/check-test-temp-hygiene.js +438 -0
- package/.agents/scripts/deliver-recover.js +23 -6
- package/.agents/scripts/lib/audit-suite/index.js +5 -0
- package/.agents/scripts/lib/audit-suite/lens-diff-floor.js +179 -0
- package/.agents/scripts/lib/audit-suite/selector.js +1 -1
- package/.agents/scripts/lib/audit-to-stories/dedupe-against-github.js +120 -55
- package/.agents/scripts/lib/config/temp-paths.js +121 -1
- package/.agents/scripts/lib/config-settings-schema-delivery.js +30 -0
- package/.agents/scripts/lib/config-settings-schema.js +32 -0
- package/.agents/scripts/lib/findings/semantic-issue-search.js +43 -5
- package/.agents/scripts/lib/observability/metrics-ledger.js +217 -0
- package/.agents/scripts/lib/observability/runtime-friction.js +7 -0
- package/.agents/scripts/lib/observability/terse-result.js +114 -0
- package/.agents/scripts/lib/orchestration/complexity-gate.js +318 -0
- package/.agents/scripts/lib/orchestration/deliver-recover.js +137 -10
- package/.agents/scripts/lib/orchestration/merge-block-class.js +36 -15
- package/.agents/scripts/lib/orchestration/merge-poll.js +213 -0
- package/.agents/scripts/lib/orchestration/plan-context.js +60 -0
- package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +182 -9
- package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +29 -2
- package/.agents/scripts/lib/orchestration/plan-metrics.js +31 -82
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +102 -2
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +215 -14
- package/.agents/scripts/lib/orchestration/resolve-stories.js +7 -0
- package/.agents/scripts/lib/orchestration/review-providers/native.js +34 -16
- package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +221 -8
- package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +8 -3
- package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +230 -79
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +55 -14
- package/.agents/scripts/lib/orchestration/story-close/emit-blocked.js +9 -3
- package/.agents/scripts/lib/orchestration/story-close/phases/local-lens-review.js +89 -1
- package/.agents/scripts/lib/orchestration/story-close/phases/review-core.js +73 -0
- package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +4 -1
- package/.agents/scripts/lib/orchestration/task-body-validator.js +13 -40
- package/.agents/scripts/lib/story-body/body-format-lints.js +215 -0
- package/.agents/scripts/lib/story-body/story-body.js +18 -2
- package/.agents/scripts/lib/templates/decomposer-prompts.js +29 -6
- package/.agents/scripts/lib/test-env.js +65 -0
- package/.agents/scripts/plan-context.js +66 -9
- package/.agents/scripts/plan-critics.js +115 -3
- package/.agents/scripts/plan-persist.js +11 -1
- package/.agents/scripts/plan-run-epilogue.js +1 -1
- package/.agents/scripts/providers/github/issues.js +54 -7
- package/.agents/scripts/providers/github/search-budget.js +124 -0
- package/.agents/scripts/providers/github/search-query.js +71 -0
- package/.agents/scripts/single-story-confirm-merge.js +79 -10
- package/.agents/scripts/single-story-init.js +19 -3
- package/.agents/scripts/stories-wave-tick.js +1 -1
- package/.agents/scripts/sync-branch-from-base.js +9 -3
- package/.agents/workflows/deliver.md +86 -230
- package/.agents/workflows/helpers/deliver-reference.md +167 -0
- package/.agents/workflows/helpers/deliver-story-reference.md +203 -0
- package/.agents/workflows/helpers/deliver-story.md +114 -422
- package/.agents/workflows/helpers/plan-reference.md +211 -0
- package/.agents/workflows/plan.md +107 -279
- package/docs/CHANGELOG.md +47 -0
- package/package.json +1 -1
|
@@ -112,6 +112,209 @@ not a substitute for prefixing paths correctly.
|
|
|
112
112
|
|
|
113
113
|
---
|
|
114
114
|
|
|
115
|
+
## Engine invariants and the lite route
|
|
116
|
+
|
|
117
|
+
The v2 engine's trait table:
|
|
118
|
+
|
|
119
|
+
| Trait | v2 `/deliver-story` |
|
|
120
|
+
| --- | --- |
|
|
121
|
+
| Ticket type | `type::story` only |
|
|
122
|
+
| Branch | `story-<id>` seeded from `project.baseBranch` (`main`) |
|
|
123
|
+
| Merge target | `main` via PR (squash + required checks) |
|
|
124
|
+
| Epic integration branch | **None** — no `epic/<id>`, no `--no-ff` wave merge |
|
|
125
|
+
| Spec / slices | Folded `## Spec` + optional `## Slicing` checkpoints in-session |
|
|
126
|
+
| Ceremony | Per-Story, routed off the derived change level via `ceremony-routing.js` |
|
|
127
|
+
|
|
128
|
+
**Ceremony-lite Stories still land through this engine unchanged (Story #4683).** A Story that `/plan` routed onto the ceremony-lite path (its
|
|
129
|
+
`complexityRoute.route === "lite"`) collapses only the *advisory* plan/deliver
|
|
130
|
+
ceremony — the fresh-critic / Tech-Spec authoring a one-artifact scope does
|
|
131
|
+
not earn. It does **not** get a cheaper landing: the close-validation gates
|
|
132
|
+
(lint / test / format / coverage / CRAP / maintainability), the PR to `main`,
|
|
133
|
+
and the `rules/security-baseline.md` MUSTs all run exactly as for a
|
|
134
|
+
full-ceremony Story. The lite route's `preserves` field is the machine-readable
|
|
135
|
+
record of those non-negotiables; there is no lite-specific gate bypass.
|
|
136
|
+
|
|
137
|
+
**The lite route persists to delivery as the `route::lite` label (Story #4707).** Persist stamps every Story of a lite-routed plan with that marker
|
|
138
|
+
(and ledgers the route — including any audited planner-downgrade reason — on
|
|
139
|
+
its `story-plan-state` checkpoint); a full-routed Story carries no marker.
|
|
140
|
+
`/deliver` reads the label via `resolveStoryDispatchMode`
|
|
141
|
+
(`lib/orchestration/complexity-gate.js`) and executes a lite Story
|
|
142
|
+
**inline in the deliver session** — no `story-worker` sub-agent boot, and
|
|
143
|
+
the Step 1a acceptance self-eval runs its critics **inline** (no
|
|
144
|
+
fresh-context acceptance-critic sub-agent dispatch; sub-agent boots are the
|
|
145
|
+
dominant deliver-phase token cost at trivial scope). Inline execution
|
|
146
|
+
changes the isolation only: the engine, every script gate, and the
|
|
147
|
+
terminal envelope are byte-identical either way.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Step 1 — Implementation detail
|
|
152
|
+
|
|
153
|
+
**Docs context — digest-first.** Read a full doc only when the Story's own
|
|
154
|
+
context points you at one — do not ingest the whole
|
|
155
|
+
`project.docsContextFiles` set up front. If the caller provides a
|
|
156
|
+
`docsDigestPath`, prefer that compact outline and pull individual files on
|
|
157
|
+
demand. See [`.agents/instructions.md` § 3](../../instructions.md).
|
|
158
|
+
|
|
159
|
+
**Write-time audit checklists.** When the caller provides a `checklistPath`
|
|
160
|
+
(footprint-matched **local**-lens authoring checklists), read it before you
|
|
161
|
+
write and self-check as you author. When absent, lens-aware coverage still
|
|
162
|
+
runs maker-blind at Story-scope review inside the close subprocess. The
|
|
163
|
+
dispatch step produces `checklistPath` from the Story's predicted footprint
|
|
164
|
+
before it spawns the worker (Story #4627) — see [`/deliver`](../deliver.md).
|
|
165
|
+
|
|
166
|
+
**Pre-eval full-suite discipline (spine step 5).** Repo-invariant guards —
|
|
167
|
+
drift-guard and schema tests living outside the Story's scoped greps — are
|
|
168
|
+
the failure class that actually bounces deliveries: close-validation
|
|
169
|
+
discovers them only after the whole close pipeline has run, at several times
|
|
170
|
+
the cost of one pre-eval full-suite run.
|
|
171
|
+
|
|
172
|
+
**Conflict with `main` mid-implementation** → resolve as you would any branch
|
|
173
|
+
rebase. There is no `epic/<id>` intermediate, so the rebase base is `main`
|
|
174
|
+
directly.
|
|
175
|
+
|
|
176
|
+
### Step 1a — self-eval mechanics
|
|
177
|
+
|
|
178
|
+
**Critic evidence-share (Story #4250).** When the critic runs a `verify[]`
|
|
179
|
+
command that is byte-identical to a close gate (`lint` / `typecheck`), it
|
|
180
|
+
records the pass into the Story evidence keyspace via `--standalone` so
|
|
181
|
+
close short-circuits the gate at unchanged HEAD. Run it in the **Story
|
|
182
|
+
worktree** (`workCwd` from Step 0):
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
node <main-repo>/.agents/scripts/evidence-gate.js \
|
|
186
|
+
--standalone --scope-id <storyId> --gate lint \
|
|
187
|
+
--worktree <workCwd> -- npm run lint
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**On `decision: "block"`** — post a `friction` comment naming the unmet
|
|
191
|
+
criteria, then transition the Story to `agent::blocked`:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
node .agents/scripts/diagnose-friction.js --story <storyId> \
|
|
195
|
+
--cmd node .agents/scripts/acceptance-eval.js --story <storyId> --verdict <verdict-path>
|
|
196
|
+
node .agents/scripts/update-ticket-state.js --ticket <storyId> --state agent::blocked
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Step 2 — Ceremony detail
|
|
202
|
+
|
|
203
|
+
**Compute the change set once** (Story #4593) with the shared enumerator —
|
|
204
|
+
the same module close uses — and reuse that one list downstream:
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
node --input-type=module -e '
|
|
208
|
+
import { computeChangeSet } from "<main-repo>/.agents/scripts/lib/orchestration/change-set.js";
|
|
209
|
+
const { files } = computeChangeSet({ baseRef: "main", headRef: "story-<storyId>" });
|
|
210
|
+
console.log(JSON.stringify(files));
|
|
211
|
+
'
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Derive the level with
|
|
215
|
+
[`deriveChangeLevel`](../../scripts/lib/orchestration/review-depth.js) over
|
|
216
|
+
the one computed change-set list: a diff touching a sensitive path registered
|
|
217
|
+
in `.agents/schemas/audit-rules.json` derives `high`, one touching none
|
|
218
|
+
derives `low`, and an unenumerable diff (`files === null`) derives `null`.
|
|
219
|
+
Hand the **same** list to every acceptance critic you spawn (Step 1a) — a
|
|
220
|
+
critic that re-ran its own `git diff` could score against a different set
|
|
221
|
+
than the one that routed it.
|
|
222
|
+
|
|
223
|
+
Resolve fresh-vs-inline acceptance critics per AC-cluster with
|
|
224
|
+
[`resolveCeremonyForRisk`](../../scripts/lib/orchestration/ceremony-routing.js)
|
|
225
|
+
(`minimal` → always inline; `strict` → always fresh; `standard` →
|
|
226
|
+
`high`/`null` → `fresh`, `low` → `inline` unless the `freshCriticSampleRate`
|
|
227
|
+
floor forces `fresh`). Review depth reads the same derived level via
|
|
228
|
+
`review-depth.js` inside close, so the two decisions cannot disagree.
|
|
229
|
+
|
|
230
|
+
**Lite-route override (Story #4707).** When the Story carries the
|
|
231
|
+
`route::lite` marker (`resolveStoryDispatchMode` → `inline`), run every
|
|
232
|
+
acceptance critic **inline** — do not spawn fresh-context critic sub-agents
|
|
233
|
+
regardless of what the profile would otherwise resolve. The self-eval rigor
|
|
234
|
+
(scoring each `acceptance[]` item against the one computed change set, with
|
|
235
|
+
`verify[]` output as evidence) is unchanged; only the sub-agent boot is
|
|
236
|
+
removed. Hard gates are untouched.
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Step 3 — Merge wait, async mode, and flags
|
|
241
|
+
|
|
242
|
+
**What close does internally.** The script runs the close-validation gates
|
|
243
|
+
against `baseBranch`, syncs the Story branch from `origin/<baseBranch>`
|
|
244
|
+
(Story #2580 — the parallel-race defence), pushes `story-<id>`, opens (or
|
|
245
|
+
reuses) a PR against `baseBranch` with a `Closes #<storyId>` footer, enables
|
|
246
|
+
GitHub native auto-merge (`--auto --squash --delete-branch`) **when
|
|
247
|
+
`delivery.ci.autoMerge` is `"trust-ci"` (the default)**, flips the Story to
|
|
248
|
+
`agent::closing`, reaps the worktree, releases the lease, then **waits for
|
|
249
|
+
the merge** and — on a confirmed merge — flips `agent::done` and runs the
|
|
250
|
+
post-land tail.
|
|
251
|
+
|
|
252
|
+
**The merge wait is bounded and resumable.** Two budgets, deliberately
|
|
253
|
+
separate (`delivery.mergeWatch.*`):
|
|
254
|
+
|
|
255
|
+
- **`maxWaitSeconds`** (default 300) bounds **one invocation**, sized to fit
|
|
256
|
+
inside a single host tool invocation (~10 min ceiling) alongside the gates
|
|
257
|
+
that precede it. Expiry → `pending`. Pass `--max-wait-seconds <n>` to raise
|
|
258
|
+
it when your host has no such ceiling and you want to land in one block.
|
|
259
|
+
- **`maxBudgetSeconds`** (default 3600) bounds the **cumulative** wait across
|
|
260
|
+
resumes, anchored at the PR's `createdAt` so resuming does not restart the
|
|
261
|
+
clock. Exhausting *this* is the genuine give-up → `blocked`.
|
|
262
|
+
|
|
263
|
+
The wait probes the checks every poll: a red required check fails fast as
|
|
264
|
+
`checks-failed` instead of burning the budget, and a PR that falls behind its
|
|
265
|
+
base is brought up to date within `updateAttempts` tries.
|
|
266
|
+
|
|
267
|
+
**Async merge-confirm mode (`delivery.mergeWatch.mode: "async"`, Story #4698).** Under the default `"sync"` the merge wait runs in the foreground as
|
|
268
|
+
described above. When a consumer's CI routinely takes longer than the host
|
|
269
|
+
tool ceiling (~10 min) can hold a single close invocation, the foreground
|
|
270
|
+
wait almost always expires `pending` after burning ~5 minutes of the slot —
|
|
271
|
+
so `"async"` makes that async confirm a designed mode instead of an expiry
|
|
272
|
+
accident. In async mode the close arms auto-merge, runs one short **~60s
|
|
273
|
+
probe window** (long enough to catch an instant merge and, via the
|
|
274
|
+
head-anchored required-check predicate, an instantly-red required check),
|
|
275
|
+
then returns the standard `pending` terminal with a `nextCommand`. When you
|
|
276
|
+
receive that `pending` envelope, launch its `nextCommand`
|
|
277
|
+
(`single-story-confirm-merge.js … --wait`) as a **background** invocation —
|
|
278
|
+
host **background Bash** (`run_in_background`), whose completion re-invokes
|
|
279
|
+
the agent — and continue; do **not** sit in a foreground poll the tool
|
|
280
|
+
ceiling will kill. `single-story-confirm-merge.js` is already idempotent and
|
|
281
|
+
owns the whole tail, so no new state holder is needed, and
|
|
282
|
+
`deliver-recover.js` remains the recovery path for an orphaned confirm. The
|
|
283
|
+
cumulative `maxBudgetSeconds` give-up is unchanged; `"sync"` behaviour is
|
|
284
|
+
byte-compatible.
|
|
285
|
+
|
|
286
|
+
**`delivery.ci.autoMerge` policy.** Under the default `"trust-ci"`, GitHub
|
|
287
|
+
native auto-merge is armed and the PR squash-merges once its **required**
|
|
288
|
+
checks pass. Under `"strict"`, the close **does not arm auto-merge** — the
|
|
289
|
+
PR opens and waits for an **operator merge**, exactly as `--no-auto-merge`
|
|
290
|
+
does per-run.
|
|
291
|
+
|
|
292
|
+
**Close flags:**
|
|
293
|
+
|
|
294
|
+
- `--skip-validation` — bypass the gates. Use only when re-running close
|
|
295
|
+
after a fixed gate failure that's already known to pass.
|
|
296
|
+
- `--skip-sync` — bypass the base-sync (Story #2580). Use only after a
|
|
297
|
+
hand-resolved sync, or in tests.
|
|
298
|
+
- `--no-auto-merge` — disable auto-merge. Use when the PR materially changes
|
|
299
|
+
behaviour and warrants a pre-merge eyeball; the operator then merges via
|
|
300
|
+
the GitHub UI.
|
|
301
|
+
- `--wait-merge` — **close-and-land** (Story #4428). Forces close to poll
|
|
302
|
+
the armed PR to merge confirmation and flip `agent::done` itself. When
|
|
303
|
+
neither land flag is passed, close defaults from
|
|
304
|
+
`delivery.routing.closeAndLand` (**true**): attended and headless delivers
|
|
305
|
+
share the land-in-one-close happy path.
|
|
306
|
+
- `--no-wait-merge` — explicit opt-out that always wins. Use when the
|
|
307
|
+
operator wants the PR left at `agent::closing` for a human land (or a
|
|
308
|
+
wrapper that will invoke `single-story-confirm-merge.js` itself). Reports
|
|
309
|
+
`pending` — the work is not done, nothing is broken, and one named command
|
|
310
|
+
finishes it.
|
|
311
|
+
- `--max-wait-seconds <n>` — raise the merge wait's per-invocation bound for
|
|
312
|
+
this run (Story #4543). Use from a headless caller with no host
|
|
313
|
+
tool-invocation ceiling to keep single-block semantics without editing the
|
|
314
|
+
consumer's config.
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
115
318
|
## Step 3 — Close pipeline detail
|
|
116
319
|
|
|
117
320
|
The `single-story-close.js` script, in order:
|