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.
- package/README.md +7 -3
- package/dist/adapters/catalog-remote.d.ts +23 -0
- package/dist/adapters/catalog-remote.js +38 -0
- package/dist/adapters/claude-code.d.ts +1 -1
- package/dist/adapters/claude-code.js +4 -1
- package/dist/adapters/codex-commit-check.d.ts +36 -0
- package/dist/adapters/codex-commit-check.js +205 -0
- package/dist/adapters/codex.d.ts +1 -0
- package/dist/adapters/codex.js +12 -6
- package/dist/adapters/model-discovery-check.d.ts +33 -0
- package/dist/adapters/model-discovery-check.js +123 -0
- package/dist/adapters/model-lints.d.ts +1 -1
- package/dist/adapters/model-lints.js +4 -0
- package/dist/adapters/prompt.js +3 -2
- package/dist/adapters/types.d.ts +10 -0
- package/dist/cli/commands/approve.js +10 -4
- package/dist/cli/commands/doctor.d.ts +2 -0
- package/dist/cli/commands/doctor.js +17 -3
- package/dist/cli/commands/fleet.d.ts +10 -1
- package/dist/cli/commands/fleet.js +363 -106
- package/dist/cli/commands/init.js +10 -3
- package/dist/cli/commands/plan.js +76 -4
- package/dist/cli/commands/verify.d.ts +1 -1
- package/dist/cli/commands/verify.js +65 -15
- package/dist/config/fleet-overlay.d.ts +16 -1
- package/dist/config/fleet-overlay.js +230 -28
- package/dist/drivers/orca.d.ts +34 -2
- package/dist/drivers/orca.js +206 -32
- package/dist/gates/cache.d.ts +4 -0
- package/dist/gates/cache.js +10 -2
- package/dist/gates/llm.d.ts +4 -1
- package/dist/gates/llm.js +6 -1
- package/dist/gates/review.js +8 -3
- package/dist/gates/run-gates.d.ts +5 -3
- package/dist/gates/run-gates.js +112 -56
- package/dist/gates/test-manifest.d.ts +3 -0
- package/dist/gates/test-manifest.js +1 -1
- package/dist/gates/timeout-shaped.d.ts +3 -0
- package/dist/gates/timeout-shaped.js +10 -0
- package/dist/run/daemon.js +326 -130
- package/dist/run/git.js +9 -0
- package/dist/run/journal.d.ts +6 -2
- package/dist/run/journal.js +22 -7
- package/dist/run/launchservices-check.d.ts +56 -0
- package/dist/run/launchservices-check.js +117 -0
- package/dist/run/operator-state.d.ts +22 -2
- package/dist/run/operator-state.js +20 -3
- package/dist/run/title-environment.d.ts +45 -0
- package/dist/run/title-environment.js +127 -0
- package/dist/run/title-preload.cjs +38 -0
- package/dist/run/title-preload.d.cts +1 -0
- package/dist/tui/cockpit/board.js +12 -6
- package/dist/tui/cockpit/keys.d.ts +8 -0
- package/dist/tui/cockpit/keys.js +4 -1
- package/dist/tui/cockpit/layout.d.ts +2 -2
- package/dist/tui/cockpit/layout.js +2 -2
- package/dist/tui/cockpit/live-runtime.js +46 -20
- package/dist/tui/cockpit/live-store.d.ts +24 -1
- package/dist/tui/cockpit/live-store.js +99 -8
- package/dist/tui/cockpit/log-view.d.ts +28 -0
- package/dist/tui/cockpit/log-view.js +60 -0
- package/dist/tui/cockpit/run-view.js +4 -0
- package/dist/tui/cockpit/shell.d.ts +2 -1
- package/dist/tui/cockpit/shell.js +6 -4
- package/dist/tui/ink/fleet-app.d.ts +37 -10
- package/dist/tui/ink/fleet-app.js +65 -21
- package/package.json +1 -1
- package/skills/tickmarkr-auto/SKILL.md +32 -2
- package/skills/tickmarkr-loop/SKILL.md +46 -2
- package/skills/tickmarkr-overseer/SKILL.md +83 -8
- package/skills/tickmarkr-overseer/scripts/classify-vitest-log.sh +58 -16
- package/skills/tickmarkr-overseer/scripts/grade-ci.sh +19 -6
- package/skills/tickmarkr-overseer/scripts/watch-parks.sh +121 -64
package/dist/gates/run-gates.js
CHANGED
|
@@ -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
|
-
|
|
270
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
1276
|
-
//
|
|
1277
|
-
|
|
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
|
-
|
|
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 &&
|
|
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,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));
|