@ai-dossier/sched 0.14.0 → 0.16.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/README.md CHANGED
@@ -13,8 +13,8 @@ invokes an LLM** — it spawns the agent process the operator configured and rec
13
13
  the durable record (`ai-dossier runstate` / `gh` / `git`) that the spawned run leaves
14
14
  behind.
15
15
 
16
- Design: RFC-0001 *Batch Cycles* §B/C.1/D (the RFC lives on branch `docs/batch-cycles-rfc`,
17
- not yet merged to `main`; the §D state machines are frozen verbatim into the types below).
16
+ Design: RFC-0001 *Batch Cycles* ([`rfcs/0001-batch-cycles.md`](../../rfcs/0001-batch-cycles.md))
17
+ §B/C.1/D the §D state machines are frozen verbatim into the types below.
18
18
  This package is the deterministic replacement for fleet-cycle's LLM-prose supervision,
19
19
  whose named failure — slots sitting idle after a subagent finished — is a scheduling bug
20
20
  this state machine makes impossible to forget.
@@ -158,6 +158,20 @@ as the shorthand and are the fallback for any field a `tiers` entry leaves unset
158
158
  1.3.0 config with no `tiers` at all resolves identically to before — there is no on-disk
159
159
  migration, only resolution-time fallback in `resolveDispatch`.
160
160
 
161
+ Config schema moves to 1.6.0 (#562): `dispatch` gains `suite_command` — an explicit argv
162
+ override for the aggregate batch-suite command, the middle tier of the new resolution
163
+ order (`cap run test.full` manifest → `dispatch.suite_command` → a repo-detected safe
164
+ default that never forwards extra flags through an unrecognized wrapper script). Also new:
165
+ the `blocked` `BatchStatus` and `batch-blocked` journal event — an unreadable suite report
166
+ blocks the batch (worktree and every member commit preserved) instead of dissolving it,
167
+ distinct from `dissolving`'s "a red suite named no offender." `blocked` is a new
168
+ persisted-field *value*, not a new field, so no `state.json` schema-version bump or
169
+ backfill is needed on load — but it is NOT downgrade-safe: an older build's
170
+ `BATCH_STATUSES` (derived from its own `BATCH_TRANSITIONS`) will reject a `state.json`
171
+ containing a `blocked` batch with "unknown batch status", bricking that project's whole
172
+ `SchedStore.load()`. Resume or abandon any blocked batch before downgrading past this
173
+ version.
174
+
161
175
  Two engine-safety policies were explicit product decisions on #464:
162
176
 
163
177
  - **Pid identity is hybrid-verified (decision 1, option C).** Every spawn records the
@@ -235,6 +249,8 @@ against real scratch repos.
235
249
  validating → attributing → fixing (ONE bounded attempt) → validating
236
250
  → evicting (revert the member's commits) → validating
237
251
  > ⅓ of members evicted, or a revert conflict → dissolving → members requeued
252
+ suite report unreadable, after the fallback retry when one applied
253
+ → blocked → validating (nothing requeued/reverted; #562)
238
254
  awaiting-merge (CONFLICTING | auto-merge-blocked)
239
255
  → rebasing → re-validating → shipping
240
256
  → (2nd occurrence) dissolving into two half-batches
@@ -283,11 +299,12 @@ awaiting-merge (CONFLICTING | auto-merge-blocked)
283
299
  with no `anchor` or no `run_id` cannot post — the CLI requires both — so the milestone
284
300
  it could not post is journaled in full instead of vanishing.
285
301
 
286
- Ten journal events carry the detail: `suite-failed`, `attributed`, `fix-dispatched`,
302
+ Eleven journal events carry the detail: `suite-failed`, `attributed`, `fix-dispatched`,
287
303
  `fix-resolved`, `member-evicted`, `revert-conflict`, `batch-rebased`, `batch-dissolved`,
288
- `batch-split` and `milestone-post-failed`, plus `git-failed` for any git command that
289
- returned non-zero (the injected `ExecFn` collapses every git failure into `null`, so the
290
- command that produced one is always recorded).
304
+ `batch-blocked` (#562 the suite report was unreadable), `batch-split` and
305
+ `milestone-post-failed`, plus `git-failed` for any git command that returned non-zero (the
306
+ injected `ExecFn` collapses every git failure into `null`, so the command that produced one
307
+ is always recorded).
291
308
 
292
309
  Schema 1.3.0 carries the new state: `BatchEntry` gains `anchor`, `branch`, `run_id`,
293
310
  `eviction_groups`, `evictions`, `fix_attempts` and `rebase_attempts`; `QueueEntry` gains
@@ -344,11 +361,18 @@ ready → executing(member i/N) ⟲ → validating → reviewing → shipping
344
361
  → awaiting-merge → merged → deployed → reported → done
345
362
  failure rails: executing → dissolving (a member self-reports blocked)
346
363
  validating → attributing → (fixing | evicting) → validating → dissolving
364
+ validating → blocked (suite report unreadable, #562) → validating
347
365
  ```
348
366
 
349
367
  - **One shared worktree/branch per batch**, claimed once by a deterministic (no LLM)
350
- `batch-setup` step: `git branch`/`push`/`worktree add` off `base_branch`, named
351
- `batch/<id>-<date>`, plus a fresh `ai-dossier runstate mint` against the anchor issue.
368
+ `batch-setup` step, named `batch/<id>-<date>`, plus a fresh `ai-dossier runstate mint`
369
+ against the anchor issue. Tries a pool claim first (`npx worktree-pool claim`
370
+ already warm by construction, `BatchEntry.pool_claimed`); on the cold `git branch`/
371
+ `push`/`worktree add` path, batch-setup warms the worktree itself before returning
372
+ (#561) — `cap run worktree.prepare` when the repo's manifest declares it, else
373
+ package-manager-detected install/build (`@ai-dossier/worktree-pool`'s command
374
+ resolution — respects the repo's `.worktree-pool.json` `project_subdir`/
375
+ `warm_commands` even in repos that never use the pool for anything else).
352
376
  - **Members run serially, one fresh `slot-cycle` agent at a time**, in the shared
353
377
  worktree. A member's completion signal is `phase=review status=done mode=slot` on its
354
378
  OWN issue (`slot-cycle` posts no phase of its own past `review` — ship is batch-owned);
@@ -373,23 +397,26 @@ failure rails: executing → dissolving (a member self-reports blocked)
373
397
  `report-issue`'s batch variant.
374
398
  - **Scope cuts, recorded rather than discovered later:** no `git bisect` stage for an
375
399
  ambiguous aggregate failure (an unattributable red suite dissolves instead); no
376
- worktree-pool integration for batch-setup (cold `git worktree add` only); no per-phase
377
- stall/escalation ladder for batch sub-agents (a dead-without-verification agent is
378
- treated as blocked, not redispatched stronger).
400
+ per-phase stall/escalation ladder for batch sub-agents (a dead-without-verification
401
+ agent is treated as blocked, not redispatched stronger).
379
402
 
380
403
  Schema 1.7.0: `BatchEntry` gains `worktree` (absolute path of the shared batch worktree,
381
404
  null until batch-setup lands), `ranges` (`MemberRange[]` — each member's commit range,
382
405
  recomputed after every member completes) and `pr` (the batch PR parked on auto-merge,
383
406
  persisted so a restart mid-watch still knows what to poll). 1.6.0 states migrate on load:
384
407
  no batch was ever dispatched under them, so `null`/`[]`/`null` is the exact backfill, not
385
- a guess. Config schema moves to 1.3.0: `dispatch` gains `member_prompt`,
386
- `batch_tail_prompt` and `batch_report_prompt` (the three new agent prompt templates).
387
-
388
- New journal events: `batch-setup-done`, `batch-setup-failed`, `member-advanced`. Member/
389
- tail/report/fix-agent spawn, progress, completion and park events reuse the existing
390
- unit-generic names (`assigned`/`spawned`/`unit-failed`/`external-advance`/`pr-parked`/
391
- `merge-accepted`/`report-dispatched`/`teardown-done`/`teardown-failed`) with
392
- `unit = batch:<id>`.
408
+ a guess. `BatchEntry` also gains `pool_claimed` (#561) — pre-#561 states carry no such
409
+ key at all, and backfill it to `false` on load (no batch was ever pool-claimed before
410
+ batch-setup had pool integration). Config schema moves to 1.3.0: `dispatch` gains
411
+ `member_prompt`, `batch_tail_prompt` and `batch_report_prompt` (the three new agent
412
+ prompt templates).
413
+
414
+ New journal events: `batch-setup-done`, `batch-setup-failed`, `member-advanced`,
415
+ `batch-warmup-done`, `batch-warmup-failed` (#561 — the cold-path warm step only; a pool
416
+ claim emits neither). Member/tail/report/fix-agent spawn, progress, completion and park
417
+ events reuse the existing unit-generic names (`assigned`/`spawned`/`unit-failed`/
418
+ `external-advance`/`pr-parked`/`merge-accepted`/`report-dispatched`/`teardown-done`/
419
+ `teardown-failed`) with `unit = batch:<id>`.
393
420
 
394
421
  ## API surface
395
422
 
@@ -447,6 +474,8 @@ import {
447
474
  TEARDOWN_TIMEOUT_MS, // teardown subprocess timeout (120 s)
448
475
  attributeByOverlap, // #472 pure stage-1 attribution: failing tests → members
449
476
  parseVitestJson, // vitest --reporter=json → failing tests
477
+ isReadableVitestReport, // #562: a parseable { testResults: [...] } document exists —
478
+ // distinct from "zero failures"
450
479
  parseBoundaryCommits, // git log → issue-boundary commits via the (#N) trailer
451
480
  memberRanges, // boundary commits → each member's commit list
452
481
  runAttributionBisect, // stage-2: real git bisect over the failing tests only
@@ -456,6 +485,8 @@ import {
456
485
  evictMembers, // revert + requeue with evidence + suite re-run + dissolve check
457
486
  checkDissolveTrigger, // pure: > ⅓ of members evicted
458
487
  dissolveBatch, // full or halved dissolve; preserves everything green
488
+ blockBatch, // #562: unreadable suite report → blocked; no requeue, no revert
489
+ type BlockOptions, // { reason, milestonePhase? } for blockBatch
459
490
  handlePrConflict, // rebase + re-ship once, then dissolve into halves
460
491
  createExecMilestonePoster, // batch milestones via `ai-dossier runstate post`
461
492
  expandEvictionGroups, // members that must revert together (§E.4 eviction groups)
@@ -519,8 +550,8 @@ telemetry" below.
519
550
  ├── state.json # hot operational truth — atomic tmp+fsync+rename writes;
520
551
  ├── config.json # durable intent: max_slots, stall_timeout_ms, reconcile_interval_ms,
521
552
  │ # pr_poll_interval_ms, dispatch (incl. report_prompt,
522
- │ # phase_stall_timeout_ms, fence_takeover_timeout_ms, tiers — #527),
523
- │ # auto_upgrade — #537
553
+ │ # phase_stall_timeout_ms, fence_takeover_timeout_ms, tiers — #527,
554
+ │ # suite_command — #562), auto_upgrade — #537
524
555
  ├── events.jsonl # append-only event journal (the operator's flight recorder)
525
556
  ├── runs/ # per-unit agent output logs (issue-<n>.log)
526
557
  └── .sched-lock/ # cross-process directory mutex (pid; stolen from dead holders)
@@ -65,6 +65,16 @@ export declare function offendersOf(attributed: ReadonlyMap<number, unknown>): n
65
65
  * "no attributable tests", which routes to bisect, never to a guess.
66
66
  */
67
67
  export declare function parseVitestJson(stdout: string | null): FailingTest[];
68
+ /**
69
+ * Whether `stdout` contains a parseable vitest JSON report (a `{ testResults:
70
+ * [...] }` document) — independent of whether any test in it failed (#562).
71
+ * `parseVitestJson` returns `[]` both for "found a report, zero failures" and
72
+ * "found no report at all" (an empty string, a wrapper script's plain-text
73
+ * abort message, a make error): those two are NOT the same thing to a caller
74
+ * deciding whether to trust a red exit code's failing-test list, or a green
75
+ * exit code that happens to carry no parseable body.
76
+ */
77
+ export declare function isReadableVitestReport(stdout: string | null): boolean;
68
78
  /** One commit on the batch branch, with the member it belongs to. */
69
79
  export interface BoundaryCommit {
70
80
  sha: string;
@@ -1 +1 @@
1
- {"version":3,"file":"attribution.d.ts","sourceRoot":"","sources":["../src/attribution.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,4DAA4D;AAC5D,MAAM,WAAW,WAAW;IAC1B,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,mFAAmF;AACnF,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,wDAAwD;IACxD,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,yEAAyE;IACzE,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,gEAAgE;AAChE,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,WAAW,CAAC;IAClB,2DAA2D;IAC3D,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,qDAAqD;AACrD,MAAM,WAAW,kBAAkB;IACjC,kEAAkE;IAClE,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;IACvC,0DAA0D;IAC1D,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,2DAA2D;IAC3D,YAAY,EAAE,WAAW,EAAE,CAAC;CAC7B;AAED,+EAA+E;AAC/E,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAEnE;AAqCD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,SAAS,WAAW,EAAE,EAC/B,UAAU,EAAE,SAAS,eAAe,EAAE,GACrC,kBAAkB,CAwBpB;AAED,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,CAE9E;AAID;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,WAAW,EAAE,CA0BpE;AAiCD,qEAAqE;AACrE,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,mFAAmF;IACnF,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AASD;;;;GAIG;AACH,eAAO,MAAM,MAAM,QAAsB,CAAC;AAE1C;;;;;GAKG;AACH,eAAO,MAAM,WAAW,QAAkC,CAAC;AAE3D;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,cAAc,EAAE,CAalF;AAED,4EAA4E;AAC5E,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,gDAAgD;IAChD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB;;;;OAIG;IACH,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,GAAG,WAAW,EAAE,CAgB/E;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAS9F"}
1
+ {"version":3,"file":"attribution.d.ts","sourceRoot":"","sources":["../src/attribution.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,4DAA4D;AAC5D,MAAM,WAAW,WAAW;IAC1B,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,mFAAmF;AACnF,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,wDAAwD;IACxD,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,yEAAyE;IACzE,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,gEAAgE;AAChE,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,WAAW,CAAC;IAClB,2DAA2D;IAC3D,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,qDAAqD;AACrD,MAAM,WAAW,kBAAkB;IACjC,kEAAkE;IAClE,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;IACvC,0DAA0D;IAC1D,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,2DAA2D;IAC3D,YAAY,EAAE,WAAW,EAAE,CAAC;CAC7B;AAED,+EAA+E;AAC/E,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAEnE;AAqCD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,SAAS,WAAW,EAAE,EAC/B,UAAU,EAAE,SAAS,eAAe,EAAE,GACrC,kBAAkB,CAwBpB;AAED,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,CAE9E;AAID;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,WAAW,EAAE,CA0BpE;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAMrE;AAiCD,qEAAqE;AACrE,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,mFAAmF;IACnF,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AASD;;;;GAIG;AACH,eAAO,MAAM,MAAM,QAAsB,CAAC;AAE1C;;;;;GAKG;AACH,eAAO,MAAM,WAAW,QAAkC,CAAC;AAE3D;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,cAAc,EAAE,CAalF;AAED,4EAA4E;AAC5E,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,gDAAgD;IAChD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB;;;;OAIG;IACH,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,GAAG,WAAW,EAAE,CAgB/E;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAS9F"}
@@ -22,6 +22,7 @@ exports.failingTest = failingTest;
22
22
  exports.attributeByOverlap = attributeByOverlap;
23
23
  exports.offendersOf = offendersOf;
24
24
  exports.parseVitestJson = parseVitestJson;
25
+ exports.isReadableVitestReport = isReadableVitestReport;
25
26
  exports.parseBoundaryCommits = parseBoundaryCommits;
26
27
  exports.memberRanges = memberRanges;
27
28
  exports.memberOfCommit = memberOfCommit;
@@ -138,6 +139,24 @@ function parseVitestJson(stdout) {
138
139
  }
139
140
  return out;
140
141
  }
142
+ /**
143
+ * Whether `stdout` contains a parseable vitest JSON report (a `{ testResults:
144
+ * [...] }` document) — independent of whether any test in it failed (#562).
145
+ * `parseVitestJson` returns `[]` both for "found a report, zero failures" and
146
+ * "found no report at all" (an empty string, a wrapper script's plain-text
147
+ * abort message, a make error): those two are NOT the same thing to a caller
148
+ * deciding whether to trust a red exit code's failing-test list, or a green
149
+ * exit code that happens to carry no parseable body.
150
+ */
151
+ function isReadableVitestReport(stdout) {
152
+ if (stdout === null)
153
+ return false;
154
+ const parsed = extractJsonObject(stdout);
155
+ if (parsed === null)
156
+ return false;
157
+ const results = parsed.testResults;
158
+ return Array.isArray(results);
159
+ }
141
160
  /** The first balanced `{...}` document in `text`, parsed; null when there is none. */
142
161
  function extractJsonObject(text) {
143
162
  const start = text.indexOf('{');
@@ -1 +1 @@
1
- {"version":3,"file":"attribution.js","sourceRoot":"","sources":["../src/attribution.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"attribution.js","sourceRoot":"","sources":["../src/attribution.ts"],"names":[],"mappings":"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"}
@@ -47,12 +47,18 @@
47
47
  * an ambiguous aggregate failure (bisect needs a per-project "run only these
48
48
  * tests" command this module has no generic way to construct) — an
49
49
  * unattributable red aggregate suite dissolves the batch rather than
50
- * bisecting, which `attributing → dissolving` already models. No worktree-pool
51
- * integration for batch-setupcold `git worktree add` only, mirroring
52
- * `teardown.ts`'s cold path. No per-phase stall/escalation ladder for batch
53
- * sub-agents a dead-without-verification agent is treated as blocked and
54
- * evicted/reported rather than redispatched stronger. Both are documented
55
- * follow-ups, not gaps discovered later.
50
+ * bisecting, which `attributing → dissolving` already models. No per-phase
51
+ * stall/escalation ladder for batch sub-agentsa dead-without-verification
52
+ * agent is treated as blocked and evicted/reported rather than redispatched
53
+ * stronger. Both are documented follow-ups, not gaps discovered later.
54
+ *
55
+ * `runBatchSetup` (#561) tries a pool claim first (already warm, mirroring
56
+ * `teardown.ts`'s `poolReturn` — same `npx`-through-`deps.exec` pattern, never
57
+ * a direct in-process `claim()` import, which resolves its git root from
58
+ * `process.cwd()` and would break this module's `deps.repoDir` testability
59
+ * contract); on the cold `git worktree add` path it warms the worktree itself
60
+ * before returning, so a member's first command is never the one that
61
+ * discovers `node_modules` is missing (`env-cold`, `docs/agent-traps.md`).
56
62
  */
57
63
  import { type ResolvedDispatch, type SpawnDeps } from './dispatch';
58
64
  import { type GroundTruth } from './groundtruth';
@@ -81,17 +87,30 @@ export interface BatchDispatchDeps {
81
87
  repoDir: string;
82
88
  /** Exec for batch git/milestone-CLI operations (never throws — the `ExecFn` contract). */
83
89
  exec: ExecFn;
90
+ /**
91
+ * Exec for the cold-path warm-up install/build (#561), on its own budget —
92
+ * a real `npm ci` + build routinely exceeds the git-op timeout `exec` is
93
+ * tuned for. Falls back to `exec` when not supplied (existing callers/tests
94
+ * are unaffected; production wiring should still give this its own longer
95
+ * timeout, e.g. `@ai-dossier/worktree-pool`'s `WARM_COMMAND_TIMEOUT_MS`).
96
+ */
97
+ warmExec?: ExecFn;
84
98
  /** Runs the aggregate suite inside a batch worktree; batches never leave `validating` without one. */
85
99
  runSuite: (worktree: string) => SuiteResult;
86
100
  /**
87
- * Runs one `ai-dossier cap run <capabilityId>` in a batch worktree for the
88
- * per-member incremental gate (#523 AC2 — "typecheck + focused tests via
89
- * `cap run test.focused` when available"). Optional and degrade-not-crash,
90
- * like `batchExec`/`runBatchSuite`: without it, or on `automation-broken`/
91
- * `capability-unavailable`, the gate is skipped — the member's own
92
- * `slot-cycle` run already attempted this fast path (with its own reasoning
93
- * fallback) before ever posting `review done`, so a repo with no manifest
94
- * loses nothing but the engine's independent re-check.
101
+ * Runs one `ai-dossier cap run <capabilityId>` in a batch worktree. Two call
102
+ * sites, different degrade contracts:
103
+ * - the per-member incremental gate (#523 AC2 — "typecheck + focused tests
104
+ * via `cap run test.focused` when available"): degrade-not-crash without
105
+ * this hook, or on `automation-broken`/`capability-unavailable`, the gate
106
+ * is skipped (the member's own `slot-cycle` run already attempted this
107
+ * fast path before ever posting `review done`, so a repo with no manifest
108
+ * loses nothing but the engine's independent re-check).
109
+ * - batch-setup's `worktree.prepare` warm step (#561, `warmColdBatchWorktree`):
110
+ * `undefined`/`capability-unavailable`/`automation-broken` fall through to
111
+ * package-manager detection, but a declared-and-`task-failed` capability
112
+ * hard-fails the whole batch setup — a repo that owns its warm-up should
113
+ * never be silently second-guessed by a fallback underneath it.
95
114
  */
96
115
  runCapability?: (worktree: string, capabilityId: string) => CapOutcome;
97
116
  fsExists?: FsExists;
@@ -1 +1 @@
1
- {"version":3,"file":"batch-dispatch.d.ts","sourceRoot":"","sources":["../src/batch-dispatch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AAWH,OAAO,EAKL,KAAK,gBAAgB,EAErB,KAAK,SAAS,EAEf,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,KAAK,WAAW,EAOjB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,KAAK,OAAO,EAAa,MAAM,WAAW,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EASL,KAAK,WAAW,EACjB,MAAM,YAAY,CAAC;AAWpB,OAAO,EAAE,KAAK,QAAQ,EAA+B,MAAM,YAAY,CAAC;AACxE,OAAO,KAAK,EAKV,WAAW,EAIZ,MAAM,SAAS,CAAC;AAEjB;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,aAAa,GAAG,mBAAmB,GAAG,wBAAwB,CAAC;AAE/F,8DAA8D;AAC9D,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,UAAU,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,WAAW,CAAC;IACzB,SAAS,EAAE,SAAS,CAAC;IACrB,GAAG,EAAE,MAAM,IAAI,CAAC;IAChB,uFAAuF;IACvF,OAAO,EAAE,MAAM,CAAC;IAChB,0FAA0F;IAC1F,IAAI,EAAE,MAAM,CAAC;IACb,sGAAsG;IACtG,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,WAAW,CAAC;IAC5C;;;;;;;;;OASG;IACH,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,UAAU,CAAC;IACvE,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED,8FAA8F;AAC9F,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,8FAA8F;IAC9F,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AA6vCD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,iBAAiB,EACvB,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,gBAAgB,GACzB,eAAe,CAsEjB"}
1
+ {"version":3,"file":"batch-dispatch.d.ts","sourceRoot":"","sources":["../src/batch-dispatch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AAiBH,OAAO,EAKL,KAAK,gBAAgB,EAErB,KAAK,SAAS,EAEf,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,KAAK,WAAW,EAOjB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,KAAK,OAAO,EAAa,MAAM,WAAW,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAUL,KAAK,WAAW,EACjB,MAAM,YAAY,CAAC;AAWpB,OAAO,EAAE,KAAK,QAAQ,EAA2D,MAAM,YAAY,CAAC;AACpG,OAAO,KAAK,EAKV,WAAW,EAIZ,MAAM,SAAS,CAAC;AAEjB;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,aAAa,GAAG,mBAAmB,GAAG,wBAAwB,CAAC;AAE/F,8DAA8D;AAC9D,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,UAAU,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,WAAW,CAAC;IACzB,SAAS,EAAE,SAAS,CAAC;IACrB,GAAG,EAAE,MAAM,IAAI,CAAC;IAChB,uFAAuF;IACvF,OAAO,EAAE,MAAM,CAAC;IAChB,0FAA0F;IAC1F,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sGAAsG;IACtG,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,WAAW,CAAC;IAC5C;;;;;;;;;;;;;;OAcG;IACH,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,UAAU,CAAC;IACvE,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED,8FAA8F;AAC9F,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,8FAA8F;IAC9F,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AA67CD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,iBAAiB,EACvB,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,gBAAgB,GACzB,eAAe,CAsEjB"}
@@ -48,12 +48,18 @@
48
48
  * an ambiguous aggregate failure (bisect needs a per-project "run only these
49
49
  * tests" command this module has no generic way to construct) — an
50
50
  * unattributable red aggregate suite dissolves the batch rather than
51
- * bisecting, which `attributing → dissolving` already models. No worktree-pool
52
- * integration for batch-setupcold `git worktree add` only, mirroring
53
- * `teardown.ts`'s cold path. No per-phase stall/escalation ladder for batch
54
- * sub-agents a dead-without-verification agent is treated as blocked and
55
- * evicted/reported rather than redispatched stronger. Both are documented
56
- * follow-ups, not gaps discovered later.
51
+ * bisecting, which `attributing → dissolving` already models. No per-phase
52
+ * stall/escalation ladder for batch sub-agentsa dead-without-verification
53
+ * agent is treated as blocked and evicted/reported rather than redispatched
54
+ * stronger. Both are documented follow-ups, not gaps discovered later.
55
+ *
56
+ * `runBatchSetup` (#561) tries a pool claim first (already warm, mirroring
57
+ * `teardown.ts`'s `poolReturn` — same `npx`-through-`deps.exec` pattern, never
58
+ * a direct in-process `claim()` import, which resolves its git root from
59
+ * `process.cwd()` and would break this module's `deps.repoDir` testability
60
+ * contract); on the cold `git worktree add` path it warms the worktree itself
61
+ * before returning, so a member's first command is never the one that
62
+ * discovers `node_modules` is missing (`env-cold`, `docs/agent-traps.md`).
57
63
  */
58
64
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
59
65
  if (k2 === undefined) k2 = k;
@@ -90,7 +96,9 @@ var __importStar = (this && this.__importStar) || (function () {
90
96
  })();
91
97
  Object.defineProperty(exports, "__esModule", { value: true });
92
98
  exports.runBatchTick = runBatchTick;
99
+ const fs = __importStar(require("node:fs"));
93
100
  const path = __importStar(require("node:path"));
101
+ const worktree_pool_1 = require("@ai-dossier/worktree-pool");
94
102
  const attribution_1 = require("./attribution");
95
103
  const dispatch_1 = require("./dispatch");
96
104
  const groundtruth_1 = require("./groundtruth");
@@ -177,7 +185,9 @@ function safeSuite(deps, batchId, worktree) {
177
185
  catch (err) {
178
186
  const detail = `suite runner threw: ${err.message}`;
179
187
  journalEvent(deps, 'suite-failed', unit(batchId), { detail });
180
- return { ok: false, failing: [], detail };
188
+ // A throw is exactly "no trustworthy report" (#562) — must not default to
189
+ // `readable: true` and look like a parseable report naming zero failures.
190
+ return { ok: false, failing: [], readable: false, detail };
181
191
  }
182
192
  }
183
193
  function recoveryDeps(deps, batch, now) {
@@ -228,11 +238,93 @@ function releaseSlot(state, batchId, now) {
228
238
  }
229
239
  // --- Batch setup (ready → executing, member 1) ---
230
240
  /**
231
- * `batch/<id>-<YYYYMMDD>` off `base_branch`, a worktree at
232
- * `<repoDir>/worktrees/batch-<id>-<YYYYMMDD>` (cold git only no pool
233
- * integration in this version, see the module doc), and a fresh runstate run
234
- * id minted against the anchor. All-or-nothing: any failed step reports the
235
- * step name and nothing is partially recorded on the batch.
241
+ * A pool claim already returns a warm worktree (deps installed, built), so
242
+ * only the cold `git worktree add` path needs its own warm step (#561).
243
+ *
244
+ * Tries `worktree.prepare` via the optional `runCapability` hook first (a
245
+ * repo that declares it in its manifest knows its own warm-up best) — a
246
+ * declared-and-`task-failed` capability hard-fails the whole batch setup
247
+ * (deliberately NOT degrade-not-crash, unlike every other `runCapability`
248
+ * call site in this file: a repo that owns `worktree.prepare` should never
249
+ * be silently second-guessed by falling back underneath it). `undefined`,
250
+ * `capability-unavailable` (no such id declared) AND `automation-broken`
251
+ * (broken manifest, missing tool, timeout — "do not trust the machinery",
252
+ * not "the task failed") all fall through to the generic path below.
253
+ *
254
+ * The generic path reuses `@ai-dossier/worktree-pool`'s package-manager
255
+ * detection, guarded on `package.json` actually being present — `npm
256
+ * install` (and the pnpm/yarn/bun equivalents) hard-fail with no
257
+ * `package.json` at all, so an unguarded call would turn "nothing to warm"
258
+ * into a false failure for every non-Node repo. The guard is bypassed when
259
+ * `.worktree-pool.json` declares explicit `warm_commands` — the escape hatch
260
+ * for a repo whose warm-up isn't `npm install`-shaped at all.
261
+ *
262
+ * Warm commands run on `deps.warmExec ?? deps.exec` — a separate, longer
263
+ * budget than the git/milestone calls `deps.exec` is tuned for (a cold
264
+ * install+build routinely exceeds a git-op timeout; see `BatchDispatchDeps`).
265
+ *
266
+ * Journals `batch-warmup-done`/`batch-warmup-failed` with the elapsed time
267
+ * appended to `detail` either way (satisfying AC1 even on the no-op branch —
268
+ * `JournalEvent` has no dedicated `duration_ms` field).
269
+ */
270
+ function warmColdBatchWorktree(deps, batch, worktree, now,
271
+ /** Prefixed onto the journaled detail — e.g. `pool-claim-invalid:` when the cold path was taken because a pool claim returned unusable output, rather than the ordinary "no warm spares" case. */
272
+ poolNote = '') {
273
+ const warmStart = deps.now();
274
+ const elapsedMs = () => deps.now().getTime() - warmStart.getTime();
275
+ const fail = (tag) => {
276
+ deps.journal.append((0, journal_1.unitEvent)('batch-warmup-failed', unit(batch.id), {
277
+ detail: `${poolNote}${tag} ${elapsedMs()}ms`,
278
+ }), now);
279
+ return { ok: false, reason: `warmup-failed:${tag}` };
280
+ };
281
+ const done = (tag) => {
282
+ deps.journal.append((0, journal_1.unitEvent)('batch-warmup-done', unit(batch.id), {
283
+ detail: `${poolNote}${tag} ${elapsedMs()}ms`,
284
+ }), now);
285
+ return { ok: true };
286
+ };
287
+ const capOutcome = deps.runCapability?.(worktree, 'worktree.prepare');
288
+ if (capOutcome === 'ok')
289
+ return done('cap:worktree.prepare');
290
+ if (capOutcome === 'task-failed')
291
+ return fail('cap:worktree.prepare:task-failed');
292
+ // `undefined` (no hook injected) / `capability-unavailable` (not declared)
293
+ // / `automation-broken` (declared but the machinery itself is untrustworthy)
294
+ // all fall through to the generic package-manager path below.
295
+ const fsExists = deps.fsExists ?? ((p) => fs.existsSync(p));
296
+ const cfg = (0, worktree_pool_1.readPoolFileConfig)(deps.repoDir);
297
+ const projectDir = (0, worktree_pool_1.resolveProjectDir)(worktree, cfg.project_subdir);
298
+ // `.worktree-pool.json`'s `project_subdir` is repo config, not attacker
299
+ // input, but a `../`-shaped value would otherwise run install/build
300
+ // OUTSIDE the batch worktree entirely — skip rather than warm the wrong
301
+ // directory (or fail the whole batch over a misconfigured pool file).
302
+ if (projectDir !== worktree && !projectDir.startsWith(worktree + path.sep)) {
303
+ return done('skipped:project-dir-outside-worktree');
304
+ }
305
+ const hasExplicitWarmCommands = (cfg.warm_commands?.length ?? 0) > 0;
306
+ if (!hasExplicitWarmCommands && !fsExists(path.join(projectDir, 'package.json'))) {
307
+ return done('skipped:no-package-json');
308
+ }
309
+ const commands = (0, worktree_pool_1.resolveWarmCommands)(projectDir, cfg);
310
+ for (const [i, cmd] of commands.entries()) {
311
+ const [bin, ...args] = cmd;
312
+ if (bin === undefined)
313
+ return fail(`pm:${i + 1}/${commands.length}:empty-command`);
314
+ if ((deps.warmExec ?? deps.exec)(bin, args, projectDir) === null) {
315
+ return fail(`pm:${i + 1}/${commands.length}:${bin}`);
316
+ }
317
+ }
318
+ const pm = commands[0]?.[0] ?? 'none';
319
+ return done(`pm:${pm}:${commands.length}cmds`);
320
+ }
321
+ /**
322
+ * `batch/<id>-<YYYYMMDD>` off `base_branch`, and a fresh runstate run id
323
+ * minted against the anchor. The worktree is either a pool claim (already
324
+ * warm) or a cold `git worktree add` that this function then warms itself
325
+ * (#561, see the module doc) — either way, the worktree is warm before this
326
+ * returns `ok: true`. All-or-nothing: any failed step reports the step name
327
+ * and nothing is partially recorded on the batch.
236
328
  */
237
329
  function runBatchSetup(deps, batch, now) {
238
330
  if (batch.anchor === null)
@@ -255,6 +347,36 @@ function runBatchSetup(deps, batch, now) {
255
347
  if (runId === null || runId.trim() === '')
256
348
  return { ok: false, reason: 'runstate-mint-failed' };
257
349
  const mintedRunId = runId.trim();
350
+ // Pool claim first — a claimed worktree is already warm by construction, so
351
+ // no separate warm step runs for it (AC2). `claim()` itself never pushes
352
+ // the branch (mirrors setup-issue-workflow's per-issue pool-claim step),
353
+ // so that still happens here on success.
354
+ const claimed = deps.exec(teardown_1.POOL_BIN, [...teardown_1.POOL_ARGS_PREFIX, 'claim', '--issue', String(batch.anchor), '--branch', branch], deps.repoDir);
355
+ const claimedWorktree = claimed?.trim();
356
+ // Same hardening `teardown.ts`'s destructive sinks apply to a worktree path
357
+ // (no NUL/newline, no unresolved `..`) — this one is our own CLI's stdout,
358
+ // not attacker input, but garbage here would otherwise be trusted verbatim
359
+ // as `BatchEntry.worktree` and used as a spawn/exec cwd for every member.
360
+ const claimIsUsable = !!claimedWorktree &&
361
+ !claimedWorktree.includes('\0') &&
362
+ !claimedWorktree.includes('\n') &&
363
+ path.isAbsolute(claimedWorktree) &&
364
+ path.resolve(claimedWorktree) === claimedWorktree &&
365
+ (deps.fsExists ?? ((p) => fs.existsSync(p)))(claimedWorktree);
366
+ if (claimedWorktree && claimIsUsable) {
367
+ if (deps.exec('git', ['push', '-u', 'origin', '--', branch], claimedWorktree) === null) {
368
+ // Return the claim rather than leaking a permanently `assigned` pool
369
+ // entry nothing else will ever reference — `BatchEntry` only records
370
+ // `worktree`/`pool_claimed` on the `ok: true` path below.
371
+ deps.exec(teardown_1.POOL_BIN, [...teardown_1.POOL_ARGS_PREFIX, 'return', '--path', claimedWorktree, '--json'], deps.repoDir);
372
+ return { ok: false, reason: 'branch-push-failed', runId: mintedRunId };
373
+ }
374
+ return { ok: true, branch, worktree: claimedWorktree, runId: mintedRunId, poolClaimed: true };
375
+ }
376
+ // A non-null, non-empty, unusable claim response (rather than a plain "no
377
+ // warm spares" null/empty) is unusual enough to note on the batch that
378
+ // otherwise cold-builds silently.
379
+ const poolNote = claimedWorktree ? 'pool-claim-invalid:' : '';
258
380
  if (deps.exec('git', ['fetch', 'origin', '--', batch.base_branch], deps.repoDir) === null) {
259
381
  return { ok: false, reason: 'fetch-failed', runId: mintedRunId };
260
382
  }
@@ -267,7 +389,20 @@ function runBatchSetup(deps, batch, now) {
267
389
  if (deps.exec('git', ['worktree', 'add', '--', worktree, branch], deps.repoDir) === null) {
268
390
  return { ok: false, reason: 'worktree-add-failed', runId: mintedRunId };
269
391
  }
270
- return { ok: true, branch, worktree, runId: mintedRunId };
392
+ const warmed = warmColdBatchWorktree(deps, batch, worktree, now, poolNote);
393
+ if (!warmed.ok) {
394
+ // Restore the "all-or-nothing" contract this function documents: a warm
395
+ // failure otherwise leaves the branch pushed and the worktree on disk,
396
+ // so the NEXT tick's retry dies at `branch-create-failed` forever
397
+ // instead of ever reaching warm-up again. Best-effort — a failed cleanup
398
+ // here just means the next retry's `worktree-add-failed`/`branch-create-failed`
399
+ // surfaces the leftover instead, no worse than before this cleanup existed.
400
+ deps.exec('git', ['worktree', 'remove', '--force', '--', worktree], deps.repoDir);
401
+ deps.exec('git', ['branch', '-D', branch], deps.repoDir);
402
+ deps.exec('git', ['push', 'origin', '--delete', branch], deps.repoDir);
403
+ return { ok: false, reason: warmed.reason, runId: mintedRunId };
404
+ }
405
+ return { ok: true, branch, worktree, runId: mintedRunId, poolClaimed: false };
271
406
  }
272
407
  /** Spawn one batch member's `slot-cycle` agent into the slot batch-setup (or a prior member) just released. */
273
408
  /**
@@ -420,7 +555,12 @@ function claimAndSetup(deps, config, dispatch, batchId, now, result) {
420
555
  poster(batch.anchor, setup.runId, {
421
556
  phase: 'batch-setup',
422
557
  status: 'done',
423
- kv: { branch: setup.branch, worktree: setup.worktree, base_branch: batch.base_branch },
558
+ kv: {
559
+ branch: setup.branch,
560
+ worktree: setup.worktree,
561
+ base_branch: batch.base_branch,
562
+ pool_claimed: setup.poolClaimed ? 'true' : 'false',
563
+ },
424
564
  });
425
565
  deps.journal.append((0, journal_1.unitEvent)('batch-setup-done', unit(batchId), { detail: setup.worktree }), now);
426
566
  deps.store.withLock((s) => {
@@ -438,7 +578,12 @@ function claimAndSetup(deps, config, dispatch, batchId, now, result) {
438
578
  });
439
579
  return { state: releaseSlot(s, batchId, now), result: undefined };
440
580
  }
441
- let next = (0, state_1.patchBatch)(s, batchId, { branch: setup.branch, worktree: setup.worktree, run_id: setup.runId }, now);
581
+ let next = (0, state_1.patchBatch)(s, batchId, {
582
+ branch: setup.branch,
583
+ worktree: setup.worktree,
584
+ run_id: setup.runId,
585
+ pool_claimed: setup.poolClaimed,
586
+ }, now);
442
587
  next = (0, state_1.transitionBatch)(next, batchId, 'executing', { executing_member: 1 }, now);
443
588
  const slot = slotFor(next, batchId);
444
589
  if (slot)
@@ -590,8 +735,11 @@ function boundaryCommits(deps, batch) {
590
735
  /**
591
736
  * `validating`, no live slot: run the aggregate suite (deterministic — no
592
737
  * agent, no slot claimed, matching AC5's "member or batch-LLM-step" wording).
593
- * Green proceeds to the tail; red attributes and either fixes one offender or
594
- * dissolves when nothing could be attributed (RFC F.2/F.8).
738
+ * Green proceeds to the tail; an unreadable report (never got a parseable
739
+ * report at all, distinct from a parseable one naming zero failures) blocks
740
+ * the batch instead of attributing (#562); a genuinely red, parseable report
741
+ * attributes and either fixes one offender or dissolves when nothing could be
742
+ * attributed (RFC F.2/F.8).
595
743
  */
596
744
  function runValidate(deps, config, dispatch, batchId, now, result) {
597
745
  const state = deps.store.load();
@@ -605,7 +753,7 @@ function runValidate(deps, config, dispatch, batchId, now, result) {
605
753
  if (batch.anchor !== null && batch.run_id !== null) {
606
754
  poster(batch.anchor, batch.run_id, { phase: 'batch-validate', status: 'done', kv: {} });
607
755
  }
608
- deps.journal.append((0, journal_1.unitEvent)('verify-complete', unit(batchId), { detail: 'suite green' }), now);
756
+ deps.journal.append((0, journal_1.unitEvent)('verify-complete', unit(batchId), { detail: suite.detail ?? 'suite green' }), now);
609
757
  deps.store.withLock((s) => {
610
758
  const b = (0, state_1.findBatch)(s, batchId);
611
759
  if (!b || b.status !== 'validating')
@@ -615,6 +763,28 @@ function runValidate(deps, config, dispatch, batchId, now, result) {
615
763
  spawnTailAgent(deps, config, dispatch, batchId, now, result);
616
764
  return;
617
765
  }
766
+ // #562: the suite report itself was unreadable (empty, unparseable, a
767
+ // spawn/timeout error) even after the runner's own fallback retry — NOT the
768
+ // same as a parseable report naming zero failures. Attribution would read
769
+ // `suite.failing` as "nothing to attribute" and dissolve a batch that may
770
+ // be fully green; block instead, preserving every member commit and the
771
+ // worktree, for an operator to inspect. `blocked` has no CLI resume verb
772
+ // yet — `sched abandon --batch` is today's only real exit.
773
+ if (suite.readable === false) {
774
+ deps.journal.append((0, journal_1.unitEvent)('suite-failed', unit(batchId), {
775
+ detail: suite.detail ?? 'suite report unreadable',
776
+ }), now);
777
+ const blocked = (0, recovery_1.blockBatch)(state, batchId, { reason: 'suite-unreadable' }, rDeps);
778
+ deps.store.withLock((s) => ({
779
+ state: applyBatchAndIssues(s, blocked.state, batchId, []),
780
+ result: undefined,
781
+ }));
782
+ // `result.blocked` is "issue numbers requeued full-cycle by a dissolve"
783
+ // (see `BatchTickResult`'s field doc) — a blocked batch requeues nothing,
784
+ // so it is reported only under `failed`, not under `blocked`.
785
+ result.failed.push(unit(batchId));
786
+ return;
787
+ }
618
788
  const { state: attributed, outcome } = (0, recovery_1.beginAttribution)(state, batchId, { failing: suite.failing, footprints: memberFootprints(deps, batch) }, rDeps);
619
789
  if (outcome.offenders.length === 0) {
620
790
  const dissolve = (0, recovery_1.dissolveBatch)(attributed, batchId, { strategy: 'full', reason: 'unattributable-suite-failure' }, rDeps);
@@ -1037,14 +1207,18 @@ function teardownBatch(deps, batchId) {
1037
1207
  if (!batch || batch.worktree === null)
1038
1208
  return;
1039
1209
  const root = deps.exec('git', ['rev-parse', '--show-toplevel'], deps.repoDir) ?? deps.repoDir;
1040
- if (!(0, teardown_1.isSafeWorktree)(path.resolve(root), batch.worktree)) {
1210
+ // Pool-claimed worktrees are validated by the pool's own `return` (it only
1211
+ // accepts paths in pool state) and can legitimately live outside either
1212
+ // `isSafeWorktree` root when `.worktree-pool.json` configures a custom
1213
+ // `pool_dir` — mirrors `runTeardown`'s own internal skip for `poolClaimed`.
1214
+ if (batch.pool_claimed !== true && !(0, teardown_1.isSafeWorktree)(path.resolve(root), batch.worktree)) {
1041
1215
  journalEvent(deps, 'teardown-failed', unit(batchId), {
1042
1216
  reason: 'unsafe-worktree-path',
1043
1217
  detail: batch.worktree,
1044
1218
  });
1045
1219
  return;
1046
1220
  }
1047
- const result = (0, teardown_1.runTeardown)(deps.exec, deps.repoDir, { worktree: batch.worktree, poolClaimed: false, branch: batch.branch }, deps.fsExists);
1221
+ const result = (0, teardown_1.runTeardown)(deps.exec, deps.repoDir, { worktree: batch.worktree, poolClaimed: batch.pool_claimed === true, branch: batch.branch }, deps.fsExists);
1048
1222
  journalEvent(deps, result.cleanup === 'done' ? 'teardown-done' : 'teardown-failed', unit(batchId), {
1049
1223
  cleanup: result.cleanup,
1050
1224
  detail: result.detail,