tickmarkr 2.6.4 → 2.6.6

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 (73) hide show
  1. package/README.md +7 -3
  2. package/dist/adapters/catalog-remote.d.ts +23 -0
  3. package/dist/adapters/catalog-remote.js +38 -0
  4. package/dist/adapters/claude-code.d.ts +1 -1
  5. package/dist/adapters/claude-code.js +4 -1
  6. package/dist/adapters/codex-commit-check.d.ts +36 -0
  7. package/dist/adapters/codex-commit-check.js +205 -0
  8. package/dist/adapters/codex.d.ts +1 -0
  9. package/dist/adapters/codex.js +12 -6
  10. package/dist/adapters/model-discovery-check.d.ts +33 -0
  11. package/dist/adapters/model-discovery-check.js +123 -0
  12. package/dist/adapters/model-lints.d.ts +1 -1
  13. package/dist/adapters/model-lints.js +4 -0
  14. package/dist/adapters/prompt.js +3 -2
  15. package/dist/adapters/types.d.ts +10 -0
  16. package/dist/cli/commands/approve.js +10 -4
  17. package/dist/cli/commands/doctor.d.ts +2 -0
  18. package/dist/cli/commands/doctor.js +17 -3
  19. package/dist/cli/commands/fleet.d.ts +10 -1
  20. package/dist/cli/commands/fleet.js +363 -106
  21. package/dist/cli/commands/init.js +10 -3
  22. package/dist/cli/commands/plan.js +76 -4
  23. package/dist/cli/commands/verify.d.ts +1 -1
  24. package/dist/cli/commands/verify.js +65 -15
  25. package/dist/config/fleet-overlay.d.ts +16 -1
  26. package/dist/config/fleet-overlay.js +230 -28
  27. package/dist/drivers/orca.d.ts +34 -2
  28. package/dist/drivers/orca.js +206 -32
  29. package/dist/gates/cache.d.ts +4 -0
  30. package/dist/gates/cache.js +10 -2
  31. package/dist/gates/llm.d.ts +4 -1
  32. package/dist/gates/llm.js +6 -1
  33. package/dist/gates/review.js +8 -3
  34. package/dist/gates/run-gates.d.ts +5 -3
  35. package/dist/gates/run-gates.js +112 -56
  36. package/dist/gates/test-manifest.d.ts +3 -0
  37. package/dist/gates/test-manifest.js +1 -1
  38. package/dist/gates/timeout-shaped.d.ts +3 -0
  39. package/dist/gates/timeout-shaped.js +10 -0
  40. package/dist/run/daemon.js +326 -130
  41. package/dist/run/git.js +9 -0
  42. package/dist/run/journal.d.ts +6 -2
  43. package/dist/run/journal.js +22 -7
  44. package/dist/run/launchservices-check.d.ts +56 -0
  45. package/dist/run/launchservices-check.js +117 -0
  46. package/dist/run/operator-state.d.ts +22 -2
  47. package/dist/run/operator-state.js +20 -3
  48. package/dist/run/title-environment.d.ts +45 -0
  49. package/dist/run/title-environment.js +127 -0
  50. package/dist/run/title-preload.cjs +38 -0
  51. package/dist/run/title-preload.d.cts +1 -0
  52. package/dist/tui/cockpit/board.js +12 -6
  53. package/dist/tui/cockpit/keys.d.ts +8 -0
  54. package/dist/tui/cockpit/keys.js +4 -1
  55. package/dist/tui/cockpit/layout.d.ts +2 -2
  56. package/dist/tui/cockpit/layout.js +2 -2
  57. package/dist/tui/cockpit/live-runtime.js +46 -20
  58. package/dist/tui/cockpit/live-store.d.ts +24 -1
  59. package/dist/tui/cockpit/live-store.js +99 -8
  60. package/dist/tui/cockpit/log-view.d.ts +28 -0
  61. package/dist/tui/cockpit/log-view.js +60 -0
  62. package/dist/tui/cockpit/run-view.js +4 -0
  63. package/dist/tui/cockpit/shell.d.ts +2 -1
  64. package/dist/tui/cockpit/shell.js +6 -4
  65. package/dist/tui/ink/fleet-app.d.ts +37 -10
  66. package/dist/tui/ink/fleet-app.js +65 -21
  67. package/package.json +1 -1
  68. package/skills/tickmarkr-auto/SKILL.md +32 -2
  69. package/skills/tickmarkr-loop/SKILL.md +46 -2
  70. package/skills/tickmarkr-overseer/SKILL.md +83 -8
  71. package/skills/tickmarkr-overseer/scripts/classify-vitest-log.sh +58 -16
  72. package/skills/tickmarkr-overseer/scripts/grade-ci.sh +19 -6
  73. package/skills/tickmarkr-overseer/scripts/watch-parks.sh +121 -64
@@ -15,6 +15,7 @@ import { marginalCostRank } from "../route/router.js";
15
15
  import { carriedAuthorVendors, gateReviewerFloor, pickReviewer, reviewGate } from "./review.js";
16
16
  import { scopeGate } from "./scope.js";
17
17
  import { discoverTestManifest, evaluateManifestedTest, isVitestTestCommand, VITEST_CACHE_ENV, worktreeVitestCache } from "./test-manifest.js";
18
+ import { RUNNER_INFRA_DIAGNOSTIC_RE, timeoutShaped } from "./timeout-shaped.js";
18
19
  import { executionSignal } from "../run/execution-budget.js";
19
20
  import { failureDisposition } from "../run/recovery.js";
20
21
  import { dependencyLinkRefusal, FORK_CAP_ENV, preserveWorktree, producerFields, ROUTING_ENV_SEAMS, shGit, resolvedCapacity, SUITE_PARENT_ENV, verificationProtocol } from "../run/git.js";
@@ -253,7 +254,13 @@ async function listFullManifest(cmd, worktree) {
253
254
  rmSync(dir, { recursive: true, force: true });
254
255
  }
255
256
  }
256
- /** The manifest-report path for a detected vitest test command — never the stdout-count/file-count path. */
257
+ /** The manifest-report path for a detected vitest test command — never the stdout-count/file-count path.
258
+ * ONE outer latch (`retried`) buys at most one more measurement of the exact same selection: the bounded
259
+ * infrastructure retry, or — v2.6.6 C (D-873), default-on — a behavioral timeout red carrying runner
260
+ * infra diagnostics, read by the daemon's own predicate (timeout-shaped.ts). Either waits for calm and
261
+ * spends the bounded allowance when one is configured; neither follows a receipt-backed recovery
262
+ * (retryable:false) or a diagnostic re-observation, and the second sample never stacks the stranded
263
+ * single-fork recovery. The second sample is authoritative; the first keeps its receipts beside it. */
257
264
  async function runVitestManifestGate(worktree, cmd, baseline, selected, artifactDir, retry = {}, retried = false) {
258
265
  const entry = baseline.commands.test;
259
266
  const outcome = await evaluateManifestedTest(cmd, worktree, {
@@ -263,21 +270,28 @@ async function runVitestManifestGate(worktree, cmd, baseline, selected, artifact
263
270
  artifactDir,
264
271
  evidence: retry.evidence,
265
272
  retryBaseCommand: retry.retryBaseCommand, // OBS-1166: the un-narrowed command for a selected screen's stranded retry
273
+ allowStrandedRecovery: !retried,
266
274
  });
267
275
  const reportPath = outcome.reportPath;
268
276
  const evidence = { evidenceReceipt: outcome.evidenceReceipt, evidenceReceipts: outcome.evidenceReceipts };
269
- if (!retried && retry.authorizeRetry && failureDisposition(outcome) === "infrastructure"
270
- && outcome.meta?.retryable !== false) {
277
+ const disposition = failureDisposition(outcome);
278
+ const shaped = disposition === "behavioral" && timeoutShaped(outcome.details) && RUNNER_INFRA_DIAGNOSTIC_RE.test(outcome.details);
279
+ if (!retried && outcome.meta?.retryable !== false && retry.retryBaseCommand !== REOBSERVATION_RETRY_BASE
280
+ && (shaped || (retry.authorizeRetry && disposition === "infrastructure"))) {
271
281
  const waitedMs = await waitForCalmWindow(executionSignal());
272
282
  if (!calmWindowReady())
273
283
  return { ...evidence, gate: "test", pass: false, details: outcome.details,
274
284
  meta: { ...outcome.meta, reportPath, recoveryBlocked: "calm window unavailable within the existing wait ceiling" } };
275
- if (!retry.authorizeRetry("infra")) {
285
+ if (retry.authorizeRetry && !retry.authorizeRetry("infra")) {
276
286
  return { ...evidence, gate: "test", pass: false, details: outcome.details,
277
287
  meta: { ...outcome.meta, reportPath, recoveryBlocked: "infrastructure retry allowance exhausted or subject unavailable" } };
278
288
  }
279
289
  const result = await runVitestManifestGate(worktree, cmd, baseline, selected, artifactDir, retry, true);
280
- return { ...result, evidenceReceipts: [...(outcome.evidenceReceipts ?? []), ...(result.evidenceReceipts ?? [])], meta: { ...result.meta, runnerInfraRerun: { count: 1, waitedMs, firstReportPath: reportPath } } };
290
+ const rerun = { count: 1, waitedMs, firstReportPath: reportPath };
291
+ return { ...result, evidenceReceipts: [...(outcome.evidenceReceipts ?? []), ...(result.evidenceReceipts ?? [])],
292
+ meta: { ...result.meta, ...(shaped
293
+ ? { remeasured: { ...rerun, first: { pass: outcome.pass, details: outcome.details, meta: outcome.meta } } }
294
+ : { runnerInfraRerun: rerun }) } };
281
295
  }
282
296
  return {
283
297
  ...evidence,
@@ -290,7 +304,8 @@ async function runVitestManifestGate(worktree, cmd, baseline, selected, artifact
290
304
  /** A retry base no runner invocation parses. evaluateManifestedTest builds its stranded single-fork
291
305
  * retry from the base it is handed, and one it cannot parse throws before any spawn — so this base
292
306
  * disables that inner recovery: a worker-RPC-stranded re-observation comes back infra (the caller
293
- * parks it as ambiguous) instead of launching a second execution. */
307
+ * parks it as ambiguous) instead of launching a second execution. runVitestManifestGate reads it as
308
+ * the mark of a diagnostic subset, which never buys the outer timeout-shaped remeasurement either. */
294
309
  export const REOBSERVATION_RETRY_BASE = "tickmarkr-reobservation-refuses-stranded-retry";
295
310
  /** OBS-1106 residual: ONE isolated re-observation of a timeout-shaped red's attributed failing files on
296
311
  * the same checkout, narrowed exactly as a screen is. Never cached and never a verdict: the caller
@@ -467,16 +482,28 @@ export async function runGates(task, ctx) {
467
482
  };
468
483
  const shapeGates = ctx.cfg.gates.byShape?.[task.shape];
469
484
  const enabled = (g) => task.gates.includes(g) && (g !== "acceptance" && g !== "review" || shapeGates?.[g] !== false);
470
- const failed = () => results.some((r) => !r.pass);
471
485
  // T4 (OBS-265): a GREEN selected-test run is a screen, not the round's verdict — the merge-candidate
472
486
  // round re-runs the full suite on the same commit and THAT is what the round reports. With no
473
487
  // semantic gate to act on it, the screen is held so its full suite speaks for it in one row.
474
488
  // (A RED screen IS the verdict: the round ends there, so it is recorded immediately.)
475
489
  let heldTest;
490
+ const semantic = enabled("acceptance") || enabled("review");
491
+ // v2.6.5 T6 (D): the closed repair-mode table. A repair the REVIEW sent back (carried review:material)
492
+ // whose test history is clean — selection allowed, no required repair test — asks the question it
493
+ // was sent back for first: cheap gates → acceptance ‖ review → screen → merge-candidate full suite,
494
+ // so a semantic red costs zero test starts. selectTests false or a nonempty requiredRepairTests is a
495
+ // test-red repair and keeps battery-first order; so does every first attempt (nothing carried).
496
+ const semanticFirst = semantic && ctx.selectTests === true && !ctx.requiredRepairTests?.length
497
+ && (ctx.carriedFindings ?? []).some((f) => f.class === "review:material");
498
+ // Semantic-first, a review that returned NO verdict is not a red that ends the round before its
499
+ // test proof: the daemon re-asks only the review (runReviewRecovery) and merges on the rows beside
500
+ // it, so the round still buys its screen and full suite. The row stays red; the round is unsatisfied.
501
+ const failed = () => results.some((r) => !r.pass && !(semanticFirst && r.gate === "review" && r.meta?.noVerdict === true));
476
502
  // OBS-1176: when acceptance/review WILL act on a green screen, the screen is published before they
477
503
  // start, as its own selected row. The full suite afterwards is a second invocation on its own row —
478
504
  // it carries only its own receipts and interval, so it neither erases nor re-counts the screen.
479
- const publishScreen = enabled("acceptance") || enabled("review");
505
+ // Semantic-first, nothing acts on the screen but its full suite: it is held like a gate-less round's.
506
+ const publishScreen = semantic && !semanticFirst;
480
507
  // v2.0 T2 (OBS-554): this round's per-gate measurement. Every interval a gate actually spends
481
508
  // executing is added HERE, at the call site that runs it, so a gate that runs twice (the test
482
509
  // gate's screen and its full suite) sums to its own cost and never to the span between them.
@@ -1126,8 +1153,12 @@ export async function runGates(task, ctx) {
1126
1153
  if (typeof rv.meta.reviewer === "string") {
1127
1154
  const reviewer = rv.meta.reviewer;
1128
1155
  const cause = String(rv.meta.cause);
1156
+ // E1: a checkout-proof timeout is the host being slow to print the seat's proof, not the seat
1157
+ // failing. It is still a no-verdict re-routed away from for THIS round, but it never strikes the
1158
+ // run-scoped tally; every other launch failure (foreign or malformed proof included) does.
1159
+ const proofTimeout = cause === "seat-launch-failed" && rv.meta.launchCause === "checkout-proof-timeout";
1129
1160
  // OBS-1025 add.2: the run-scoped tally; two no-verdicts retire the seat for the rest of the run.
1130
- const causes = rv.meta.noVerdict === true && ctx.reviewNoVerdicts
1161
+ const causes = rv.meta.noVerdict === true && ctx.reviewNoVerdicts && !proofTimeout
1131
1162
  ? [...(ctx.reviewNoVerdicts.get(reviewer) ?? []), cause] : undefined;
1132
1163
  if (causes)
1133
1164
  ctx.reviewNoVerdicts.set(reviewer, causes);
@@ -1272,51 +1303,9 @@ export async function runGates(task, ctx) {
1272
1303
  if (failed())
1273
1304
  return done();
1274
1305
  }
1275
- // A non-final round may run only the tests covering its own diff; the merge-candidate round below
1276
- // pays the full suite anyway, so a selection that misses costs a round and can never merge.
1277
- let selected = ctx.selectTests && enabled("test") && ctx.commands.test
1278
- ? await coveringTests(ctx.worktree, ctx.baseRef)
1279
- : undefined;
1280
- let selectionReason = ctx.selectionReason ?? (selected ? "affected-tests" : "full-suite-fallback");
1281
- if (selected && ctx.requiredRepairTests?.length) {
1282
- const required = ctx.requiredRepairTests;
1283
- const safe = required.every((path) => posix.normalize(path) === path && !path.startsWith("../")
1284
- && !path.startsWith("/") && TEST_FILE_RE.test(path) && existsSync(join(ctx.worktree, path)));
1285
- const listed = safe ? await shGit(`git ls-files -z -- ${required.map(shq).join(" ")}`, ctx.worktree) : undefined;
1286
- const tracked = new Set(listed?.stdout.split("\0").filter(Boolean));
1287
- if (!listed || listed.code !== 0 || required.some((path) => !tracked.has(path))) {
1288
- selected = undefined;
1289
- selectionReason = "required-repair-test-unavailable";
1290
- }
1291
- else
1292
- selected = [...new Set([...selected, ...required])].sort();
1293
- }
1294
- // OBS-635: a screen buys nothing when the full suite that must follow it already has a qualified
1295
- // green on this exact identity (read BEFORE any screen), or when the harness-measured screen costs
1296
- // at least 75 % of it — then the full suite runs in the battery instead. Unknown timing keeps the
1297
- // screen. A selected-only green never answers here: its identity names its selection.
1298
- let promotion;
1299
- if (selected) {
1300
- const hit = verdictStore.get(await fullTestIdentity());
1301
- const costRatio = screenCostRatio(ctx.baseline, selected);
1302
- if (hit?.pass === true && !isInfraResult(hit) && await certifiesFullManifest(hit))
1303
- promotion = { reason: "full-green-cache" };
1304
- else if (costRatio !== undefined && costRatio >= SCREEN_PROMOTION_RATIO)
1305
- promotion = { reason: "screen-cost-promoted", costRatio };
1306
- if (promotion)
1307
- selected = undefined;
1308
- }
1309
- if (ctx.selectionReason || promotion)
1310
- selectionDecision = {
1311
- scope: selected ? "selected" : "full", reason: promotion?.reason ?? (selected ? selectionReason
1312
- : selectionReason === "known-failing-files" ? "unsupported-selection-full-suite" : selectionReason),
1313
- requiredFiles: [...(ctx.requiredRepairTests ?? [])],
1314
- ...(promotion?.costRatio !== undefined ? { costRatio: promotion.costRatio } : {}),
1315
- };
1316
- await runBattery(selected ? { ...ctx.commands, test: testCommandForFiles(ctx.commands.test, selected) } : ctx.commands, selected, enabled("test") ? ["test"] : []);
1317
- if (failed())
1318
- return done();
1319
- if (enabled("acceptance") || enabled("review")) {
1306
+ // The judge and the reviewer, together. Returns true when the round ends here: a red, or a
1307
+ // cancelled sibling — a cancelled seat's round never goes on to borrow a test row.
1308
+ const runSemantics = async () => {
1320
1309
  // Judge and review are launched TOGETHER (96m of serialization over 5 runs). Enforcement is
1321
1310
  // unchanged — it is still the AND of both, both still fail closed, and neither reads the other's
1322
1311
  // verdict: each gets the same commit and the same brief it always got, and neither promise is
@@ -1365,16 +1354,83 @@ export async function runGates(task, ctx) {
1365
1354
  if (failure)
1366
1355
  throw failure.reason;
1367
1356
  executionSignal()?.throwIfAborted();
1368
- if (failed())
1357
+ return cancelled || failed();
1358
+ };
1359
+ if (semanticFirst) {
1360
+ if (await runSemantics()) {
1361
+ // A cancelled round starts no test, yet its no-verdict review can still be re-asked alone. Its test
1362
+ // proof is recorded OWED — unsatisfied, never a verdict — so that re-ask cannot merge an unrun subject.
1363
+ if (!failed() && enabled("test") && ctx.commands.test !== undefined) {
1364
+ await record({ gate: "test", pass: false, details: "test not run — the semantic round was cancelled before its "
1365
+ + "test screen; the merge-candidate full suite is still owed on this subject",
1366
+ meta: { skipped: true, infra: true, classification: "infra", retryable: false, testOwed: true } });
1367
+ }
1368
+ return done();
1369
+ }
1370
+ // Subject freshness: the semantic gates ran oracles and vendor CLIs in this worktree. Their dirt is
1371
+ // not the committed subject, so no screen measures it and no cached green row answers for it.
1372
+ const dirt = enabled("test") && ctx.commands.test !== undefined ? await dirtyWorktree() : undefined;
1373
+ if (dirt) {
1374
+ await emitStart("test");
1375
+ await record(await dirtyRefusal("test", dirt));
1369
1376
  return done();
1377
+ }
1378
+ }
1379
+ // A non-final round may run only the tests covering its own diff; the merge-candidate round below
1380
+ // pays the full suite anyway, so a selection that misses costs a round and can never merge.
1381
+ let selected = ctx.selectTests && enabled("test") && ctx.commands.test
1382
+ ? await coveringTests(ctx.worktree, ctx.baseRef)
1383
+ : undefined;
1384
+ let selectionReason = ctx.selectionReason ?? (selected ? "affected-tests" : "full-suite-fallback");
1385
+ if (selected && ctx.requiredRepairTests?.length) {
1386
+ const required = ctx.requiredRepairTests;
1387
+ const safe = required.every((path) => posix.normalize(path) === path && !path.startsWith("../")
1388
+ && !path.startsWith("/") && TEST_FILE_RE.test(path) && existsSync(join(ctx.worktree, path)));
1389
+ const listed = safe ? await shGit(`git ls-files -z -- ${required.map(shq).join(" ")}`, ctx.worktree) : undefined;
1390
+ const tracked = new Set(listed?.stdout.split("\0").filter(Boolean));
1391
+ if (!listed || listed.code !== 0 || required.some((path) => !tracked.has(path))) {
1392
+ selected = undefined;
1393
+ selectionReason = "required-repair-test-unavailable";
1394
+ }
1395
+ else
1396
+ selected = [...new Set([...selected, ...required])].sort();
1397
+ }
1398
+ // OBS-635: a screen buys nothing when the full suite that must follow it already has a qualified
1399
+ // green on this exact identity (read BEFORE any screen), or when the harness-measured screen costs
1400
+ // at least 75 % of it — then the full suite runs in the battery instead. Unknown timing keeps the
1401
+ // screen. A selected-only green never answers here: its identity names its selection.
1402
+ let promotion;
1403
+ if (selected) {
1404
+ const hit = verdictStore.get(await fullTestIdentity());
1405
+ const costRatio = screenCostRatio(ctx.baseline, selected);
1406
+ if (hit?.pass === true && !isInfraResult(hit) && await certifiesFullManifest(hit))
1407
+ promotion = { reason: "full-green-cache" };
1408
+ else if (costRatio !== undefined && costRatio >= SCREEN_PROMOTION_RATIO)
1409
+ promotion = { reason: "screen-cost-promoted", costRatio };
1410
+ if (promotion)
1411
+ selected = undefined;
1370
1412
  }
1413
+ if (ctx.selectionReason || promotion)
1414
+ selectionDecision = {
1415
+ scope: selected ? "selected" : "full", reason: promotion?.reason ?? (selected ? selectionReason
1416
+ : selectionReason === "known-failing-files" ? "unsupported-selection-full-suite" : selectionReason),
1417
+ requiredFiles: [...(ctx.requiredRepairTests ?? [])],
1418
+ ...(promotion?.costRatio !== undefined ? { costRatio: promotion.costRatio } : {}),
1419
+ };
1420
+ await runBattery(selected ? { ...ctx.commands, test: testCommandForFiles(ctx.commands.test, selected) } : ctx.commands, selected, enabled("test") ? ["test"] : []);
1421
+ if (failed())
1422
+ return done();
1423
+ if (semantic && !semanticFirst && await runSemantics())
1424
+ return done();
1371
1425
  // OBS-635: the semantic gates ran oracles and vendor CLIs in this worktree after an in-battery full
1372
1426
  // suite spoke, and its green stands only for the identity it measured. Oracle dirt withdraws it; a
1373
1427
  // changed or unmeasurable identity — tree, command, baseline, environment, dependency resolution,
1374
1428
  // capacity, protocol, lifecycle, full manifest — buys a fresh merge-candidate suite below.
1429
+ // Semantic-first too: an in-battery full suite that ran AFTER the semantic gates is clear of their
1430
+ // dirt, never of its own test command's effects (an ignored lockfile, a newly collected test).
1375
1431
  let rerunFull = false;
1376
1432
  listing = undefined;
1377
- if (fullInBattery && ctx.commands.test !== undefined && (enabled("acceptance") || enabled("review"))) {
1433
+ if (fullInBattery && ctx.commands.test !== undefined && semantic) {
1378
1434
  const dirt = await dirtyWorktree();
1379
1435
  if (dirt) {
1380
1436
  const refusal = withTelemetry(await dirtyRoundRefusal("test", dirt));
@@ -208,4 +208,7 @@ export declare function evaluateManifestedTest(cmd: string, cwd: string, opts: {
208
208
  /** OBS-1166: the configured command BEFORE a selected screen narrowed it, so a stranded retry's
209
209
  * only positional filters are the stranded files. Absent (a full suite), `cmd` is that command. */
210
210
  retryBaseCommand?: string;
211
+ /** v2.6.6 C: false on the manifest gate's one outer re-measurement — the second sample is one
212
+ * execution and never stacks the stranded single-fork recovery on top of the retry it already is. */
213
+ allowStrandedRecovery?: boolean;
211
214
  }): Promise<ManifestGateOutcome>;
@@ -594,7 +594,7 @@ export async function evaluateManifestedTest(cmd, cwd, opts) {
594
594
  hangActiveMs: invoked.hangActiveMs, hangWallMs: invoked.hangWallMs });
595
595
  evidenceReceipts.push(...invoked.evidenceReceipts);
596
596
  const interruptions = [...invoked.interruptions];
597
- const stranded = strandedSingleForkFiles(files, nonce, invoked);
597
+ const stranded = opts.allowStrandedRecovery === false ? undefined : strandedSingleForkFiles(files, nonce, invoked);
598
598
  if (stranded) {
599
599
  const first = invoked.report;
600
600
  const retryNonce = randomBytes(16).toString("hex");
@@ -0,0 +1,3 @@
1
+ export declare const TIMEOUT_SHAPED_RE: RegExp;
2
+ export declare const RUNNER_INFRA_DIAGNOSTIC_RE: RegExp;
3
+ export declare const timeoutShaped: (details: string) => boolean;
@@ -0,0 +1,10 @@
1
+ import { readableExcerpt } from "../run/journal.js";
2
+ // OBS-1106 residual: a red whose assertion is a timeout or a wall-clock budget, and the runner-level
3
+ // diagnostics (never-started files, a worker RPC timeout) that make such a red infrastructure-SHAPED.
4
+ // Shape alone never parks anything: it only buys one isolated re-observation before a charge. A
5
+ // fresh-fingerprint section masks digits to `#`, so both spellings of a number are read.
6
+ // v2.6.6 C: the daemon's declarations (src/run/daemon.ts at 75dc3187:492-494), exported verbatim so the
7
+ // manifest gate's one remeasurement and the daemon's isolated adjudication read ONE predicate.
8
+ export const TIMEOUT_SHAPED_RE = /\b(?:Test|Hook) timed out in (?:\d+|#)\s*ms\b|\bexpected (?:\d+|#)(?:\.(?:\d+|#))? to be (?:less than|below)(?: or equal to)? (?:\d+|#)/i;
9
+ export const RUNNER_INFRA_DIAGNOSTIC_RE = /\bnever-started (?:[1-9]\d*|#)(?![\d#])|\[vitest-worker\]: Timeout calling\b/;
10
+ export const timeoutShaped = (details) => TIMEOUT_SHAPED_RE.test(readableExcerpt(details));