mandrel 2.9.0 → 2.11.0

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