tickmarkr 2.6.5 → 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/codex-commit-check.d.ts +15 -4
- package/dist/adapters/codex-commit-check.js +122 -25
- package/dist/adapters/codex.d.ts +1 -0
- package/dist/adapters/codex.js +12 -6
- package/dist/adapters/types.d.ts +4 -1
- package/dist/cli/commands/doctor.js +3 -1
- package/dist/cli/commands/fleet.js +318 -78
- package/dist/cli/commands/init.js +5 -2
- package/dist/cli/commands/plan.js +12 -1
- package/dist/cli/commands/verify.js +37 -9
- package/dist/config/fleet-overlay.d.ts +14 -0
- package/dist/config/fleet-overlay.js +154 -25
- package/dist/drivers/orca.d.ts +17 -3
- package/dist/drivers/orca.js +64 -20
- package/dist/gates/run-gates.d.ts +2 -1
- package/dist/gates/run-gates.js +21 -6
- 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 +184 -119
- package/dist/tui/ink/fleet-app.d.ts +26 -5
- package/dist/tui/ink/fleet-app.js +50 -12
- package/package.json +1 -1
- 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
|
|
@@ -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));
|
package/dist/run/daemon.js
CHANGED
|
@@ -4,7 +4,7 @@ import { COMMAND_LEASE_TOKEN_ENV, commandLeaseEnvironment, CommandLeases, curren
|
|
|
4
4
|
import { execFileSync, spawn } from "node:child_process";
|
|
5
5
|
import { createHash, randomBytes } from "node:crypto";
|
|
6
6
|
import { shq } from "../adapters/types.js";
|
|
7
|
-
import { appendFileSync, closeSync, constants, existsSync, fstatSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readdirSync, readlinkSync, readSync, realpathSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
7
|
+
import { appendFileSync, closeSync, constants, existsSync, fstatSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readdirSync, readlinkSync, readSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
8
8
|
import { createRequire } from "node:module";
|
|
9
9
|
import { tmpdir } from "node:os";
|
|
10
10
|
import { basename, dirname, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
|
|
@@ -23,6 +23,7 @@ import { formatOwnedName } from "../drivers/types.js";
|
|
|
23
23
|
import { captureBaseline, detectGateCommands, detectVacuousOracles } from "../gates/baseline.js";
|
|
24
24
|
import { reobserveTestFiles, runGates } from "../gates/run-gates.js";
|
|
25
25
|
import { isInfraResult } from "../gates/cache.js";
|
|
26
|
+
import { RUNNER_INFRA_DIAGNOSTIC_RE, timeoutShaped } from "../gates/timeout-shaped.js";
|
|
26
27
|
import { filesGlob } from "../graph/files-glob.js";
|
|
27
28
|
import { addEvidence, attributeBlocked, batteryPriority, blockedTasks, getTask, graphDefinitionHash, loadGraph, pendingTasks, readyTasks, saveGraph, setStatus, taskContentDigest, tickmarkrDir } from "../graph/graph.js";
|
|
28
29
|
import { GATE_NAMES } from "../graph/schema.js";
|
|
@@ -32,7 +33,8 @@ import { executionSignal, remainingExecutionMs, withExecutionBudget, withoutExec
|
|
|
32
33
|
import { repairSelectionDecision, repairSelectionEnabled } from "./repair-selection.js";
|
|
33
34
|
import { failureDisposition, reserveInfrastructureRetry } from "./recovery.js";
|
|
34
35
|
import { runEnvironment } from "./environment.js";
|
|
35
|
-
import { changeRepresented, cleanupRunWorktrees, deriveForkCap, FORK_CAP_ENV, gitHead, linkNodeModules, npmDependencyInstallCommand, npmDependencyManifestChanged, PRESERVE_COMMIT_SUBJECT, PRESERVE_PRODUCER_TRAILER, preserveWorktree, producerFields, resolvedCapacity, runWithForkBudget, runWithVerificationBudget, sameCapacity, sameVerification, sh, shGit, SUITE_PARENT_ENV, verificationProtocol, WORKTREE_LAYOUT_CONTRACT, worktreePath } from "./git.js";
|
|
36
|
+
import { changeRepresented, cleanupRunWorktrees, deriveForkCap, FORK_CAP_ENV, gitHead, linkNodeModules, npmDependencyInstallCommand, npmDependencyManifestChanged, PRESERVE_COMMIT_SUBJECT, PRESERVE_PRODUCER_TRAILER, preserveWorktree, producerFields, removeWorktree, resolvedCapacity, runWithForkBudget, runWithVerificationBudget, sameCapacity, sameVerification, sh, shGit, SUITE_PARENT_ENV, verificationProtocol, WORKTREE_LAYOUT_CONTRACT, WORKTREES_DIR, worktreePath } from "./git.js";
|
|
37
|
+
import { MASK } from "./redact.js";
|
|
36
38
|
import { runInteractiveSeed } from "./interactive-seed.js";
|
|
37
39
|
import { classifyRepairDisposition, resolveScopeHints } from "./repair-disposition.js";
|
|
38
40
|
import { applyScopeAmendments, activeRetryBan, interruptedAttempt, RESUME_HARVEST_SOURCE, resumeHarvestAuthor, approvalAction, APPROVAL_REFUSED, bindingToken, effectiveEvents, foldDecisions, physicalLine, staleApprovals, classifyTaskFailure, classifyWorkerResultCause, deferredReviewFindings, engagementComparable, formatPriorFindingEvidence, GATE_FINGERPRINT_CAP, GATE_SATISFIED_RELEASE, identicalGateFailures, isDeferredFinding, journaledFailureBrief, Journal, loadRoutingProfile, newRunId, normalizeGateFailure, outstandingConsultGuidance, readableExcerpt, receiptOrigin, outstandingReviewFindings, pendingApprovalActions, pendingRechecks, pendingRepairFindings, phaseForGate, priorJudgments, readPriorRunEvidence, recordedTaskFailureKind, reraisedReviewChains, RECHECK_RELEASE, renderStructuredReviewFinding, repairReachSinceApproval, repairsSinceApproval, reviewRoundsSinceApproval, runHasEnded, standingRulings, structuredFindings, upheldFeedbackByTask } from "./journal.js";
|
|
@@ -374,9 +376,6 @@ function classifyInfraResult(g) {
|
|
|
374
376
|
// diagnostics (never-started files, a worker RPC timeout) that make such a red infrastructure-SHAPED.
|
|
375
377
|
// Shape alone never parks anything: it only buys one isolated re-observation before a charge. A
|
|
376
378
|
// fresh-fingerprint section masks digits to `#`, so both spellings of a number are read.
|
|
377
|
-
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;
|
|
378
|
-
const RUNNER_INFRA_DIAGNOSTIC_RE = /\bnever-started (?:[1-9]\d*|#)(?![\d#])|\[vitest-worker\]: Timeout calling\b/;
|
|
379
|
-
const timeoutShaped = (details) => TIMEOUT_SHAPED_RE.test(readableExcerpt(details));
|
|
380
379
|
/** C (E1): the one no-verdict that is the host's slowness, not the seat's — the same predicate run-gates
|
|
381
380
|
* applies to its run-scoped tally, read here for the live task strike and the resume reconstruction. */
|
|
382
381
|
const proofTimeoutNote = (data) => data.cause === "seat-launch-failed" && data.launchCause === "checkout-proof-timeout";
|
|
@@ -2690,7 +2689,29 @@ export async function runDaemon(repoRoot, opts = {}) {
|
|
|
2690
2689
|
to: loadedHash,
|
|
2691
2690
|
});
|
|
2692
2691
|
}
|
|
2693
|
-
|
|
2692
|
+
// B (D-812): a daemon interrupted inside its capture leaves run-start without baseline.json. Only
|
|
2693
|
+
// that absence recaptures; existing bytes that do not parse fail closed and are never written over.
|
|
2694
|
+
const baselinePath = join(journal.dir, "baseline.json");
|
|
2695
|
+
let recapture = false;
|
|
2696
|
+
try {
|
|
2697
|
+
baseline = JSON.parse(readFileSync(baselinePath, "utf8"));
|
|
2698
|
+
}
|
|
2699
|
+
catch (error) {
|
|
2700
|
+
if (error.code !== "ENOENT") {
|
|
2701
|
+
throw new Error(`refusing to resume ${runId}: existing baseline.json is unreadable (${error instanceof Error ? error.message : String(error)}) — it is never recaptured over`);
|
|
2702
|
+
}
|
|
2703
|
+
// The recapture measures run-start's commands; current config is no authority for that base.
|
|
2704
|
+
// Only unmasked bytes prove equality: redaction maps a changed credential onto the same mask, so a
|
|
2705
|
+
// journaled command carrying the mask cannot be proven unchanged and is refused like a change.
|
|
2706
|
+
const recorded = (typeof start.data.commands === "object" && start.data.commands !== null ? start.data.commands : {});
|
|
2707
|
+
const effective = commands;
|
|
2708
|
+
const changed = [...new Set([...Object.keys(recorded), ...Object.keys(effective)])]
|
|
2709
|
+
.filter((name) => recorded[name] !== effective[name] || String(recorded[name]).includes(MASK)).sort();
|
|
2710
|
+
if (changed.length > 0) {
|
|
2711
|
+
throw new Error(`refusing to resume ${runId}: baseline recapture refused — effective commands differ from (or are masked in) run-start's journaled commands: ${changed.join(", ")}`);
|
|
2712
|
+
}
|
|
2713
|
+
recapture = true;
|
|
2714
|
+
}
|
|
2694
2715
|
const replayEvents = journal.read();
|
|
2695
2716
|
// journal.replayStatuses predates typed releases and re-pends even inert rows. Keep that
|
|
2696
2717
|
// legacy reader intact; scheduling accepts only the fold's recognised authorities.
|
|
@@ -2739,8 +2760,47 @@ export async function runDaemon(repoRoot, opts = {}) {
|
|
|
2739
2760
|
});
|
|
2740
2761
|
runStarted = true;
|
|
2741
2762
|
await placeBoard();
|
|
2742
|
-
|
|
2743
|
-
|
|
2763
|
+
if (recapture) {
|
|
2764
|
+
// Moved HEAD is never baseline authority: measure the journaled base in a detached checkout this
|
|
2765
|
+
// daemon owns, and publish by rename so any interruption leaves no baseline.json or a whole one.
|
|
2766
|
+
journal.append("baseline-recapture-start", undefined, { baseRef, commands, capacity: resolvedCapacity() });
|
|
2767
|
+
baselinePending = true;
|
|
2768
|
+
baselineCapture = withCommandContext(undefined, async () => {
|
|
2769
|
+
const parent = join(tickmarkrDir(repoRoot), WORKTREES_DIR);
|
|
2770
|
+
mkdirSync(parent, { recursive: true });
|
|
2771
|
+
// A killed daemon never reaches the finally below; sweep checkouts whose owner is dead.
|
|
2772
|
+
for (const entry of readdirSync(parent)) {
|
|
2773
|
+
const owner = /^baseline-recapture-(\d+)-\w{6}$/.exec(entry)?.[1];
|
|
2774
|
+
if (owner && !isPidLive(Number(owner)))
|
|
2775
|
+
await removeWorktree(repoRoot, join(parent, entry));
|
|
2776
|
+
}
|
|
2777
|
+
const pristine = mkdtempSync(join(parent, `baseline-recapture-${process.pid}-`));
|
|
2778
|
+
const staged = `${baselinePath}.${process.pid}.tmp`;
|
|
2779
|
+
try {
|
|
2780
|
+
const added = await shGit(`git worktree add --detach ${shq(pristine)} ${shq(baseRef)}`, repoRoot);
|
|
2781
|
+
if (added.code !== 0)
|
|
2782
|
+
throw new Error(`baseline recapture: journaled base ${baseRef} cannot be checked out: ${added.stderr.trim()}`);
|
|
2783
|
+
if (!linkNodeModules(repoRoot, pristine, { force: true }))
|
|
2784
|
+
throw new Error("baseline recapture: pristine node_modules link could not be provisioned");
|
|
2785
|
+
await initializeHost();
|
|
2786
|
+
const captured = { ...await captureBaseline(pristine, commands),
|
|
2787
|
+
provenance: { baseRef, capturedAt: new Date().toISOString() } };
|
|
2788
|
+
writeFileSync(staged, JSON.stringify(captured, null, 2));
|
|
2789
|
+
renameSync(staged, baselinePath);
|
|
2790
|
+
baseline = captured;
|
|
2791
|
+
journal.append("baseline-recapture-complete", undefined, { provenance: captured.provenance });
|
|
2792
|
+
for (const warning of captured.warnings ?? [])
|
|
2793
|
+
journal.append("baseline-warning", undefined, { ...warning });
|
|
2794
|
+
}
|
|
2795
|
+
finally {
|
|
2796
|
+
rmSync(staged, { force: true });
|
|
2797
|
+
await removeWorktree(repoRoot, pristine);
|
|
2798
|
+
}
|
|
2799
|
+
}).finally(() => { baselinePending = false; });
|
|
2800
|
+
void baselineCapture.catch(() => { baselineFailed = true; });
|
|
2801
|
+
}
|
|
2802
|
+
else if (Object.keys(commands).length > 0 || graph.tasks.some(t => ["pending", "running", "gated"].includes(t.status))) {
|
|
2803
|
+
// A terminal resume with no commands has no execution to admit.
|
|
2744
2804
|
baselineCapture = initializeHost();
|
|
2745
2805
|
void baselineCapture.catch(() => { baselineFailed = true; });
|
|
2746
2806
|
}
|
|
@@ -3709,6 +3769,81 @@ export async function runDaemon(repoRoot, opts = {}) {
|
|
|
3709
3769
|
let testGateFailed = journal.read().some((e) => e.event === "gate-result" && e.taskId === t.id && e.data.gate === "test" && e.data.pass === false
|
|
3710
3770
|
&& !(cfg.executionPolicy?.boundedInfrastructure && e.data.disposition === "infrastructure"));
|
|
3711
3771
|
let retryMode = "fresh";
|
|
3772
|
+
// v2.6.6 A: the ONE post-result settlement both gate loops call. Order is the contract:
|
|
3773
|
+
// satisfied → merge (tip-moved once regates, twice parks; a conflict goes back to the caller), else
|
|
3774
|
+
// noEligible → infra → diff-cap → adjudication → outside-scope → disposition, and whatever survives
|
|
3775
|
+
// is the caller's "red". Every declared resume/live difference arrives as a parameter; nothing here
|
|
3776
|
+
// normalizes one. Preparation, replay, conflict handling, repair funding, feedback and cleanup stay
|
|
3777
|
+
// in the callers.
|
|
3778
|
+
const settleResults = async (results, p) => {
|
|
3779
|
+
const parkAs = (reason, kind) => park(t, reason, kind, p.author, p.attempts, startMs, gateFails, consults, tokens, metered, retryMode);
|
|
3780
|
+
if (results.every(gateSatisfied)) {
|
|
3781
|
+
p.beforeMerge?.();
|
|
3782
|
+
const m = await mergeSerial(p.taskBranch, t, p.gated);
|
|
3783
|
+
if (m.tipMoved) {
|
|
3784
|
+
journal.append("tip-moved", t.id, m.tipMoved);
|
|
3785
|
+
if (tipMoves++ === 0)
|
|
3786
|
+
return { kind: "regate" };
|
|
3787
|
+
await parkAs("task branch tip moved twice after gating", "tip-moved");
|
|
3788
|
+
return { kind: "parked" };
|
|
3789
|
+
}
|
|
3790
|
+
if (!m.ok) {
|
|
3791
|
+
journal.append("merge-conflict", t.id, { conflict: m.conflict });
|
|
3792
|
+
return { kind: "conflict", conflict: m.conflict };
|
|
3793
|
+
}
|
|
3794
|
+
graph = setStatus(graph, t.id, "done");
|
|
3795
|
+
saveGraph(repoRoot, graph);
|
|
3796
|
+
journal.append("task-done", t.id, {
|
|
3797
|
+
attempts: p.attempts, assignment: p.author, taskContentDigest: contentDigest,
|
|
3798
|
+
authors: mergedAuthors(await subjectAuthors(journal.read(), t.id, p.wt, p.taskBase)),
|
|
3799
|
+
});
|
|
3800
|
+
journal.append("merge", t.id, { branch: p.taskBranch, commit: await integrationHead(intWt) });
|
|
3801
|
+
await trackedDriver.project?.(t.id, "completed");
|
|
3802
|
+
// firstAttemptOk/gateFails/consults are recorded FACTS, not policy — a parkKind:"stall" row is
|
|
3803
|
+
// recorded but NOT quality-negative in v1.6; Phase 12 owns reward policy, so flipping it later needs zero data migration.
|
|
3804
|
+
journal.telemetry({
|
|
3805
|
+
taskId: t.id, shape: t.shape, adapter: p.author.adapter, model: p.author.model,
|
|
3806
|
+
channel: p.author.channel, attempts: p.attempts, outcome: "done",
|
|
3807
|
+
durationMs: Date.now() - startMs, firstAttemptOk: p.firstAttemptOk, gateFails, consults, ...p.metering, retryMode,
|
|
3808
|
+
});
|
|
3809
|
+
return { kind: "merged" };
|
|
3810
|
+
}
|
|
3811
|
+
// OBS-540: infra is a fail-closed NON-verdict, not quality degradation. Park with the blocker
|
|
3812
|
+
// already journaled by onGate, before gateFails and before any ladder selection can fund an
|
|
3813
|
+
// identical retry in the same environment. Parsed judge refusals never carry infra and keep
|
|
3814
|
+
// flowing through the chargeable quality path below.
|
|
3815
|
+
const unavailableReview = results.find((g) => gateFailed(g) && g.meta?.noEligibleReviewer === true);
|
|
3816
|
+
if (unavailableReview) {
|
|
3817
|
+
await parkAs(gateFailApprovalReason(t.id, unavailableReview.details, true), "gate-fail");
|
|
3818
|
+
return { kind: "parked" };
|
|
3819
|
+
}
|
|
3820
|
+
// OBS-1106: same predicate as classification — an infra replay is parked, never repaired.
|
|
3821
|
+
const infra = results.find((g) => gateFailed(g) && isInfraResult(g));
|
|
3822
|
+
if (infra) {
|
|
3823
|
+
await parkAs(p.infraReason(infra), "infra");
|
|
3824
|
+
return { kind: "parked" };
|
|
3825
|
+
}
|
|
3826
|
+
// OBS-1007: a cap trip is a typed park of its own — the diff cannot shrink by retrying and the
|
|
3827
|
+
// only honest verb is a recheck under a raised cap. Decided BEFORE the authoring classifier, which
|
|
3828
|
+
// used to lift `gates.diffCap` out of the cap prose as a files[] hint.
|
|
3829
|
+
const capTrip = results.find(isDiffCapPark);
|
|
3830
|
+
if (capTrip) {
|
|
3831
|
+
await parkAs(`${capTrip.gate}: ${capTrip.details}`, "diff-cap");
|
|
3832
|
+
return { kind: "parked" };
|
|
3833
|
+
}
|
|
3834
|
+
if (await adjudicateInfraShapedRed(t, results, p.wt, p.commit, p.reobserve.attempt, p.reobserve.replayed, (reason) => parkAs(reason, "infra")))
|
|
3835
|
+
return { kind: "parked" };
|
|
3836
|
+
if (p.outsideScopeForced && freshOutsideScopeRed(t, p.commit)) {
|
|
3837
|
+
await parkAs(outsideScopePark(t), "gate-fail");
|
|
3838
|
+
return { kind: "parked" };
|
|
3839
|
+
}
|
|
3840
|
+
// OBS-547: who pays for this red is decided by the run's collateral prediction (see
|
|
3841
|
+
// dispositionScopeRed) — an authoring defect parks unchargeable before any accounting below.
|
|
3842
|
+
const d = p.disposition();
|
|
3843
|
+
if (await dispositionScopeRed(t, results, d.author, d.attempt, startMs, gateFails, consults, tokens, metered, retryMode, await treeOrDiffPaths(p.taskBase, p.wt)))
|
|
3844
|
+
return { kind: "parked" };
|
|
3845
|
+
return { kind: "red" };
|
|
3846
|
+
};
|
|
3712
3847
|
let lastContextTokens; // v1.23 reset signal, including stalled/quota attempts
|
|
3713
3848
|
// v1.29: only a gate-failed attempt can seed same-session retry. The next attempt consumes this
|
|
3714
3849
|
// once; a changed channel, unknown context, or missing resumeCommand falls back to fresh.
|
|
@@ -4306,23 +4441,11 @@ export async function runDaemon(repoRoot, opts = {}) {
|
|
|
4306
4441
|
const approvedCommits = await commitsAheadOf(taskBase, wt);
|
|
4307
4442
|
graph = addEvidence(graph, t.id, { commits: approvedCommits, gateResults: results });
|
|
4308
4443
|
saveGraph(repoRoot, graph);
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
}
|
|
4315
|
-
// OBS-1106: same predicate as classification — an infra replay is parked, never repaired.
|
|
4316
|
-
const infra = results.find((g) => gateFailed(g) && isInfraResult(g));
|
|
4317
|
-
if (infra) {
|
|
4318
|
-
await park(t, `${infra.gate}: ${infra.details}`, "infra", gateAuthor, rs?.attempts ?? 0, startMs, gateFails, consults, tokens, metered, retryMode);
|
|
4319
|
-
return;
|
|
4320
|
-
}
|
|
4321
|
-
const capTrip = results.find(isDiffCapPark);
|
|
4322
|
-
if (capTrip) {
|
|
4323
|
-
await park(t, `${capTrip.gate}: ${capTrip.details}`, "diff-cap", gateAuthor, rs?.attempts ?? 0, startMs, gateFails, consults, tokens, metered, retryMode);
|
|
4324
|
-
return;
|
|
4325
|
-
}
|
|
4444
|
+
const n = rs?.attempts ?? 0;
|
|
4445
|
+
const settled = await settleResults(results, {
|
|
4446
|
+
wt, taskBase, taskBranch, gated, commit: gateSubject.commit, outsideScopeForced,
|
|
4447
|
+
author: gateAuthor, attempts: n, infraReason: (g) => `${g.gate}: ${g.details}`,
|
|
4448
|
+
reobserve: { attempt: n, replayed: false }, firstAttemptOk: false, metering: {},
|
|
4326
4449
|
// OBS-547: disposition FIRST, and through the same helper the ordinary attempt path uses. A
|
|
4327
4450
|
// crash between a journaled scope red and its classification must not turn a predicted red
|
|
4328
4451
|
// into a charged one just because a resume is what observed it.
|
|
@@ -4334,15 +4457,12 @@ export async function runDaemon(repoRoot, opts = {}) {
|
|
|
4334
4457
|
// erase an EARLIER chargeable attempt and attribute the park to the assignment the rewind
|
|
4335
4458
|
// restored. Ask the journal which state this is, and take the classified dispatch's own
|
|
4336
4459
|
// assignment back with it.
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
const classified = journal.classifiedDispatch(t.id);
|
|
4344
|
-
if (await dispositionScopeRed(t, results, classified?.assignment ?? gateAuthor, classified ? (rs?.attempts ?? 0) : Math.max(0, (rs?.attempts ?? 0) - 1), startMs, gateFails, consults, tokens, metered, retryMode, await treeOrDiffPaths(taskBase, wt)))
|
|
4345
|
-
return;
|
|
4460
|
+
disposition: () => {
|
|
4461
|
+
const classified = journal.classifiedDispatch(t.id);
|
|
4462
|
+
return { author: classified?.assignment ?? gateAuthor, attempt: classified ? n : Math.max(0, n - 1) };
|
|
4463
|
+
},
|
|
4464
|
+
});
|
|
4465
|
+
if (settled.kind === "red") {
|
|
4346
4466
|
gateFails++;
|
|
4347
4467
|
// Observed green gates are only measurements. If the resumed suffix is red, preserve that
|
|
4348
4468
|
// result in the journal and return to the ordinary attempt/consult ladder, which rebuilds
|
|
@@ -4385,32 +4505,14 @@ export async function runDaemon(repoRoot, opts = {}) {
|
|
|
4385
4505
|
await park(t, gateFailApprovalReason(t.id, "post-approval gate failed", results.some((g) => g.gate === "review" && gateFailed(g))), "gate-fail", gateAuthor, rs?.attempts ?? 0, startMs, gateFails, consults, tokens, metered, retryMode);
|
|
4386
4506
|
return;
|
|
4387
4507
|
}
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
if (tipMoves++ === 0)
|
|
4392
|
-
continue gateLoop;
|
|
4393
|
-
await park(t, "task branch tip moved twice after gating", "tip-moved", gateAuthor, rs?.attempts ?? 0, startMs, gateFails, consults, tokens, metered, retryMode);
|
|
4508
|
+
if (settled.kind === "regate")
|
|
4509
|
+
continue gateLoop;
|
|
4510
|
+
if (settled.kind === "parked")
|
|
4394
4511
|
return;
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
journal.append("merge-conflict", t.id, { conflict: m.conflict });
|
|
4398
|
-
await park(t, `merge conflict after ${resumeReason}: ${m.conflict ?? "unknown conflict"}`, "merge-conflict", gateAuthor, rs?.attempts ?? 0, startMs, gateFails, consults, tokens, metered, retryMode);
|
|
4512
|
+
if (settled.kind === "conflict") {
|
|
4513
|
+
await park(t, `merge conflict after ${resumeReason}: ${settled.conflict ?? "unknown conflict"}`, "merge-conflict", gateAuthor, rs?.attempts ?? 0, startMs, gateFails, consults, tokens, metered, retryMode);
|
|
4399
4514
|
return;
|
|
4400
4515
|
}
|
|
4401
|
-
graph = setStatus(graph, t.id, "done");
|
|
4402
|
-
saveGraph(repoRoot, graph);
|
|
4403
|
-
journal.append("task-done", t.id, {
|
|
4404
|
-
attempts: rs?.attempts ?? 0, assignment: gateAuthor, taskContentDigest: contentDigest,
|
|
4405
|
-
authors: mergedAuthors(await subjectAuthors(journal.read(), t.id, wt, taskBase)),
|
|
4406
|
-
});
|
|
4407
|
-
journal.append("merge", t.id, { branch: taskBranch, commit: await integrationHead(intWt) });
|
|
4408
|
-
await trackedDriver.project?.(t.id, "completed");
|
|
4409
|
-
journal.telemetry({
|
|
4410
|
-
taskId: t.id, shape: t.shape, adapter: gateAuthor.adapter, model: gateAuthor.model,
|
|
4411
|
-
channel: gateAuthor.channel, attempts: rs?.attempts ?? 0, outcome: "done",
|
|
4412
|
-
durationMs: Date.now() - startMs, firstAttemptOk: false, gateFails, consults, retryMode,
|
|
4413
|
-
});
|
|
4414
4516
|
await reconcile({ spareLiveLlm: true });
|
|
4415
4517
|
return;
|
|
4416
4518
|
}
|
|
@@ -6455,7 +6557,14 @@ export async function runDaemon(repoRoot, opts = {}) {
|
|
|
6455
6557
|
if (results.some((g) => g.gate === "test" && !g.pass
|
|
6456
6558
|
&& !(cfg.executionPolicy?.boundedInfrastructure && failureDisposition(g) === "infrastructure")))
|
|
6457
6559
|
testGateFailed = true;
|
|
6458
|
-
|
|
6560
|
+
const live = assignment;
|
|
6561
|
+
const settled = await settleResults(results, {
|
|
6562
|
+
wt, taskBase, taskBranch, gated, commit: gateSubject.commit, outsideScopeForced,
|
|
6563
|
+
author: live, attempts: attempt + 1,
|
|
6564
|
+
infraReason: (g) => `${g.gate}: ${g.details}${g.meta?.recoveryBlocked ? ` — ${g.meta.recoveryBlocked}` : ""}`,
|
|
6565
|
+
reobserve: { attempt, replayed: gateSubject.replayedFromAttempt !== undefined },
|
|
6566
|
+
disposition: () => ({ author: live, attempt }),
|
|
6567
|
+
firstAttemptOk: attempt === 0, metering: { tokens, meteredAttempts: tokens ? metered : undefined },
|
|
6459
6568
|
// T6: every gate — the review included — is satisfied on this commit, so the failure brief
|
|
6460
6569
|
// this loop is still holding describes nothing outstanding. It is dropped HERE, before the
|
|
6461
6570
|
// merge, because a conflict below sends the task around the attempt loop again: a brief kept
|
|
@@ -6464,35 +6573,23 @@ export async function runDaemon(repoRoot, opts = {}) {
|
|
|
6464
6573
|
// leaving that dispatch's row indistinguishable from an amnesiac one. Rebuilt exactly as the
|
|
6465
6574
|
// gate-fail brief is, so prior-RUN evidence (retired by its own rule, not by this reviewer)
|
|
6466
6575
|
// survives and only this run's settled findings go.
|
|
6467
|
-
|
|
6468
|
-
|
|
6469
|
-
|
|
6470
|
-
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
|
|
6477
|
-
|
|
6478
|
-
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
|
|
6482
|
-
|
|
6483
|
-
|
|
6484
|
-
}
|
|
6485
|
-
graph = setStatus(graph, t.id, "done");
|
|
6486
|
-
saveGraph(repoRoot, graph);
|
|
6487
|
-
journal.append("task-done", t.id, {
|
|
6488
|
-
attempts: attempt + 1, assignment, taskContentDigest: contentDigest,
|
|
6489
|
-
authors: mergedAuthors(await subjectAuthors(journal.read(), t.id, wt, taskBase)),
|
|
6490
|
-
});
|
|
6491
|
-
journal.append("merge", t.id, { branch: taskBranch, commit: await integrationHead(intWt) });
|
|
6492
|
-
await trackedDriver.project?.(t.id, "completed");
|
|
6493
|
-
// firstAttemptOk/gateFails/consults are recorded FACTS, not policy — a parkKind:"stall" row is
|
|
6494
|
-
// recorded but NOT quality-negative in v1.6; Phase 12 owns reward policy, so flipping it later needs zero data migration.
|
|
6495
|
-
journal.telemetry({ taskId: t.id, shape: t.shape, adapter: assignment.adapter, model: assignment.model, channel: assignment.channel, attempts: attempt + 1, outcome: "done", durationMs: Date.now() - startMs, firstAttemptOk: attempt === 0, gateFails, consults, tokens, meteredAttempts: tokens ? metered : undefined, retryMode });
|
|
6576
|
+
beforeMerge: () => {
|
|
6577
|
+
feedback = withCarriedEvidence("");
|
|
6578
|
+
fatalStop.signal.throwIfAborted();
|
|
6579
|
+
executionSignal()?.throwIfAborted();
|
|
6580
|
+
},
|
|
6581
|
+
});
|
|
6582
|
+
if (settled.kind === "regate")
|
|
6583
|
+
continue gateLoop;
|
|
6584
|
+
if (settled.kind === "parked")
|
|
6585
|
+
return;
|
|
6586
|
+
if (settled.kind === "conflict") {
|
|
6587
|
+
const v = await runConsult("merge-conflict", output, settled.conflict ?? "", results);
|
|
6588
|
+
if (await applyVerdict(v, attempt + 1, "merge-conflict"))
|
|
6589
|
+
continue attempts;
|
|
6590
|
+
return;
|
|
6591
|
+
}
|
|
6592
|
+
if (settled.kind === "merged") {
|
|
6496
6593
|
// D-07 done means gone (merged-P42-01-worker incident): a merged task's worker pane closes on
|
|
6497
6594
|
// the task-done path, not at run end. Only THIS successful attempt's `slot` is in scope — prior
|
|
6498
6595
|
// failed attempts' slots stay in keptSlots governed by keepPanes (they hold failure context the
|
|
@@ -6511,38 +6608,6 @@ export async function runDaemon(repoRoot, opts = {}) {
|
|
|
6511
6608
|
}
|
|
6512
6609
|
break gateLoop;
|
|
6513
6610
|
}
|
|
6514
|
-
// OBS-540: infra is a fail-closed NON-verdict, not quality degradation. Park with the blocker
|
|
6515
|
-
// already journaled by onGate, before gateFails and before any ladder selection can fund an
|
|
6516
|
-
// identical retry in the same environment. Parsed judge refusals never carry infra and keep
|
|
6517
|
-
// flowing through the chargeable quality path below.
|
|
6518
|
-
const unavailableReview = results.find((g) => gateFailed(g) && g.meta?.noEligibleReviewer === true);
|
|
6519
|
-
if (unavailableReview) {
|
|
6520
|
-
await park(t, gateFailApprovalReason(t.id, unavailableReview.details, true), "gate-fail", assignment, attempt + 1, startMs, gateFails, consults, tokens, metered, retryMode);
|
|
6521
|
-
return;
|
|
6522
|
-
}
|
|
6523
|
-
const infraFailure = results.find((g) => gateFailed(g) && isInfraResult(g));
|
|
6524
|
-
if (infraFailure) {
|
|
6525
|
-
await park(t, `${infraFailure.gate}: ${infraFailure.details}${infraFailure.meta?.recoveryBlocked ? ` — ${infraFailure.meta.recoveryBlocked}` : ""}`, "infra", assignment, attempt + 1, startMs, gateFails, consults, tokens, metered, retryMode);
|
|
6526
|
-
return;
|
|
6527
|
-
}
|
|
6528
|
-
// OBS-1007: a cap trip is a typed park of its own — the diff cannot shrink by retrying and the
|
|
6529
|
-
// only honest verb is a recheck under a raised cap. Decided BEFORE the authoring classifier, which
|
|
6530
|
-
// used to lift `gates.diffCap` out of the cap prose as a files[] hint.
|
|
6531
|
-
const capTrip = results.find(isDiffCapPark);
|
|
6532
|
-
if (capTrip) {
|
|
6533
|
-
await park(t, `${capTrip.gate}: ${capTrip.details}`, "diff-cap", assignment, attempt + 1, startMs, gateFails, consults, tokens, metered, retryMode);
|
|
6534
|
-
return;
|
|
6535
|
-
}
|
|
6536
|
-
if (await adjudicateInfraShapedRed(t, results, wt, gateSubject.commit, attempt, gateSubject.replayedFromAttempt !== undefined, (reason) => park(t, reason, "infra", assignment, attempt + 1, startMs, gateFails, consults, tokens, metered, retryMode)))
|
|
6537
|
-
return;
|
|
6538
|
-
if (outsideScopeForced && freshOutsideScopeRed(t, gateSubject.commit)) {
|
|
6539
|
-
await park(t, outsideScopePark(t), "gate-fail", assignment, attempt + 1, startMs, gateFails, consults, tokens, metered, retryMode);
|
|
6540
|
-
return;
|
|
6541
|
-
}
|
|
6542
|
-
// OBS-547: who pays for this red is decided by the run's collateral prediction (see
|
|
6543
|
-
// dispositionScopeRed) — an authoring defect parks unchargeable before any accounting below.
|
|
6544
|
-
if (await dispositionScopeRed(t, results, assignment, attempt, startMs, gateFails, consults, tokens, metered, retryMode, await treeOrDiffPaths(taskBase, wt)))
|
|
6545
|
-
return;
|
|
6546
6611
|
gateFails++; // this attempt's gates failed — the one place quality degradation is verified (never inferred from attempts)
|
|
6547
6612
|
// v1.53 T3: prefer the CLI's own session id captured from this attempt's output (kimi's resume
|
|
6548
6613
|
// trailer) over the harness slot name; absent hook or no capture keeps today's slot-name id.
|
|
@@ -2,9 +2,21 @@ import { type AuthHealth, type WorkerAdapter } from "../../adapters/types.js";
|
|
|
2
2
|
import { type DenyScopeKey, type InitialDenyProps, type MapEntry, type RoutingMode, type StagedDenyKey, type Tier, type TickmarkrConfig } from "../../config/config.js";
|
|
3
3
|
import { type Effort, type Shape } from "../../graph/schema.js";
|
|
4
4
|
export type FleetSteeringKey = "review" | "consult";
|
|
5
|
+
/** T10 (D-939): the routing.allow entry a classification staged this session excluded — the row's own
|
|
6
|
+
* entry in its ownAllow spelling, added to the staged complement so Space can admit it in-session.
|
|
7
|
+
* `model` is the classified id (the tiers key), `displayModel` the browser row it was staged from. */
|
|
8
|
+
export type FleetSeededAllow = {
|
|
9
|
+
adapter: string;
|
|
10
|
+
model: string;
|
|
11
|
+
displayModel: string;
|
|
12
|
+
identity?: string;
|
|
13
|
+
entry: string;
|
|
14
|
+
};
|
|
5
15
|
export type FleetEditorState = Record<DenyScopeKey, string[]> & {
|
|
6
16
|
/** OBS-1046: the staged routing.allow complement, distinct from the authored deny lists above */
|
|
7
17
|
allowOut?: string[];
|
|
18
|
+
/** T10: one seed per staged classification the current allow form left out */
|
|
19
|
+
seededAllowOut?: FleetSeededAllow[];
|
|
8
20
|
classifications: FleetClassification[];
|
|
9
21
|
/** OBS-1182: staged launch effort per adapter → model; absent = the CLI's own default */
|
|
10
22
|
efforts?: Record<string, Record<string, Effort>>;
|
|
@@ -18,12 +30,17 @@ export type FleetEditorState = Record<DenyScopeKey, string[]> & {
|
|
|
18
30
|
};
|
|
19
31
|
/** T8: the staged classifications and efforts every preview folds — the same metadata a review stages,
|
|
20
32
|
* so a preview ranks what w would write before the first review and after an escaped one. */
|
|
21
|
-
export type FleetStagedMetadata = Pick<FleetEditorState, "classifications" | "efforts">;
|
|
33
|
+
export type FleetStagedMetadata = Pick<FleetEditorState, "classifications" | "efforts" | "seededAllowOut">;
|
|
22
34
|
/** OBS-525: auto reverts the shape's WHOLE routing declaration — pool included, not just the pin.
|
|
23
35
|
* prefer survives: it biases auto routing and is valid without pin/pool. */
|
|
24
36
|
export declare function autoMapEntry(entry: MapEntry | undefined): MapEntry;
|
|
25
37
|
export type FleetOverlayReview = {
|
|
26
38
|
kind: "empty";
|
|
39
|
+
}
|
|
40
|
+
/** T10: a staged batch the one-destination review cannot write (mixed destinations, a stray seed) */
|
|
41
|
+
| {
|
|
42
|
+
kind: "refused";
|
|
43
|
+
reason: string;
|
|
27
44
|
} | {
|
|
28
45
|
kind: "diff";
|
|
29
46
|
before: string;
|
|
@@ -131,7 +148,7 @@ export type FleetStagedRouting = {
|
|
|
131
148
|
ok: false;
|
|
132
149
|
error: string;
|
|
133
150
|
};
|
|
134
|
-
export declare function FleetApp({ ageMs, agents, initialAllowOut, initialEfforts, modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewAdvisories, reviewOverlay, reloadGuard, stagedRouting, entry, initialJudge, judgeSeats, viewRows, frameColumns, frameRows, ...initialDenyProps }: InitialDenyProps & {
|
|
151
|
+
export declare function FleetApp({ ageMs, agents, initialAllowOut, initialEfforts, modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewAdvisories, reviewOverlay, reloadGuard, stagedRouting, holderOf, entry, initialJudge, judgeSeats, viewRows, frameColumns, frameRows, ...initialDenyProps }: InitialDenyProps & {
|
|
135
152
|
ageMs: number | null;
|
|
136
153
|
agents: AgentCli[];
|
|
137
154
|
/** OBS-1046: adapter ids and adapter:model keys routing.allow leaves out of the fleet */
|
|
@@ -158,7 +175,9 @@ export declare function FleetApp({ ageMs, agents, initialAllowOut, initialEffort
|
|
|
158
175
|
reloadGuard: (bytes: string) => string | null;
|
|
159
176
|
/** the routing policy the staged deny sets load as (fleet: the config loader over the candidate
|
|
160
177
|
* bytes); absent ⇒ the four staged sets alone, with no allowlist */
|
|
161
|
-
stagedRouting?: (deny: FleetStagedDeny) => FleetStagedRouting;
|
|
178
|
+
stagedRouting?: (deny: FleetStagedDeny, stage?: FleetStagedMetadata) => FleetStagedRouting;
|
|
179
|
+
/** T10: the overlay `path:line` holding a dotted config scope in this repository, undefined = defaults */
|
|
180
|
+
holderOf?: (dotted: string) => string | undefined;
|
|
162
181
|
entry?: "presets" | "probe";
|
|
163
182
|
/** resolved config judge as "adapter:model" — shown on the (keep default) picker row */
|
|
164
183
|
initialJudge?: string;
|
|
@@ -171,7 +190,7 @@ export declare function FleetApp({ ageMs, agents, initialAllowOut, initialEffort
|
|
|
171
190
|
/** terminal height — set only on a real TTY so the footer sticks to the bottom */
|
|
172
191
|
frameRows?: number;
|
|
173
192
|
}): import("react").JSX.Element;
|
|
174
|
-
export declare function runFleetInkEditor({ ageMs, adapters, health, initialAllowOut, initialEfforts, modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewAdvisories, reviewOverlay, reloadGuard, stagedRouting, entry, initialJudge, judgeSeats, initialInput, input, output, debug, ...initialDenyProps }: InitialDenyProps & {
|
|
193
|
+
export declare function runFleetInkEditor({ ageMs, adapters, health, initialAllowOut, initialEfforts, modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewAdvisories, reviewOverlay, reloadGuard, stagedRouting, holderOf, entry, initialJudge, judgeSeats, initialInput, input, output, debug, ...initialDenyProps }: InitialDenyProps & {
|
|
175
194
|
ageMs: number | null;
|
|
176
195
|
adapters: WorkerAdapter[];
|
|
177
196
|
health: Record<string, AuthHealth>;
|
|
@@ -199,7 +218,9 @@ export declare function runFleetInkEditor({ ageMs, adapters, health, initialAllo
|
|
|
199
218
|
reloadGuard: (bytes: string) => string | null;
|
|
200
219
|
/** the routing policy the staged deny sets load as (fleet: the config loader over the candidate
|
|
201
220
|
* bytes); absent ⇒ the four staged sets alone, with no allowlist */
|
|
202
|
-
stagedRouting?: (deny: FleetStagedDeny) => FleetStagedRouting;
|
|
221
|
+
stagedRouting?: (deny: FleetStagedDeny, stage?: FleetStagedMetadata) => FleetStagedRouting;
|
|
222
|
+
/** T10: the overlay `path:line` holding a dotted config scope in this repository, undefined = defaults */
|
|
223
|
+
holderOf?: (dotted: string) => string | undefined;
|
|
203
224
|
/** "presets" = init's entry: Esc in the browser is HOME to the preset overlay, not quit */
|
|
204
225
|
entry?: "presets" | "probe";
|
|
205
226
|
initialJudge?: string;
|