@claudexor/orchestrator 4.1.1 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/account-pool.d.ts +11 -0
- package/dist/account-pool.d.ts.map +1 -1
- package/dist/account-pool.js +9 -0
- package/dist/account-pool.js.map +1 -1
- package/dist/account-resolution.d.ts +5 -13
- package/dist/account-resolution.d.ts.map +1 -1
- package/dist/account-resolution.js +14 -15
- package/dist/account-resolution.js.map +1 -1
- package/dist/attemptTelemetry.d.ts +4 -6
- package/dist/attemptTelemetry.d.ts.map +1 -1
- package/dist/attemptTelemetry.js +6 -10
- package/dist/attemptTelemetry.js.map +1 -1
- package/dist/attemptUsageCost.d.ts +6 -1
- package/dist/attemptUsageCost.d.ts.map +1 -1
- package/dist/attemptUsageCost.js +16 -1
- package/dist/attemptUsageCost.js.map +1 -1
- package/dist/budgetFailure.d.ts +4 -1
- package/dist/budgetFailure.d.ts.map +1 -1
- package/dist/budgetFailure.js +4 -2
- package/dist/budgetFailure.js.map +1 -1
- package/dist/candidateEvidence.d.ts.map +1 -1
- package/dist/candidateEvidence.js +7 -2
- package/dist/candidateEvidence.js.map +1 -1
- package/dist/candidateOutputs.d.ts.map +1 -1
- package/dist/candidateOutputs.js +6 -0
- package/dist/candidateOutputs.js.map +1 -1
- package/dist/candidateReview.d.ts.map +1 -1
- package/dist/candidateReview.js +1 -0
- package/dist/candidateReview.js.map +1 -1
- package/dist/candidateTerminals.d.ts +12 -1
- package/dist/candidateTerminals.d.ts.map +1 -1
- package/dist/candidateTerminals.js +51 -21
- package/dist/candidateTerminals.js.map +1 -1
- package/dist/captureRefusal.d.ts +28 -0
- package/dist/captureRefusal.d.ts.map +1 -0
- package/dist/captureRefusal.js +54 -0
- package/dist/captureRefusal.js.map +1 -0
- package/dist/continuation-evidence.d.ts.map +1 -1
- package/dist/continuation-evidence.js +1 -0
- package/dist/continuation-evidence.js.map +1 -1
- package/dist/continuity-notice.d.ts.map +1 -1
- package/dist/continuity-notice.js +1 -0
- package/dist/continuity-notice.js.map +1 -1
- package/dist/continuity-terminal.d.ts +3 -1
- package/dist/continuity-terminal.d.ts.map +1 -1
- package/dist/continuity-terminal.js.map +1 -1
- package/dist/credential-cooldown.d.ts +2 -0
- package/dist/credential-cooldown.d.ts.map +1 -1
- package/dist/credential-cooldown.js +7 -1
- package/dist/credential-cooldown.js.map +1 -1
- package/dist/credential-preflight.d.ts.map +1 -1
- package/dist/credential-preflight.js +1 -0
- package/dist/credential-preflight.js.map +1 -1
- package/dist/credential-profile-rotation.d.ts.map +1 -1
- package/dist/credential-profile-rotation.js +4 -3
- package/dist/credential-profile-rotation.js.map +1 -1
- package/dist/decisionBudget.d.ts +17 -5
- package/dist/decisionBudget.d.ts.map +1 -1
- package/dist/decisionBudget.js +28 -6
- package/dist/decisionBudget.js.map +1 -1
- package/dist/deepScanReducer.d.ts +1 -1
- package/dist/deepScanReducer.d.ts.map +1 -1
- package/dist/delegationBudgetAuthority.d.ts +2 -2
- package/dist/delegationBudgetAuthority.d.ts.map +1 -1
- package/dist/delegationBudgetAuthority.js.map +1 -1
- package/dist/delegationFailure.d.ts +104 -7
- package/dist/delegationFailure.d.ts.map +1 -1
- package/dist/delegationFailure.js +47 -31
- package/dist/delegationFailure.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/inrun-continuity-types.d.ts +13 -1
- package/dist/inrun-continuity-types.d.ts.map +1 -1
- package/dist/inrun-continuity.d.ts +10 -2
- package/dist/inrun-continuity.d.ts.map +1 -1
- package/dist/inrun-continuity.js +69 -50
- package/dist/inrun-continuity.js.map +1 -1
- package/dist/inrun-continuity.test-support.d.ts +17 -3
- package/dist/inrun-continuity.test-support.d.ts.map +1 -1
- package/dist/inrun-continuity.test-support.js +4 -1
- package/dist/inrun-continuity.test-support.js.map +1 -1
- package/dist/interaction.d.ts +5 -5
- package/dist/interaction.d.ts.map +1 -1
- package/dist/interaction.js +58 -26
- package/dist/interaction.js.map +1 -1
- package/dist/modelGovernance.d.ts +3 -1
- package/dist/modelGovernance.d.ts.map +1 -1
- package/dist/modelGovernance.js +4 -0
- package/dist/modelGovernance.js.map +1 -1
- package/dist/orchestrator-credentials.d.ts +5 -4
- package/dist/orchestrator-credentials.d.ts.map +1 -1
- package/dist/orchestrator-credentials.js +7 -4
- package/dist/orchestrator-credentials.js.map +1 -1
- package/dist/orchestrator.d.ts +10 -1
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +283 -335
- package/dist/orchestrator.js.map +1 -1
- package/dist/persistedPatch.js +2 -2
- package/dist/persistedPatch.js.map +1 -1
- package/dist/plannerAttempt.d.ts +1 -1
- package/dist/plannerAttempt.d.ts.map +1 -1
- package/dist/pool-attempt-evidence.d.ts +62 -0
- package/dist/pool-attempt-evidence.d.ts.map +1 -0
- package/dist/pool-attempt-evidence.js +153 -0
- package/dist/pool-attempt-evidence.js.map +1 -0
- package/dist/pre-progress-refusal.d.ts +43 -6
- package/dist/pre-progress-refusal.d.ts.map +1 -1
- package/dist/pre-progress-refusal.js +53 -8
- package/dist/pre-progress-refusal.js.map +1 -1
- package/dist/processing-routing.d.ts.map +1 -1
- package/dist/processing-routing.js +9 -0
- package/dist/processing-routing.js.map +1 -1
- package/dist/quota-refusal-scope.d.ts +11 -0
- package/dist/quota-refusal-scope.d.ts.map +1 -0
- package/dist/quota-refusal-scope.js +21 -0
- package/dist/quota-refusal-scope.js.map +1 -0
- package/dist/readonlyTerminals.d.ts +55 -0
- package/dist/readonlyTerminals.d.ts.map +1 -0
- package/dist/readonlyTerminals.js +205 -0
- package/dist/readonlyTerminals.js.map +1 -0
- package/dist/retry-processing-intent.d.ts +19 -0
- package/dist/retry-processing-intent.d.ts.map +1 -0
- package/dist/retry-processing-intent.js +32 -0
- package/dist/retry-processing-intent.js.map +1 -0
- package/dist/root-ledger.d.ts.map +1 -1
- package/dist/root-ledger.js +3 -1
- package/dist/root-ledger.js.map +1 -1
- package/dist/rotation-predicate.d.ts +7 -23
- package/dist/rotation-predicate.d.ts.map +1 -1
- package/dist/rotation-predicate.js +45 -0
- package/dist/rotation-predicate.js.map +1 -1
- package/dist/runEventLog.d.ts +1 -0
- package/dist/runEventLog.d.ts.map +1 -1
- package/dist/runEventLog.js +3 -2
- package/dist/runEventLog.js.map +1 -1
- package/dist/runSupport.d.ts +2 -2
- package/dist/runSupport.d.ts.map +1 -1
- package/dist/runSupport.js +3 -4
- package/dist/runSupport.js.map +1 -1
- package/dist/runTerminalContext.d.ts +7 -1
- package/dist/runTerminalContext.d.ts.map +1 -1
- package/dist/runTerminalContext.js +1 -0
- package/dist/runTerminalContext.js.map +1 -1
- package/dist/runTerminalHelpers.d.ts +25 -1
- package/dist/runTerminalHelpers.d.ts.map +1 -1
- package/dist/runTerminalHelpers.js +69 -6
- package/dist/runTerminalHelpers.js.map +1 -1
- package/dist/runTerminalResults.d.ts +25 -3
- package/dist/runTerminalResults.d.ts.map +1 -1
- package/dist/runTerminalResults.js +61 -29
- package/dist/runTerminalResults.js.map +1 -1
- package/dist/runTerminals.d.ts +0 -1
- package/dist/runTerminals.d.ts.map +1 -1
- package/dist/runTerminals.js +47 -11
- package/dist/runTerminals.js.map +1 -1
- package/dist/secretDiff.d.ts +14 -11
- package/dist/secretDiff.d.ts.map +1 -1
- package/dist/secretDiff.js +21 -28
- package/dist/secretDiff.js.map +1 -1
- package/dist/terminalOutcome.d.ts +24 -4
- package/dist/terminalOutcome.d.ts.map +1 -1
- package/dist/terminalOutcome.js +32 -5
- package/dist/terminalOutcome.js.map +1 -1
- package/dist/transientClassify.d.ts +1 -0
- package/dist/transientClassify.d.ts.map +1 -1
- package/dist/transientClassify.js +3 -0
- package/dist/transientClassify.js.map +1 -1
- package/package.json +17 -17
package/dist/orchestrator.js
CHANGED
|
@@ -4,11 +4,13 @@ import { openRunScopeCustody, scopedRouteContext, scopedSpecEnv, } from "./runSc
|
|
|
4
4
|
import { delegationBeltFor } from "./delegationBelt.js";
|
|
5
5
|
import { bindProcessingAdmission, processingAttemptCostEvidence, processingAdmissionForLease, updateProcessingStreamHold, ProcessingBudgetAdmissionError, } from "./processing-dispatch.js";
|
|
6
6
|
import { cancelledCandidatesResult, emptyCandidateResult, failedCandidatesResult, failAfterCandidates, } from "./candidateTerminals.js";
|
|
7
|
+
import { deepScanFailureResult, readOnlyChainFailureResult, } from "./readonlyTerminals.js";
|
|
7
8
|
import { effectiveAuthPreference, observeNativeSessionEvent, resumeSessionForProfile, } from "./credential-profiles.js";
|
|
8
9
|
import { AdmissionProfileProbes, OrchestratorCredentials, reviewerProfileResolver, } from "./orchestrator-credentials.js";
|
|
9
10
|
import { continuityWorkspace, resumableOf, resumableTerminal, } from "./continuity-terminal.js";
|
|
10
11
|
import { openContinuity } from "./continue-from.js";
|
|
11
12
|
import { accountPoolRows } from "./account-pool.js";
|
|
13
|
+
import { PoolAttemptEvidence, terminalPoolAttempts, withPoolAttempts, } from "./pool-attempt-evidence.js";
|
|
12
14
|
import { writeRunTelemetryArtifact } from "./runTelemetryWriter.js";
|
|
13
15
|
import { buildFileBackedSynthesisInput, materializeWinnerOutputs, stageFileBackedContext, writeCandidateAttemptArtifacts, } from "./candidateOutputs.js";
|
|
14
16
|
import { processAttemptUsage } from "./attemptUsage.js";
|
|
@@ -16,7 +18,7 @@ import { captureDirectoryCandidate, directoryHasOutput, directoryCandidateStable
|
|
|
16
18
|
import { appliedAttemptFacts, assertDelegatedEvidence, isMutatingAccess, outerBoundaryNotice, scopedHarnessHome, selectLaneHome, followSpecHome, } from "./delegatedHome.js";
|
|
17
19
|
import * as AC from "./attemptUsageCost.js";
|
|
18
20
|
import * as custody from "./continuation-custody.js";
|
|
19
|
-
import { candidateRoster, candidateStatuses, convergenceOutcomeFacts, isWorkingCandidate, partitionCandidates, toCandidateEvidence,
|
|
21
|
+
import { candidateRoster, candidateStatuses, convergenceOutcomeFacts, isWorkingCandidate, partitionCandidates, toCandidateEvidence, } from "./candidateEvidence.js";
|
|
20
22
|
import { capabilityIntents } from "@claudexor/gateway";
|
|
21
23
|
import { reviewCandidateRuns, resolveEngineReview, unreviewedCandidateEvidence, evaluateUnreviewedConvergence, } from "./candidateReview.js";
|
|
22
24
|
import { join } from "node:path";
|
|
@@ -39,7 +41,7 @@ import { resolveRunInputDefaults } from "./run-input-resolution.js";
|
|
|
39
41
|
import { beginAnnouncedRun } from "./runEventLog.js";
|
|
40
42
|
import { arbitrationBudgetOptions } from "./decisionBudget.js";
|
|
41
43
|
import { buildRevisePrompt } from "./revisePrompt.js";
|
|
42
|
-
import { cancelledResult, declaredFailure, failTerminally, guardAnnouncedRun, writeFailure,
|
|
44
|
+
import { cancelledResult, declaredFailure, failTerminally, guardAnnouncedRun, writeFailure, cancelReasonFromSignalToken, } from "./runTerminals.js";
|
|
43
45
|
import { budgetFailureRecord, classifyBudgetFailure } from "./budgetFailure.js";
|
|
44
46
|
import { finalizeStructuredOutput } from "./structuredOutput.js";
|
|
45
47
|
import { admitRun } from "./runAdmission.js";
|
|
@@ -56,14 +58,14 @@ import { createAttemptTelemetry, observeAttemptTelemetry, setAttemptOutcome, tel
|
|
|
56
58
|
import { newAttemptOutputMarkers } from "./attemptOutputMarkers.js";
|
|
57
59
|
import * as delegateFailure from "./delegationFailure.js";
|
|
58
60
|
import * as secretDiff from "./secretDiff.js";
|
|
59
|
-
import {
|
|
60
|
-
import { finalizeAttempt, readOnlyNoSuccessTerminal, unrecoveredToolErrorFailure, webEvidenceFailure, } from "./attemptFinalize.js";
|
|
61
|
+
import { finalizeAttempt, unrecoveredToolErrorFailure, webEvidenceFailure, } from "./attemptFinalize.js";
|
|
61
62
|
import { resolveWorkReportEnvelope, unwrapWorkReportEnvelope, } from "./workReportEnvelope.js";
|
|
62
63
|
import { buildContinuationPacket, decideContinuation, synthesizeContinuationRequest, } from "./continuation.js";
|
|
63
64
|
import { interactionChannelFor } from "./interaction.js";
|
|
64
65
|
import { liveAttempt } from "./live-input.js";
|
|
65
66
|
import { finalVerifyRun, gateSpecsFromContract, renderTestsEvidence } from "./contract-gates.js";
|
|
66
67
|
import { buildTaskContract } from "./task-contract-builder.js";
|
|
68
|
+
import { recordProcessingIntent } from "./retry-processing-intent.js";
|
|
67
69
|
import { prepareReviewEvidenceDir, recordReviewEvidenceCleanup } from "./reviewEvidence.js";
|
|
68
70
|
import { ArtifactStore } from "@claudexor/artifact-store";
|
|
69
71
|
import { buildContextPack, rawContextForEnvelope, writeEvidencePacket } from "@claudexor/context";
|
|
@@ -809,6 +811,11 @@ export class Orchestrator {
|
|
|
809
811
|
effectiveN: out.length,
|
|
810
812
|
droppedLanes,
|
|
811
813
|
});
|
|
814
|
+
if (runId) {
|
|
815
|
+
// Freeze each selected lane's inherited speed for Exact Retry (first routing wins).
|
|
816
|
+
const store = this.artifactStore(input);
|
|
817
|
+
recordProcessingIntent(store, store.runPaths(runId).contextDir, Object.fromEntries(out.flatMap((lane) => lane.processingIntent ? [[lane.adapter.id, lane.processingIntent]] : [])));
|
|
818
|
+
}
|
|
812
819
|
this.requestRequirements.requireEffectiveBrowser(input.browser === true, out.map((lane) => lane.browserRequirement));
|
|
813
820
|
// Owner decision (2026-07-26): known PRE-START belt unavailability does
|
|
814
821
|
// not discard the requested Agent work. Continue without Delegate and emit
|
|
@@ -1312,7 +1319,10 @@ export class Orchestrator {
|
|
|
1312
1319
|
* must record what it ran under, not just why it stopped. REQUIRED (no
|
|
1313
1320
|
* default): a silent fallback here would spawn a delegated attempt on the
|
|
1314
1321
|
* operator's real home while the record still claimed scoped state. */
|
|
1315
|
-
harnessHome, observedPaths = new Set()
|
|
1322
|
+
harnessHome, observedPaths = new Set(),
|
|
1323
|
+
/** The caller's, like `harnessHome`: its catch keeps the tries' accounts.
|
|
1324
|
+
* REQUIRED, so no caller can drop them by omission again. */
|
|
1325
|
+
poolEvidence) {
|
|
1316
1326
|
const adapter = routed.adapter;
|
|
1317
1327
|
const knobs = this.routeSpecKnobs(routed, contract, modelHint, effortHint);
|
|
1318
1328
|
// Isolated scoped-home sessions are never retained after disposal.
|
|
@@ -1418,6 +1428,7 @@ export class Orchestrator {
|
|
|
1418
1428
|
let beltFailureDisclosed = false;
|
|
1419
1429
|
const errors = [];
|
|
1420
1430
|
let answer = new secretDiff.CountedAnswerAssembly();
|
|
1431
|
+
let currentCapture;
|
|
1421
1432
|
const retryPolicy = transientRetryPolicy(this.config(contract.repo.root));
|
|
1422
1433
|
// QA-024: the delegation belt is the ONLY engine-owned extra MCP server
|
|
1423
1434
|
// injected into an agent lane (the browser MCP rides its own field), so its
|
|
@@ -1444,9 +1455,11 @@ export class Orchestrator {
|
|
|
1444
1455
|
harnessHome,
|
|
1445
1456
|
workOrder: prompt,
|
|
1446
1457
|
firstPrompt: spec.prompt,
|
|
1458
|
+
poolEvidence,
|
|
1447
1459
|
}), { spec, continuation: runInput?.continuation, store, telemetry }));
|
|
1448
1460
|
try {
|
|
1449
1461
|
for (let nativeTry = 0; !signal?.aborted; nativeTry += 1) {
|
|
1462
|
+
currentCapture = undefined;
|
|
1450
1463
|
// A3 per-try isolation: neither output nor progress markers leak across tries.
|
|
1451
1464
|
if (nativeTry > 0) {
|
|
1452
1465
|
answer = new secretDiff.CountedAnswerAssembly();
|
|
@@ -1462,9 +1475,8 @@ export class Orchestrator {
|
|
|
1462
1475
|
? AbortSignal.any([signal, attemptAbort.signal])
|
|
1463
1476
|
: attemptAbort.signal;
|
|
1464
1477
|
activeSessionId = runSpec.session_id;
|
|
1465
|
-
continuity.beginTry(runSpec, nativeTry, attemptAbort);
|
|
1466
1478
|
// #363: bound before the spawn — a credential change mid-try voids it.
|
|
1467
|
-
const refusal =
|
|
1479
|
+
const refusal = continuity.beginTry(runSpec, nativeTry, attemptAbort);
|
|
1468
1480
|
const transientStart = telemetry.transientFailures.length;
|
|
1469
1481
|
const rateLimitStart = telemetry.rateLimits.length;
|
|
1470
1482
|
let rawPatch = null;
|
|
@@ -1588,30 +1600,42 @@ export class Orchestrator {
|
|
|
1588
1600
|
// #31: the centralized retry gate reads the classified `retryable` verdict.
|
|
1589
1601
|
const sawRetryable = newTransients.some((f) => f.retryable);
|
|
1590
1602
|
const sawTypedLimit = telemetry.rateLimits.length > rateLimitStart;
|
|
1603
|
+
const tryFacts = {
|
|
1604
|
+
harnessErrored,
|
|
1605
|
+
originalError: errors[0],
|
|
1606
|
+
budgetStopped: streamBudgetDenied,
|
|
1607
|
+
aborted: signal?.aborted === true,
|
|
1608
|
+
requestRefused: Boolean(telemetry.requestRefusal),
|
|
1609
|
+
newTransients,
|
|
1610
|
+
markers: telemetry.outputMarkers,
|
|
1611
|
+
};
|
|
1612
|
+
// Its account row first: an infrastructure failure below never erases it.
|
|
1613
|
+
continuity.settleTry(tryFacts);
|
|
1591
1614
|
if (directory)
|
|
1592
1615
|
await captureDirectory();
|
|
1593
|
-
|
|
1616
|
+
currentCapture = directory
|
|
1617
|
+
? undefined
|
|
1618
|
+
: await secretDiff.captureCandidateDiff(wsm, envelope);
|
|
1619
|
+
const currentDiff = secretDiff.capturedDiff(currentCapture);
|
|
1594
1620
|
const workspaceUnchanged = directory
|
|
1595
1621
|
? directoryCapture.files?.noChanges === true
|
|
1596
|
-
: currentDiff
|
|
1622
|
+
: currentDiff?.trim().length === 0;
|
|
1597
1623
|
const deliverableEmpty = workspaceUnchanged && answer.text().length === 0;
|
|
1598
1624
|
const delivered = !harnessErrored && !deliverableEmpty && !signal?.aborted;
|
|
1599
1625
|
this.credentials.noteTryServed(refusal, telemetry.outputMarkers, delivered);
|
|
1600
1626
|
const verdict = await continuity.afterTry({
|
|
1627
|
+
...tryFacts,
|
|
1628
|
+
// A cancel or deadline that landed during capture still stops the
|
|
1629
|
+
// attempt; the try's own facts above stay its evidence.
|
|
1630
|
+
stopped: signal?.aborted === true,
|
|
1601
1631
|
runSpec,
|
|
1602
1632
|
nativeTry,
|
|
1603
|
-
harnessErrored,
|
|
1604
|
-
budgetStopped: streamBudgetDenied,
|
|
1605
|
-
aborted: signal?.aborted === true,
|
|
1606
|
-
requestRefused: Boolean(telemetry.requestRefusal),
|
|
1607
|
-
newTransients,
|
|
1608
1633
|
sawTypedLimit,
|
|
1609
1634
|
sawRetryable,
|
|
1610
1635
|
answer,
|
|
1611
1636
|
rawDeliverableEmpty: deliverableEmpty,
|
|
1612
1637
|
workspaceDiffNonEmpty: !workspaceUnchanged,
|
|
1613
|
-
currentDiff,
|
|
1614
|
-
markers: telemetry.outputMarkers,
|
|
1638
|
+
...(currentDiff === undefined ? {} : { currentDiff }),
|
|
1615
1639
|
lastLimit: telemetry.rateLimits.at(-1) ?? null,
|
|
1616
1640
|
refusal,
|
|
1617
1641
|
telemetry,
|
|
@@ -1635,8 +1659,13 @@ export class Orchestrator {
|
|
|
1635
1659
|
signal?.removeEventListener("abort", onAbort);
|
|
1636
1660
|
}
|
|
1637
1661
|
// A pool-exhausted terminal is rotation's verdict, not the transient
|
|
1638
|
-
// machinery's
|
|
1639
|
-
|
|
1662
|
+
// machinery's, and a stopped run exhausted no retries: no
|
|
1663
|
+
// `route.transient.exhausted` rides along with either.
|
|
1664
|
+
if (harnessErrored &&
|
|
1665
|
+
!poolExhausted &&
|
|
1666
|
+
!processingRefusal &&
|
|
1667
|
+
!telemetry.requestRefusal &&
|
|
1668
|
+
!signal?.aborted) {
|
|
1640
1669
|
emitTransientExhausted((t, p) => log?.emit(t, p), adapter.id, attemptId, telemetry, retryPolicy.maxRetries);
|
|
1641
1670
|
}
|
|
1642
1671
|
const attemptStreamEndedMs = Date.now();
|
|
@@ -1656,9 +1685,10 @@ export class Orchestrator {
|
|
|
1656
1685
|
: await secretDiff.captureCandidateWorkspace({
|
|
1657
1686
|
...{ wsm, envelope, inPlace: inPlaceEnvelope, projectRoot: store.repoRoot },
|
|
1658
1687
|
...{ answerText, answerMatches: answer.secretLikeMatches() },
|
|
1688
|
+
holder: { runId: paths.runId, runDir: paths.root },
|
|
1689
|
+
incompleteCapture: currentCapture?.captureIncomplete ? currentCapture : undefined,
|
|
1659
1690
|
});
|
|
1660
1691
|
const { diff, captureRefusal } = captured;
|
|
1661
|
-
harnessErrored = secretDiff.recordCaptureRefusal(captureRefusal, errors, harnessErrored);
|
|
1662
1692
|
const deliverableEvidence = diff.trim().length > 0 || Boolean(answerText) || directoryHasOutput(directoryCapture.files);
|
|
1663
1693
|
// Cancelled attempts skip gates: running a 600s-per-gate suite delays the ack
|
|
1664
1694
|
// and burns compute on a result nobody will adopt. Diff/attempt.yaml
|
|
@@ -1717,7 +1747,9 @@ export class Orchestrator {
|
|
|
1717
1747
|
if (finalized.outcomeClass === "contract_failure" && unwrapped.contractViolation)
|
|
1718
1748
|
errors.push(`work_report contract: ${unwrapped.contractViolation}`);
|
|
1719
1749
|
const deliverablePresent = finalized.deliverablePresent;
|
|
1720
|
-
|
|
1750
|
+
if (captureRefusal)
|
|
1751
|
+
errors.push(captureRefusal.detail);
|
|
1752
|
+
const errored = harnessErrored || webBlocked || captureRefusal !== undefined;
|
|
1721
1753
|
const resumable = await continuity.finish({
|
|
1722
1754
|
runSpec: spec,
|
|
1723
1755
|
errored,
|
|
@@ -1727,6 +1759,7 @@ export class Orchestrator {
|
|
|
1727
1759
|
workState: finalized.workState,
|
|
1728
1760
|
});
|
|
1729
1761
|
setAttemptOutcome(telemetry, {
|
|
1762
|
+
captureFailed: captureRefusal !== undefined,
|
|
1730
1763
|
deliverablePresent,
|
|
1731
1764
|
gatesPassed: gates.length > 0 ? gatesPassed(gates) : null,
|
|
1732
1765
|
harnessErrored,
|
|
@@ -1840,9 +1873,11 @@ export class Orchestrator {
|
|
|
1840
1873
|
probeReadyProfiles: (spec, tried) => runInput
|
|
1841
1874
|
? this.credentials.readyProfileIdsForRotation(runInput, adapter.id, spec.credential_profile ?? null, tried, spec.model_hint ?? null)
|
|
1842
1875
|
: Promise.resolve(new Set()),
|
|
1876
|
+
bindRefusal: (spec, producer) => this.credentials.bindPreProgressRefusal(adapter.id, spec, producer),
|
|
1843
1877
|
rotationObservations: (spec, transients, refusal) => this.credentials.rotationObservations(adapter, spec, transients, refusal),
|
|
1844
1878
|
emit: (type, payload) => log?.emit(type, payload),
|
|
1845
1879
|
route: routed,
|
|
1880
|
+
poolEvidence: lane.poolEvidence,
|
|
1846
1881
|
newSessionId: () => newId("ses"),
|
|
1847
1882
|
thread: runInput?.threadId
|
|
1848
1883
|
? {
|
|
@@ -1864,7 +1899,7 @@ export class Orchestrator {
|
|
|
1864
1899
|
const mutatingRun = isMutatingAccess(contract.access.effective_profile);
|
|
1865
1900
|
const planBrief = verifiedPlanBrief(input);
|
|
1866
1901
|
const quotaSnapshots = this.quotaSnapshotPreflight();
|
|
1867
|
-
const { store, paths, log, ledger } = beginAnnouncedRun({
|
|
1902
|
+
const { store, paths, log, ledger, runContext } = beginAnnouncedRun({
|
|
1868
1903
|
input,
|
|
1869
1904
|
contract,
|
|
1870
1905
|
quotaSnapshots,
|
|
@@ -2020,6 +2055,7 @@ export class Orchestrator {
|
|
|
2020
2055
|
});
|
|
2021
2056
|
}
|
|
2022
2057
|
const runsBySlot = new Array(slots.length);
|
|
2058
|
+
runContext.attempts = () => runsBySlot.filter((run) => run !== undefined);
|
|
2023
2059
|
// D-16d: one-shot continuation budget shared across the concurrent candidate
|
|
2024
2060
|
// slots (parity with the read-only chain's single counter). Claimed
|
|
2025
2061
|
// synchronously (check-then-increment with no await between), so two slots
|
|
@@ -2072,8 +2108,9 @@ export class Orchestrator {
|
|
|
2072
2108
|
const effectiveWeb = this.discloseWebUpgrade(log, slot.routed, knobs.webPolicy, slot.attemptId);
|
|
2073
2109
|
let envelope;
|
|
2074
2110
|
// Declared OUTSIDE the try so the catch below can state what the attempt
|
|
2075
|
-
// actually ran under, not merely that it stopped.
|
|
2111
|
+
// actually ran under, not merely that it stopped — and on which accounts.
|
|
2076
2112
|
let harnessHome;
|
|
2113
|
+
const tried = new PoolAttemptEvidence(adapter.id, slot.attemptId);
|
|
2077
2114
|
try {
|
|
2078
2115
|
log.emit("harness.started", {
|
|
2079
2116
|
harness_id: adapter.id,
|
|
@@ -2110,7 +2147,7 @@ export class Orchestrator {
|
|
|
2110
2147
|
budgetStopped = true;
|
|
2111
2148
|
return true;
|
|
2112
2149
|
}, input, requestedSingleCandidate, // W-C4 deltas: single-candidate chat lane only (racing = noise x N)
|
|
2113
|
-
undefined, undefined, harnessHome);
|
|
2150
|
+
undefined, undefined, harnessHome, undefined, tried);
|
|
2114
2151
|
ledger.settle(slot.lease.lease_id, attemptUsageCostSettlement(run.cost, run.costEstimated, run.attemptId, run.harnessId, run.telemetry.authMode, run.telemetry.usageCost));
|
|
2115
2152
|
log.emit("harness.completed", {
|
|
2116
2153
|
harness_id: adapter.id,
|
|
@@ -2175,6 +2212,7 @@ export class Orchestrator {
|
|
|
2175
2212
|
continuation_of: run.attemptId,
|
|
2176
2213
|
});
|
|
2177
2214
|
const contLeaseId = contLease.lease?.lease_id ?? "";
|
|
2215
|
+
const contTried = new PoolAttemptEvidence(adapter.id, contAttemptId);
|
|
2178
2216
|
try {
|
|
2179
2217
|
const contRun = await this.runCandidateInEnvelope(slot.routed, envelope, contAttemptId, slot.label, contract, input.prompt, store, paths, wsm, ledger, {
|
|
2180
2218
|
lease: contLease.lease,
|
|
@@ -2188,7 +2226,7 @@ export class Orchestrator {
|
|
|
2188
2226
|
return false;
|
|
2189
2227
|
budgetStopped = true;
|
|
2190
2228
|
return true;
|
|
2191
|
-
}, input, requestedSingleCandidate, undefined, packet.pointerLine ?? undefined, harnessHome);
|
|
2229
|
+
}, input, requestedSingleCandidate, undefined, packet.pointerLine ?? undefined, harnessHome, undefined, contTried);
|
|
2192
2230
|
ledger.settle(contLeaseId, attemptUsageCostSettlement(contRun.cost, contRun.costEstimated, contRun.attemptId, contRun.harnessId, contRun.telemetry.authMode, contRun.telemetry.usageCost));
|
|
2193
2231
|
log.emit("harness.completed", {
|
|
2194
2232
|
harness_id: adapter.id,
|
|
@@ -2215,6 +2253,8 @@ export class Orchestrator {
|
|
|
2215
2253
|
error: safeErrorMessage(err),
|
|
2216
2254
|
});
|
|
2217
2255
|
}
|
|
2256
|
+
// Whichever attempt speaks names both attempts' failed tries, in order.
|
|
2257
|
+
effectiveRun.poolAttempts = [...(run.poolAttempts ?? []), ...contTried.snapshot()];
|
|
2218
2258
|
}
|
|
2219
2259
|
else {
|
|
2220
2260
|
ledger.cancel(contLease.lease?.lease_id ?? "");
|
|
@@ -2260,6 +2300,7 @@ export class Orchestrator {
|
|
|
2260
2300
|
slot.routed.delegationRequirement,
|
|
2261
2301
|
], knobs.model),
|
|
2262
2302
|
infraPhase,
|
|
2303
|
+
poolAttempts: tried.snapshot(),
|
|
2263
2304
|
// Keep a TYPED pre-spawn refusal (spent quota window + its reset)
|
|
2264
2305
|
// alive past this catch; `message` alone would force the terminal to
|
|
2265
2306
|
// read prose back out.
|
|
@@ -2274,10 +2315,17 @@ export class Orchestrator {
|
|
|
2274
2315
|
};
|
|
2275
2316
|
await runParallelCandidates(slots, this.deps.runtimeConcurrencyCaps, runSlot);
|
|
2276
2317
|
const runs = runsBySlot.filter((r) => r !== undefined);
|
|
2318
|
+
// From here `runs` is the lane's one attempt collection (a synthesis that
|
|
2319
|
+
// returns joins it); a synthesis that threw first leaves only its tries.
|
|
2320
|
+
let thrownSynthesis = null;
|
|
2321
|
+
const history = () => thrownSynthesis ? [...runs, thrownSynthesis] : runs;
|
|
2322
|
+
runContext.attempts = history;
|
|
2277
2323
|
// Fail-closed terminal: a delegated mutating run whose attempts state
|
|
2278
2324
|
// neither historical proof nor deliberate absence refuses instead of passing.
|
|
2279
2325
|
assertDelegatedEvidence(input.delegated === true, candidateAccess, runs);
|
|
2280
|
-
|
|
2326
|
+
// One candidate-terminal context for every race terminal below; its
|
|
2327
|
+
// telemetry writer reads the live roster, so a later synth attempt counts.
|
|
2328
|
+
const raceTerminal = {
|
|
2281
2329
|
store,
|
|
2282
2330
|
paths,
|
|
2283
2331
|
log,
|
|
@@ -2286,6 +2334,11 @@ export class Orchestrator {
|
|
|
2286
2334
|
mode,
|
|
2287
2335
|
ledger,
|
|
2288
2336
|
runs,
|
|
2337
|
+
writeTelemetry: () => this.writeRunTelemetry(store, paths, contract, runId, taskId, mode, candidateRoster(runs), null),
|
|
2338
|
+
};
|
|
2339
|
+
const cancelledRaceResult = () => cancelledCandidatesResult({
|
|
2340
|
+
...raceTerminal,
|
|
2341
|
+
attempts: history(),
|
|
2289
2342
|
git: input.workspaceKind !== "directory"
|
|
2290
2343
|
? {
|
|
2291
2344
|
live: input.inPlace === true &&
|
|
@@ -2297,7 +2350,6 @@ export class Orchestrator {
|
|
|
2297
2350
|
}
|
|
2298
2351
|
: undefined,
|
|
2299
2352
|
signal: input.signal,
|
|
2300
|
-
writeTelemetry: () => this.writeRunTelemetry(store, paths, contract, runId, taskId, mode, candidateRoster(runs), null),
|
|
2301
2353
|
});
|
|
2302
2354
|
// Revert divergence fence for the single-candidate in-place path: the
|
|
2303
2355
|
// candidate mutated the LIVE tree during execution above, so the post-turn
|
|
@@ -2324,31 +2376,34 @@ export class Orchestrator {
|
|
|
2324
2376
|
}
|
|
2325
2377
|
const failedDelegation = delegateFailure.dominantRaceCandidateFailure(runs);
|
|
2326
2378
|
if (failedDelegation) {
|
|
2327
|
-
const failure = delegateFailure.candidateFailureTerminal(failedDelegation, "race");
|
|
2328
2379
|
await disposeReviewEnvelopes();
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2380
|
+
const failedOutput = {
|
|
2381
|
+
...{ store, log, paths, execRoot, preTurnSha, taskId, mode, run: failedDelegation },
|
|
2382
|
+
live: input.inPlace === true &&
|
|
2383
|
+
input.workspaceKind !== "directory" &&
|
|
2384
|
+
failedDelegation.reviewCwd === execRoot,
|
|
2385
|
+
postTurnSha: earlyPostTurnSha,
|
|
2386
|
+
kind: input.create === true ? "new_repo" : "patch",
|
|
2387
|
+
};
|
|
2388
|
+
// A live candidate whose capture failed (no Delegate failure) ends in the
|
|
2389
|
+
// shared no-working-candidate terminal, as in the isolated race and
|
|
2390
|
+
// convergence: a recorded budget denial, a budget terminal the ledger
|
|
2391
|
+
// settled (stream hard cap, exact overshoot, unverifiable cash) or a
|
|
2392
|
+
// unanimous typed refusal leads, its live receipt carries the same facts,
|
|
2393
|
+
// and the capture facts and inspection actions follow. Any other dominant
|
|
2394
|
+
// capture failure joins it only under a settled budget terminal.
|
|
2395
|
+
if (delegateFailure.candidateFailureKind(failedDelegation) === "capture" &&
|
|
2396
|
+
(failedOutput.live || ledger.terminal() !== null))
|
|
2397
|
+
return failedCandidatesResult({
|
|
2398
|
+
...raceTerminal,
|
|
2399
|
+
budgetDenial,
|
|
2400
|
+
...(mutatingRun ? { liveCapture: failedOutput } : {}),
|
|
2348
2401
|
});
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2402
|
+
const failure = delegateFailure.candidateFailureTerminal(failedDelegation, "race");
|
|
2403
|
+
if (mutatingRun)
|
|
2404
|
+
await delegateFailure.persistFailedCandidateOutput(failedOutput);
|
|
2405
|
+
raceTerminal.writeTelemetry();
|
|
2406
|
+
return failTerminally(log, store, paths, runId, taskId, mode, failure.phase, failure.error, ledger.spend(), { ...failure.metadata, attempts: runs });
|
|
2352
2407
|
}
|
|
2353
2408
|
if (runs.length === 0)
|
|
2354
2409
|
return emptyCandidateResult({
|
|
@@ -2370,18 +2425,7 @@ export class Orchestrator {
|
|
|
2370
2425
|
const workingRuns = partitionCandidates(runs).working;
|
|
2371
2426
|
if (workingRuns.length === 0) {
|
|
2372
2427
|
await disposeReviewEnvelopes();
|
|
2373
|
-
return failedCandidatesResult({
|
|
2374
|
-
store,
|
|
2375
|
-
paths,
|
|
2376
|
-
log,
|
|
2377
|
-
runId,
|
|
2378
|
-
taskId,
|
|
2379
|
-
mode,
|
|
2380
|
-
ledger,
|
|
2381
|
-
runs,
|
|
2382
|
-
budgetDenial,
|
|
2383
|
-
writeTelemetry: () => this.writeRunTelemetry(store, paths, contract, runId, taskId, mode, candidateRoster(runs), null),
|
|
2384
|
-
});
|
|
2428
|
+
return failedCandidatesResult({ ...raceTerminal, budgetDenial });
|
|
2385
2429
|
}
|
|
2386
2430
|
// QA-025: only announce that review STARTED when the panel will actually
|
|
2387
2431
|
// run. A candidate that changed no files is skipped inside reviewRuns; a
|
|
@@ -2423,7 +2467,7 @@ export class Orchestrator {
|
|
|
2423
2467
|
catch (err) {
|
|
2424
2468
|
// Review preflight/evidence failures end TERMINALLY with artifacts —
|
|
2425
2469
|
// never as an escaped throw that orphans the run dir.
|
|
2426
|
-
return failAfterCandidates(
|
|
2470
|
+
return failAfterCandidates(raceTerminal, "review", err);
|
|
2427
2471
|
}
|
|
2428
2472
|
finally {
|
|
2429
2473
|
// Review preflight failures must not leak candidate worktrees.
|
|
@@ -2453,6 +2497,7 @@ export class Orchestrator {
|
|
|
2453
2497
|
});
|
|
2454
2498
|
if (lease.granted) {
|
|
2455
2499
|
let envelope;
|
|
2500
|
+
const synthTried = new PoolAttemptEvidence(synthRouted.adapter.id, "synth");
|
|
2456
2501
|
try {
|
|
2457
2502
|
const plan = buildSynthesisPlan(evidences);
|
|
2458
2503
|
const synthesisInput = buildFileBackedSynthesisInput({
|
|
@@ -2481,7 +2526,7 @@ export class Orchestrator {
|
|
|
2481
2526
|
budgetStopped = true;
|
|
2482
2527
|
budgetDenial ??= denial;
|
|
2483
2528
|
},
|
|
2484
|
-
}, candidateAccess, input.onHarnessEvent, input.signal, undefined, input.effort, "synthesize", log, effectiveWeb, interactionChannelFor(input, log, runId, taskId, "synth", synthAdapter.id, synthRouted.supportsInteractive), undefined, input, false, synthesisInput.content, undefined, synthHome);
|
|
2529
|
+
}, candidateAccess, input.onHarnessEvent, input.signal, undefined, input.effort, "synthesize", log, effectiveWeb, interactionChannelFor(input, log, runId, taskId, "synth", synthAdapter.id, synthRouted.supportsInteractive), undefined, input, false, synthesisInput.content, undefined, synthHome, undefined, synthTried);
|
|
2485
2530
|
ledger.settle(lease.lease?.lease_id ?? "", attemptUsageCostSettlement(run.cost, run.costEstimated, run.attemptId, run.harnessId, run.telemetry.authMode, run.telemetry.usageCost));
|
|
2486
2531
|
reviewEnvelopes.push(envelope);
|
|
2487
2532
|
envelope = undefined;
|
|
@@ -2495,7 +2540,7 @@ export class Orchestrator {
|
|
|
2495
2540
|
workingRuns.push(run);
|
|
2496
2541
|
}
|
|
2497
2542
|
if (input.signal?.aborted) {
|
|
2498
|
-
return cancelledResult(log, runId, taskId, mode, paths.root, candidateStatuses(runs),
|
|
2543
|
+
return cancelledResult(log, runId, taskId, mode, paths.root, candidateStatuses(runs), raceTerminal.writeTelemetry, ledger.spend(), input.signal, store, undefined, undefined, history());
|
|
2499
2544
|
}
|
|
2500
2545
|
}
|
|
2501
2546
|
finally {
|
|
@@ -2503,6 +2548,9 @@ export class Orchestrator {
|
|
|
2503
2548
|
}
|
|
2504
2549
|
}
|
|
2505
2550
|
catch (err) {
|
|
2551
|
+
// Not yet in `runs`: the run goes on, so its tries' accounts stay in the history.
|
|
2552
|
+
if (!runs.some((run) => run.attemptId === "synth"))
|
|
2553
|
+
thrownSynthesis = { poolAttempts: synthTried.snapshot() };
|
|
2506
2554
|
ledger.settle(lease.lease?.lease_id ?? "", AC.attemptFailureCost(err, "synthesis-error").settlement);
|
|
2507
2555
|
log.emit("harness.completed", {
|
|
2508
2556
|
attempt_id: "synth",
|
|
@@ -2528,7 +2576,7 @@ export class Orchestrator {
|
|
|
2528
2576
|
}
|
|
2529
2577
|
catch (err) {
|
|
2530
2578
|
// Arbitration throws end terminally with artifacts, never as an orphan.
|
|
2531
|
-
return failAfterCandidates({
|
|
2579
|
+
return failAfterCandidates({ ...raceTerminal, attempts: history() }, "arbitration", err);
|
|
2532
2580
|
}
|
|
2533
2581
|
log.emit("arbitration.completed", {
|
|
2534
2582
|
winner: result.decision.winner,
|
|
@@ -2608,6 +2656,9 @@ export class Orchestrator {
|
|
|
2608
2656
|
});
|
|
2609
2657
|
store.writeYaml(join(paths.arbitrationDir, "pairwise.yaml"), result.pairwise);
|
|
2610
2658
|
const decisionPath = join(paths.arbitrationDir, "decision.yaml");
|
|
2659
|
+
// Every attempt's failed tries by the FINAL outcome (delivery below can
|
|
2660
|
+
// still fail the checks); a review or check block is not an account failure.
|
|
2661
|
+
const racePool = () => terminalPoolAttempts(facts, history());
|
|
2611
2662
|
if (winnerRun) {
|
|
2612
2663
|
for (const path of materializeWinnerOutputs({
|
|
2613
2664
|
attemptDir: join(paths.attemptsDir, winnerRun.attemptId),
|
|
@@ -2786,7 +2837,7 @@ export class Orchestrator {
|
|
|
2786
2837
|
},
|
|
2787
2838
|
});
|
|
2788
2839
|
}
|
|
2789
|
-
store.writeText(join(paths.finalDir, "summary.md"), renderSummary(runId, mode, { ...result.decision, facts }, evidences, synth.reason, actualReviewVerified, secretDiff.secretLikeSummaryLine(winnerRun.secretLike)));
|
|
2840
|
+
store.writeText(join(paths.finalDir, "summary.md"), withPoolAttempts(renderSummary(runId, mode, { ...result.decision, facts }, evidences, synth.reason, actualReviewVerified, secretDiff.secretLikeSummaryLine(winnerRun.secretLike)), racePool()));
|
|
2790
2841
|
// summary.md is a DIAGNOSTIC artifact only (V8/PLAN addendum 2): it no
|
|
2791
2842
|
// longer carries primary-output authority. A clean applyable success or a
|
|
2792
2843
|
// winner answer still marks it ready for legacy INV-116 ordering; any
|
|
@@ -2812,8 +2863,14 @@ export class Orchestrator {
|
|
|
2812
2863
|
// (applyable, no_changes, or not-verified) is an honest completion.
|
|
2813
2864
|
const needsDecisionTerminal = facts.review === "blocked" || facts.checks === "failed";
|
|
2814
2865
|
const isFailureTerminal = facts.lifecycle !== "succeeded" || needsDecisionTerminal;
|
|
2866
|
+
const pool = racePool();
|
|
2815
2867
|
if (deliveryFailureReason && !finalVerifyFailed) {
|
|
2816
|
-
|
|
2868
|
+
const refused = deliveryRefusalFailure(deliveryFailureReason, paths.root);
|
|
2869
|
+
writeFailure(store, paths, {
|
|
2870
|
+
...refused,
|
|
2871
|
+
safeMessage: withPoolAttempts(refused.safeMessage, pool),
|
|
2872
|
+
poolAttempts: pool,
|
|
2873
|
+
});
|
|
2817
2874
|
}
|
|
2818
2875
|
else if (finalVerifyFailed) {
|
|
2819
2876
|
writeFailure(store, paths, {
|
|
@@ -2821,9 +2878,10 @@ export class Orchestrator {
|
|
|
2821
2878
|
// RunFailure.category is a closed enum; "validation" is the honest
|
|
2822
2879
|
// bucket (the winner failed re-validation on a fresh base).
|
|
2823
2880
|
category: "validation",
|
|
2824
|
-
safeMessage: finalVerify?.applied_cleanly === null
|
|
2881
|
+
safeMessage: withPoolAttempts(finalVerify?.applied_cleanly === null
|
|
2825
2882
|
? `final verify ERRORED before proving the patch against a clean base: ${finalVerify?.reason ?? "verify infrastructure error"}`
|
|
2826
|
-
: `final verify failed: ${finalVerify?.reason ?? (finalVerify?.gates_passed === false ? "deterministic gates failed on the fresh verify tree" : "unknown")}`,
|
|
2883
|
+
: `final verify failed: ${finalVerify?.reason ?? (finalVerify?.gates_passed === false ? "deterministic gates failed on the fresh verify tree" : "unknown")}`, pool),
|
|
2884
|
+
poolAttempts: pool,
|
|
2827
2885
|
runDir: paths.root,
|
|
2828
2886
|
nextActions: finalVerify?.applied_cleanly === null
|
|
2829
2887
|
? [
|
|
@@ -2855,11 +2913,12 @@ export class Orchestrator {
|
|
|
2855
2913
|
: "internal",
|
|
2856
2914
|
harnessId: winnerRun?.errored ? winnerRun.harnessId : undefined,
|
|
2857
2915
|
attemptId: winnerRun?.errored ? winnerRun.attemptId : undefined,
|
|
2858
|
-
safeMessage: needsHuman
|
|
2916
|
+
safeMessage: withPoolAttempts(needsHuman
|
|
2859
2917
|
? `review escalated to a human decision: ${result.decision.why_winner}`
|
|
2860
2918
|
: needsDecisionTerminal
|
|
2861
2919
|
? `review blocked before apply: ${result.decision.why_winner}`
|
|
2862
|
-
: result.decision.why_winner,
|
|
2920
|
+
: result.decision.why_winner, pool),
|
|
2921
|
+
poolAttempts: pool,
|
|
2863
2922
|
rawDetailRef: winnerRun?.errored
|
|
2864
2923
|
? `attempts/${winnerRun.attemptId}/attempt.yaml`
|
|
2865
2924
|
: undefined,
|
|
@@ -2879,7 +2938,7 @@ export class Orchestrator {
|
|
|
2879
2938
|
],
|
|
2880
2939
|
});
|
|
2881
2940
|
if (!winnerRun) {
|
|
2882
|
-
store.writeText(join(paths.finalDir, "summary.md"), `# Run ${runId} (${mode})\n\n- Lifecycle: ${facts.lifecycle}${facts.reason ? ` (${facts.reason})` : ""}\n- Phase: arbitration\n\n${result.decision.why_winner}\n`);
|
|
2941
|
+
store.writeText(join(paths.finalDir, "summary.md"), `# Run ${runId} (${mode})\n\n- Lifecycle: ${facts.lifecycle}${facts.reason ? ` (${facts.reason})` : ""}\n- Phase: arbitration\n\n${withPoolAttempts(result.decision.why_winner, pool)}\n`);
|
|
2883
2942
|
log.emit("output.ready", {
|
|
2884
2943
|
kind: "summary",
|
|
2885
2944
|
path: "final/summary.md",
|
|
@@ -2895,6 +2954,8 @@ export class Orchestrator {
|
|
|
2895
2954
|
lifecycle: facts.lifecycle,
|
|
2896
2955
|
facts,
|
|
2897
2956
|
reason: facts.reason,
|
|
2957
|
+
// A work-state veto is still a block: it keeps the earlier refusals.
|
|
2958
|
+
...(pool.length > 0 ? { pool_attempts: pool } : {}),
|
|
2898
2959
|
...resumableTerminal(store, paths, winnerRun ? [winnerRun] : runs, isFailureTerminal),
|
|
2899
2960
|
});
|
|
2900
2961
|
}
|
|
@@ -2904,6 +2965,7 @@ export class Orchestrator {
|
|
|
2904
2965
|
log.emit("run.blocked", {
|
|
2905
2966
|
lifecycle: facts.lifecycle,
|
|
2906
2967
|
facts,
|
|
2968
|
+
pool_attempts: pool,
|
|
2907
2969
|
...resumableTerminal(store, paths, runs, isFailureTerminal),
|
|
2908
2970
|
phase: deliveryFailureReason && !finalVerifyFailed
|
|
2909
2971
|
? "delivery"
|
|
@@ -2919,6 +2981,7 @@ export class Orchestrator {
|
|
|
2919
2981
|
facts,
|
|
2920
2982
|
reason: facts.reason,
|
|
2921
2983
|
phase: "arbitration",
|
|
2984
|
+
pool_attempts: pool,
|
|
2922
2985
|
failure_ref: "final/failure.yaml",
|
|
2923
2986
|
...resumableTerminal(store, paths, runs, isFailureTerminal),
|
|
2924
2987
|
});
|
|
@@ -2931,7 +2994,7 @@ export class Orchestrator {
|
|
|
2931
2994
|
facts,
|
|
2932
2995
|
winner: result.decision.winner,
|
|
2933
2996
|
runDir: paths.root,
|
|
2934
|
-
summary: result.decision.why_winner,
|
|
2997
|
+
summary: withPoolAttempts(result.decision.why_winner, pool),
|
|
2935
2998
|
candidates: candidateStatuses(runs),
|
|
2936
2999
|
decisionPath,
|
|
2937
3000
|
reviewVerified: actualReviewVerified,
|
|
@@ -3009,7 +3072,7 @@ export class Orchestrator {
|
|
|
3009
3072
|
const contract = this.buildContract(input, taskId, mode);
|
|
3010
3073
|
const planBrief = verifiedPlanBrief(input);
|
|
3011
3074
|
const quotaSnapshots = this.quotaSnapshotPreflight();
|
|
3012
|
-
const { store, paths, log, ledger } = beginAnnouncedRun({
|
|
3075
|
+
const { store, paths, log, ledger, runContext } = beginAnnouncedRun({
|
|
3013
3076
|
input,
|
|
3014
3077
|
contract,
|
|
3015
3078
|
quotaSnapshots,
|
|
@@ -3146,6 +3209,8 @@ export class Orchestrator {
|
|
|
3146
3209
|
let interrupted = false; // D-16 r8: terminalizes the run interrupted
|
|
3147
3210
|
let lastFindings = [];
|
|
3148
3211
|
let lastRun = null;
|
|
3212
|
+
const convergenceRuns = [];
|
|
3213
|
+
runContext.attempts = () => convergenceRuns;
|
|
3149
3214
|
let actualReviewVerified = false;
|
|
3150
3215
|
let lastFinalReviewClean = false;
|
|
3151
3216
|
// Honest apply-state for in-place convergence: the attempts mutate the LIVE
|
|
@@ -3235,6 +3300,7 @@ export class Orchestrator {
|
|
|
3235
3300
|
const effectiveWeb = this.discloseWebUpgrade(log, routed, knobs.webPolicy, attemptId);
|
|
3236
3301
|
let run;
|
|
3237
3302
|
let harnessHome;
|
|
3303
|
+
const tried = new PoolAttemptEvidence(adapter.id, attemptId);
|
|
3238
3304
|
try {
|
|
3239
3305
|
harnessHome = this.harnessHomeFor(wsm, envelope, routed, input);
|
|
3240
3306
|
log.emit("harness.started", {
|
|
@@ -3253,7 +3319,7 @@ export class Orchestrator {
|
|
|
3253
3319
|
ledger.updateHold(lease.lease?.lease_id ?? "", streamedUsd);
|
|
3254
3320
|
return ledger.tier() === "hard";
|
|
3255
3321
|
}, input, true, // convergence runs one candidate: live deltas on (W-C4)
|
|
3256
|
-
undefined, undefined, harnessHome, observedPaths);
|
|
3322
|
+
undefined, undefined, harnessHome, observedPaths, tried);
|
|
3257
3323
|
ledger.settle(lease.lease?.lease_id ?? "", attemptUsageCostSettlement(run.cost, run.costEstimated, run.attemptId, run.harnessId, run.telemetry.authMode, run.telemetry.usageCost));
|
|
3258
3324
|
log.emit("harness.completed", {
|
|
3259
3325
|
harness_id: adapter.id,
|
|
@@ -3290,6 +3356,7 @@ export class Orchestrator {
|
|
|
3290
3356
|
errored: true,
|
|
3291
3357
|
costEstimated: failureCost.estimated,
|
|
3292
3358
|
errors: [message],
|
|
3359
|
+
poolAttempts: tried.snapshot(),
|
|
3293
3360
|
...(declared.code ? { declaredFailure: declared } : {}),
|
|
3294
3361
|
telemetry: createAttemptTelemetry(knobs.webPolicy, contract.external_context.web_required, effectiveWeb, [routed.browserRequirement, routed.denyRequirement, routed.delegationRequirement], knobs.model),
|
|
3295
3362
|
};
|
|
@@ -3305,6 +3372,7 @@ export class Orchestrator {
|
|
|
3305
3372
|
}
|
|
3306
3373
|
}
|
|
3307
3374
|
lastRun = run;
|
|
3375
|
+
convergenceRuns.push(run);
|
|
3308
3376
|
// Fail-closed twin of the candidate lane's gate: this loop terminalizes
|
|
3309
3377
|
// per attempt, so the proof is spent per attempt.
|
|
3310
3378
|
assertDelegatedEvidence(input.delegated === true, convergenceAccess, [run]);
|
|
@@ -3324,34 +3392,28 @@ export class Orchestrator {
|
|
|
3324
3392
|
// Cancellation/deadline keeps priority over a belt failure finalized concurrently.
|
|
3325
3393
|
if (input.signal?.aborted || processingBudgetDenial)
|
|
3326
3394
|
break;
|
|
3395
|
+
// When this attempt's capture failed (and no Delegate failure leads), a
|
|
3396
|
+
// budget terminal the ledger settled on it (stream hard cap, exact overshoot,
|
|
3397
|
+
// unverifiable cash) outranks that capture failure and its typed refusal
|
|
3398
|
+
// (race parity): the budget-aware finalizer below names the budget cause and
|
|
3399
|
+
// remedy first, with the capture facts, source and inspection actions after.
|
|
3400
|
+
if (delegateFailure.candidateFailureKind(run) === "capture" && ledger.terminal()) {
|
|
3401
|
+
exhausted = true;
|
|
3402
|
+
break;
|
|
3403
|
+
}
|
|
3327
3404
|
if (run.telemetry.requestRefusal ||
|
|
3328
3405
|
run.telemetry.effortResolution?.resolution === "rejected")
|
|
3329
3406
|
break;
|
|
3330
3407
|
if (delegateFailure.candidateFailureKind(run)) {
|
|
3331
3408
|
const failure = delegateFailure.candidateFailureTerminal(run, "convergence");
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
attemptId,
|
|
3339
|
-
harnessId: run.harnessId,
|
|
3340
|
-
facts: makeOutcomeFacts("failed", { noChanges: run.files.noChanges }),
|
|
3341
|
-
log,
|
|
3342
|
-
});
|
|
3343
|
-
}
|
|
3344
|
-
else if (input.workspaceKind !== "directory") {
|
|
3345
|
-
await delegateFailure.persistFailedInPlaceWorkProduct({
|
|
3346
|
-
...{ store, log, paths, execRoot, preTurnSha, taskId, mode },
|
|
3347
|
-
live: input.inPlace === true,
|
|
3348
|
-
run,
|
|
3349
|
-
kind: input.create === true ? "new_repo" : "patch",
|
|
3350
|
-
attempts: attempt,
|
|
3351
|
-
});
|
|
3352
|
-
}
|
|
3409
|
+
await delegateFailure.persistFailedCandidateOutput({
|
|
3410
|
+
...{ store, log, paths, execRoot, preTurnSha, taskId, mode, run },
|
|
3411
|
+
live: input.inPlace === true && input.workspaceKind !== "directory",
|
|
3412
|
+
kind: input.create === true ? "new_repo" : "patch",
|
|
3413
|
+
attempts: attempt,
|
|
3414
|
+
});
|
|
3353
3415
|
this.writeRunTelemetry(store, paths, contract, runId, taskId, mode, attemptTelemetries, null);
|
|
3354
|
-
return failTerminally(log, store, paths, runId, taskId, mode, failure.phase, failure.error, ledger.spend(), failure.metadata);
|
|
3416
|
+
return failTerminally(log, store, paths, runId, taskId, mode, failure.phase, failure.error, ledger.spend(), { ...failure.metadata, attempts: convergenceRuns });
|
|
3355
3417
|
}
|
|
3356
3418
|
if (input.workspaceKind === "directory" && run.captureRefusal) {
|
|
3357
3419
|
return failedCandidatesResult({
|
|
@@ -3363,6 +3425,7 @@ export class Orchestrator {
|
|
|
3363
3425
|
runId,
|
|
3364
3426
|
taskId,
|
|
3365
3427
|
runs: [run],
|
|
3428
|
+
attempts: convergenceRuns,
|
|
3366
3429
|
writeTelemetry: () => this.writeRunTelemetry(store, paths, contract, runId, taskId, mode, attemptTelemetries, null),
|
|
3367
3430
|
});
|
|
3368
3431
|
}
|
|
@@ -3422,7 +3485,17 @@ export class Orchestrator {
|
|
|
3422
3485
|
facts: makeOutcomeFacts("failed", { noChanges: run.files.noChanges }),
|
|
3423
3486
|
log,
|
|
3424
3487
|
});
|
|
3425
|
-
return failAfterCandidates({
|
|
3488
|
+
return failAfterCandidates({
|
|
3489
|
+
log,
|
|
3490
|
+
store,
|
|
3491
|
+
paths,
|
|
3492
|
+
runId,
|
|
3493
|
+
taskId,
|
|
3494
|
+
mode,
|
|
3495
|
+
ledger,
|
|
3496
|
+
runs: [run],
|
|
3497
|
+
attempts: convergenceRuns,
|
|
3498
|
+
}, "review", err);
|
|
3426
3499
|
}
|
|
3427
3500
|
if (conv.converged) {
|
|
3428
3501
|
converged = true;
|
|
@@ -3541,11 +3614,12 @@ export class Orchestrator {
|
|
|
3541
3614
|
facts = decision.facts;
|
|
3542
3615
|
}
|
|
3543
3616
|
}
|
|
3544
|
-
|
|
3617
|
+
// Captured files decide noChanges; an incomplete capture leaves it unknown.
|
|
3618
|
+
if (lastRun?.files || lastRun?.captureRefusal)
|
|
3545
3619
|
facts = {
|
|
3546
3620
|
...facts,
|
|
3547
|
-
noChanges: lastRun.files
|
|
3548
|
-
reason: facts.reason === "no_changes" && lastRun.files
|
|
3621
|
+
noChanges: lastRun.files?.noChanges ?? null,
|
|
3622
|
+
reason: facts.reason === "no_changes" && lastRun.files?.noChanges !== true ? null : facts.reason,
|
|
3549
3623
|
};
|
|
3550
3624
|
// A budget terminal turns a succeeded lifecycle into a failed one (D8).
|
|
3551
3625
|
const convBudgetTerminal = ledger.terminal();
|
|
@@ -3556,6 +3630,19 @@ export class Orchestrator {
|
|
|
3556
3630
|
review_requested: contract.review_requested,
|
|
3557
3631
|
});
|
|
3558
3632
|
}
|
|
3633
|
+
// The shared classifier owns a budget terminal's cause and remedy: a
|
|
3634
|
+
// processing re-admission denial, or the ledger's settled terminal once it
|
|
3635
|
+
// stopped the loop over an incomplete capture (the capture facts and
|
|
3636
|
+
// inspection actions follow it in the failure record).
|
|
3637
|
+
const convBudgetMapping = processingBudgetDenial ||
|
|
3638
|
+
(convBudgetTerminal && lastRun?.captureRefusal && isBudgetTerminal(facts.reason))
|
|
3639
|
+
? classifyBudgetFailure({ denial: processingBudgetDenial, terminal: convBudgetTerminal })
|
|
3640
|
+
: null;
|
|
3641
|
+
if (convBudgetMapping &&
|
|
3642
|
+
facts.lifecycle === "failed" &&
|
|
3643
|
+
isBudgetTerminal(facts.reason) &&
|
|
3644
|
+
facts.reason !== convBudgetMapping.reason)
|
|
3645
|
+
facts = { ...facts, reason: convBudgetMapping.reason };
|
|
3559
3646
|
// A reviewer escalation to a human forces the REVIEW axis to blocked.
|
|
3560
3647
|
const needsHuman = lastFindings.some((f) => f.severity === "NEEDS_HUMAN" && isBlocking(f));
|
|
3561
3648
|
if (needsHuman && facts.lifecycle === "succeeded" && facts.review !== "blocked") {
|
|
@@ -3579,6 +3666,8 @@ export class Orchestrator {
|
|
|
3579
3666
|
facts = { ...facts, checks: "failed", reason: "checks_failed" };
|
|
3580
3667
|
}
|
|
3581
3668
|
const convNeedsDecision = facts.review === "blocked" || facts.checks === "failed";
|
|
3669
|
+
// Every attempt's failed tries by the actual outcome; a block is not an account failure.
|
|
3670
|
+
const convPool = terminalPoolAttempts(facts, convergenceRuns);
|
|
3582
3671
|
if (decision) {
|
|
3583
3672
|
// Shared honesty owner (same as the race path): a needs-decision terminal
|
|
3584
3673
|
// overrides the persisted decision; final_verify is recorded either way.
|
|
@@ -3600,16 +3689,13 @@ export class Orchestrator {
|
|
|
3600
3689
|
reviewAllowsApply(facts) &&
|
|
3601
3690
|
facts.checks !== "failed" &&
|
|
3602
3691
|
!workStateVetoes(facts);
|
|
3603
|
-
if (lastRun.files) {
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
store,
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
harnessId: lastRun.harnessId,
|
|
3611
|
-
facts,
|
|
3612
|
-
log,
|
|
3692
|
+
if (lastRun.files || lastRun.captureRefusal) {
|
|
3693
|
+
// An incomplete capture has no exact patch: never publish an empty one.
|
|
3694
|
+
await delegateFailure.persistFailedCandidateOutput({
|
|
3695
|
+
...{ store, log, paths, execRoot, preTurnSha, taskId, mode, facts, run: lastRun },
|
|
3696
|
+
live: input.inPlace === true && input.workspaceKind !== "directory",
|
|
3697
|
+
kind: input.create === true ? "new_repo" : "patch",
|
|
3698
|
+
attempts: attempt,
|
|
3613
3699
|
});
|
|
3614
3700
|
}
|
|
3615
3701
|
else {
|
|
@@ -3656,7 +3742,7 @@ export class Orchestrator {
|
|
|
3656
3742
|
store.writeText(join(paths.finalDir, "answer.md"), lastRun.answerText);
|
|
3657
3743
|
log.emit("output.ready", { kind: "answer", path: "final/answer.md" });
|
|
3658
3744
|
}
|
|
3659
|
-
store.writeText(join(paths.finalDir, "summary.md"), `# Run ${runId} (${mode})\n\n- Lifecycle: ${facts.lifecycle}${facts.reason ? ` (${facts.reason})` : ""}\n- Attempts: ${attempt}\n- Winner: ${lastRun.attemptId}\n- Review verified (cross-family): ${actualReviewVerified}\n- Apply recommendation: ${decision?.apply_recommendation ?? "inspect"}${stuckNoProgressReason ? `\n- No-progress reason: ${stuckNoProgressReason}` : ""}${secretDiff.summaryDisclosure(lastRun.secretLike)}\n`);
|
|
3745
|
+
store.writeText(join(paths.finalDir, "summary.md"), `${withPoolAttempts(`# Run ${runId} (${mode})\n\n- Lifecycle: ${facts.lifecycle}${facts.reason ? ` (${facts.reason})` : ""}\n- Attempts: ${attempt}\n- Winner: ${lastRun.attemptId}\n- Review verified (cross-family): ${actualReviewVerified}\n- Apply recommendation: ${decision?.apply_recommendation ?? "inspect"}${stuckNoProgressReason ? `\n- No-progress reason: ${stuckNoProgressReason}` : ""}${secretDiff.summaryDisclosure(lastRun.secretLike)}`, convPool)}\n`);
|
|
3660
3746
|
// Lifecycle invariant (all modes): output.ready precedes the terminal
|
|
3661
3747
|
// event so a client that applied the terminal event has the output.
|
|
3662
3748
|
log.emit("output.ready", {
|
|
@@ -3675,9 +3761,7 @@ export class Orchestrator {
|
|
|
3675
3761
|
const convDeclared = !convNeedsDecision && facts.lifecycle === "failed" && !isBudgetTerminal(facts.reason)
|
|
3676
3762
|
? lastRun?.declaredFailure
|
|
3677
3763
|
: undefined;
|
|
3678
|
-
const
|
|
3679
|
-
? classifyBudgetFailure({ denial: processingBudgetDenial, terminal: ledger.terminal() })
|
|
3680
|
-
: null;
|
|
3764
|
+
const inspect = secretDiff.captureInspectionActions(lastRun?.captureRefusal);
|
|
3681
3765
|
writeFailure(store, paths, {
|
|
3682
3766
|
phase: convNeedsDecision ? "review" : "convergence",
|
|
3683
3767
|
category: isBudgetTerminal(facts.reason)
|
|
@@ -3687,50 +3771,58 @@ export class Orchestrator {
|
|
|
3687
3771
|
: convNeedsDecision
|
|
3688
3772
|
? "policy"
|
|
3689
3773
|
: (convDeclared?.category ?? "internal"),
|
|
3690
|
-
code:
|
|
3774
|
+
code: convBudgetMapping?.code ?? convDeclared?.code ?? null,
|
|
3691
3775
|
resetsAt: convDeclared?.resetsAt ?? null,
|
|
3692
3776
|
requestRefusal: convDeclared?.requestRefusal,
|
|
3693
|
-
safeMessage: convNeedsDecision
|
|
3694
|
-
? `review escalated to a human decision after ${attempt} attempt(s)`
|
|
3695
|
-
: facts.reason === "stuck_no_progress"
|
|
3696
|
-
? (stuckNoProgressReason ?? `stuck_no_progress after ${attempt} attempt(s)`)
|
|
3697
|
-
: `${facts.lifecycle}${facts.reason ? ` (${facts.reason})` : ""} after ${attempt} attempt(s)${lastDiffStable ? "" : " (diff changed after review; review is stale)"}`,
|
|
3698
3777
|
harnessId: lastRun?.harnessId,
|
|
3699
3778
|
attemptId: lastRun?.attemptId,
|
|
3700
3779
|
runDir: paths.root,
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
"Increase --max-seconds or narrow the scope, then re-run",
|
|
3706
|
-
]
|
|
3707
|
-
: ["Retry if cancellation was accidental"]
|
|
3708
|
-
: convNeedsDecision
|
|
3709
|
-
? [
|
|
3710
|
-
"Review the blocking findings on the run's turn",
|
|
3711
|
-
"Accept the risk to apply this exact patch, or discard the change",
|
|
3712
|
-
]
|
|
3713
|
-
: facts.reason === "stuck_no_progress"
|
|
3780
|
+
captureFailure: lastRun?.captureRefusal?.failure,
|
|
3781
|
+
nextActions: [
|
|
3782
|
+
...(facts.lifecycle === "cancelled"
|
|
3783
|
+
? facts.reason === "wall_clock_exceeded"
|
|
3714
3784
|
? [
|
|
3715
|
-
"Inspect the
|
|
3716
|
-
"
|
|
3717
|
-
"Fix the gate or provide a different repair instruction",
|
|
3785
|
+
"Inspect the partial work kept from before the deadline",
|
|
3786
|
+
"Increase --max-seconds or narrow the scope, then re-run",
|
|
3718
3787
|
]
|
|
3719
|
-
: [
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
"
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3788
|
+
: ["Retry if cancellation was accidental"]
|
|
3789
|
+
: convNeedsDecision
|
|
3790
|
+
? [
|
|
3791
|
+
"Review the blocking findings on the run's turn",
|
|
3792
|
+
"Accept the risk to apply this exact patch, or discard the change",
|
|
3793
|
+
]
|
|
3794
|
+
: facts.reason === "stuck_no_progress"
|
|
3795
|
+
? [
|
|
3796
|
+
"Inspect the stable patch",
|
|
3797
|
+
"Inspect the failing gate output",
|
|
3798
|
+
"Fix the gate or provide a different repair instruction",
|
|
3799
|
+
]
|
|
3800
|
+
: lastRun?.captureRefusal // no latest patch exists to inspect
|
|
3801
|
+
? secretDiff.captureRefusalNextActions(lastRun.captureRefusal)
|
|
3802
|
+
: [
|
|
3803
|
+
"Open diagnostics",
|
|
3804
|
+
"Inspect latest patch and review findings",
|
|
3805
|
+
"Retry with more attempts or a narrower prompt",
|
|
3806
|
+
]),
|
|
3807
|
+
...inspect,
|
|
3808
|
+
],
|
|
3809
|
+
...(convBudgetMapping
|
|
3810
|
+
? budgetFailureRecord(convBudgetMapping, { runDir: paths.root, followUps: inspect })
|
|
3726
3811
|
: {}),
|
|
3812
|
+
safeMessage: withPoolAttempts(convBudgetMapping?.safeMessage ??
|
|
3813
|
+
(convNeedsDecision
|
|
3814
|
+
? `review escalated to a human decision after ${attempt} attempt(s)`
|
|
3815
|
+
: facts.reason === "stuck_no_progress"
|
|
3816
|
+
? (stuckNoProgressReason ?? `stuck_no_progress after ${attempt} attempt(s)`)
|
|
3817
|
+
: `${facts.lifecycle}${facts.reason ? ` (${facts.reason})` : ""} after ${attempt} attempt(s)${lastDiffStable ? "" : " (diff changed after review; review is stale)"}`), convPool),
|
|
3818
|
+
poolAttempts: convPool,
|
|
3727
3819
|
});
|
|
3728
3820
|
// D-16 r8/r9: an INTERRUPTED envelope run still gets its diagnostic
|
|
3729
3821
|
// summary + output.ready (only patch/work_product are withheld) — the
|
|
3730
3822
|
// ARCHITECTURE event contract guarantees output.ready precedes the
|
|
3731
3823
|
// terminal in every mode.
|
|
3732
3824
|
if (!lastRun || (interrupted && input.inPlace !== true && !lastRun.files)) {
|
|
3733
|
-
store.writeText(join(paths.finalDir, "summary.md"), `# Run ${runId} (${mode})\n\n- Lifecycle: ${facts.lifecycle}${facts.reason ? ` (${facts.reason})` : ""}\n- Attempts: ${attempt}\n`);
|
|
3825
|
+
store.writeText(join(paths.finalDir, "summary.md"), `${withPoolAttempts(`# Run ${runId} (${mode})\n\n- Lifecycle: ${facts.lifecycle}${facts.reason ? ` (${facts.reason})` : ""}\n- Attempts: ${attempt}`, convPool)}\n`);
|
|
3734
3826
|
log.emit("output.ready", {
|
|
3735
3827
|
kind: "summary",
|
|
3736
3828
|
path: "final/summary.md",
|
|
@@ -3738,8 +3830,9 @@ export class Orchestrator {
|
|
|
3738
3830
|
});
|
|
3739
3831
|
}
|
|
3740
3832
|
}
|
|
3741
|
-
// work_product.emitted only when a product was actually written (r9)
|
|
3742
|
-
|
|
3833
|
+
// work_product.emitted only when a product was actually written (r9); a
|
|
3834
|
+
// capture refusal's live record announces itself.
|
|
3835
|
+
if (lastRun && !lastRun.captureRefusal && (lastRun.files || !interrupted || input.inPlace)) {
|
|
3743
3836
|
log.emit("work_product.emitted", { winner: lastRun.attemptId });
|
|
3744
3837
|
}
|
|
3745
3838
|
if (!convIsFailureTerminal) {
|
|
@@ -3749,6 +3842,8 @@ export class Orchestrator {
|
|
|
3749
3842
|
facts,
|
|
3750
3843
|
reason: facts.reason,
|
|
3751
3844
|
attempts: attempt,
|
|
3845
|
+
// A work-state veto is still a block: it keeps the earlier refusals.
|
|
3846
|
+
...(convPool.length > 0 ? { pool_attempts: convPool } : {}),
|
|
3752
3847
|
});
|
|
3753
3848
|
}
|
|
3754
3849
|
else if (facts.lifecycle === "succeeded") {
|
|
@@ -3758,6 +3853,7 @@ export class Orchestrator {
|
|
|
3758
3853
|
facts,
|
|
3759
3854
|
attempts: attempt,
|
|
3760
3855
|
phase: "review",
|
|
3856
|
+
pool_attempts: convPool,
|
|
3761
3857
|
failure_ref: "final/failure.yaml",
|
|
3762
3858
|
});
|
|
3763
3859
|
}
|
|
@@ -3769,6 +3865,7 @@ export class Orchestrator {
|
|
|
3769
3865
|
reason: facts.reason,
|
|
3770
3866
|
attempts: attempt,
|
|
3771
3867
|
phase: "convergence",
|
|
3868
|
+
pool_attempts: convPool,
|
|
3772
3869
|
failure_ref: "final/failure.yaml",
|
|
3773
3870
|
// QA-041: surface the typed deadline reason on the terminal event so the
|
|
3774
3871
|
// daemon job result / Control API can distinguish it from a user cancel.
|
|
@@ -3791,9 +3888,9 @@ export class Orchestrator {
|
|
|
3791
3888
|
...(facts.lifecycle === "cancelled" && convAbortReason
|
|
3792
3889
|
? { cancelReason: convAbortReason }
|
|
3793
3890
|
: {}),
|
|
3794
|
-
summary: converged
|
|
3891
|
+
summary: withPoolAttempts(converged
|
|
3795
3892
|
? `converged in ${attempt} attempt(s)`
|
|
3796
|
-
: `${facts.lifecycle} after ${attempt} attempt(s)`,
|
|
3893
|
+
: `${facts.lifecycle} after ${attempt} attempt(s)`, convPool),
|
|
3797
3894
|
candidates: lastRun
|
|
3798
3895
|
? [
|
|
3799
3896
|
{
|
|
@@ -4227,7 +4324,7 @@ export class Orchestrator {
|
|
|
4227
4324
|
// recorded user intent is the CALLER's goal.
|
|
4228
4325
|
const contract = this.buildContract({ ...input, prompt: opts.contractIntent ?? prompt }, taskId, opts.mode);
|
|
4229
4326
|
const quotaSnapshots = this.quotaSnapshotPreflight();
|
|
4230
|
-
const { store, paths, log, ledger } = beginAnnouncedRun({
|
|
4327
|
+
const { store, paths, log, ledger, runContext } = beginAnnouncedRun({
|
|
4231
4328
|
input,
|
|
4232
4329
|
contract,
|
|
4233
4330
|
quotaSnapshots,
|
|
@@ -4287,6 +4384,14 @@ export class Orchestrator {
|
|
|
4287
4384
|
};
|
|
4288
4385
|
}
|
|
4289
4386
|
const attempts = [];
|
|
4387
|
+
// A launched attempt's tries' accounts, owned here until its record joins
|
|
4388
|
+
// `attempts` (as an Agent slot owns them): a throw after its tries settled
|
|
4389
|
+
// still names them on the run's terminal, and never twice.
|
|
4390
|
+
const launched = new Set();
|
|
4391
|
+
runContext.attempts = () => [
|
|
4392
|
+
...attempts,
|
|
4393
|
+
...[...launched].map((tried) => ({ poolAttempts: tried.snapshot() })),
|
|
4394
|
+
];
|
|
4290
4395
|
const attemptTelemetries = [];
|
|
4291
4396
|
let fallbackOpen = false;
|
|
4292
4397
|
let budgetStopped = false;
|
|
@@ -4498,12 +4603,15 @@ export class Orchestrator {
|
|
|
4498
4603
|
const retryPolicy = transientRetryPolicy(this.config(input.repoRoot));
|
|
4499
4604
|
let activeSessionId = spec.session_id;
|
|
4500
4605
|
const live = liveAttempt(input, routed, paths, contract, attemptId, () => activeSessionId);
|
|
4606
|
+
const tried = new PoolAttemptEvidence(adapter.id, attemptId);
|
|
4607
|
+
launched.add(tried);
|
|
4501
4608
|
let continuity;
|
|
4502
4609
|
({ continuity, spec } = await openContinuity(this.continuityDeps(adapter, routed, contract, attemptId, paths, input, log, {
|
|
4503
4610
|
envelope: null,
|
|
4504
4611
|
harnessHome: null,
|
|
4505
4612
|
workOrder: input.prompt,
|
|
4506
4613
|
firstPrompt: spec.prompt,
|
|
4614
|
+
poolEvidence: tried,
|
|
4507
4615
|
}), { spec, continuation: input.continuation, store, telemetry }));
|
|
4508
4616
|
const onAbort = () => void adapter.cancel?.(activeSessionId)?.catch(() => { });
|
|
4509
4617
|
if (input.signal) {
|
|
@@ -4538,9 +4646,8 @@ export class Orchestrator {
|
|
|
4538
4646
|
runSpec.extra["abortSignal"] = input.signal
|
|
4539
4647
|
? AbortSignal.any([input.signal, tryAbort.signal])
|
|
4540
4648
|
: tryAbort.signal;
|
|
4541
|
-
continuity.beginTry(runSpec, nativeTry, tryAbort);
|
|
4542
4649
|
// #363: bound before the spawn — a credential change mid-try voids it.
|
|
4543
|
-
const refusal =
|
|
4650
|
+
const refusal = continuity.beginTry(runSpec, nativeTry, tryAbort);
|
|
4544
4651
|
const transientStart = telemetry.transientFailures.length;
|
|
4545
4652
|
const rateLimitStart = telemetry.rateLimits.length;
|
|
4546
4653
|
log.emit("harness.started", {
|
|
@@ -4615,8 +4722,6 @@ export class Orchestrator {
|
|
|
4615
4722
|
if (!recordAdapterThrow(telemetry, err))
|
|
4616
4723
|
break;
|
|
4617
4724
|
}
|
|
4618
|
-
if (streamBudgetDenied)
|
|
4619
|
-
break;
|
|
4620
4725
|
if (telemetry.requestRefusal)
|
|
4621
4726
|
break;
|
|
4622
4727
|
const newTransients = telemetry.transientFailures.slice(transientStart);
|
|
@@ -4625,10 +4730,14 @@ export class Orchestrator {
|
|
|
4625
4730
|
const reportSoFar = answer.text();
|
|
4626
4731
|
const delivered = !harnessError && reportSoFar.length > 0 && !input.signal?.aborted;
|
|
4627
4732
|
this.credentials.noteTryServed(refusal, telemetry.outputMarkers, delivered);
|
|
4733
|
+
// A budget stop ends the attempt; typed progress before it still served the account.
|
|
4734
|
+
if (streamBudgetDenied)
|
|
4735
|
+
break;
|
|
4628
4736
|
const verdict = await continuity.afterTry({
|
|
4629
4737
|
runSpec,
|
|
4630
4738
|
nativeTry,
|
|
4631
4739
|
harnessErrored: harnessError !== null,
|
|
4740
|
+
originalError: harnessError,
|
|
4632
4741
|
aborted: input.signal?.aborted === true,
|
|
4633
4742
|
requestRefused: Boolean(telemetry.requestRefusal),
|
|
4634
4743
|
newTransients,
|
|
@@ -4746,6 +4855,7 @@ export class Orchestrator {
|
|
|
4746
4855
|
: {}),
|
|
4747
4856
|
...continuity.terminalFacts(roResumable),
|
|
4748
4857
|
});
|
|
4858
|
+
launched.delete(tried);
|
|
4749
4859
|
if (opts.deepScan) {
|
|
4750
4860
|
store.writeText(join(paths.findingsDir, `${attemptId}-error.md`), `# Explorer ${attemptId} failed\n\n${harnessError}\n`);
|
|
4751
4861
|
}
|
|
@@ -4771,6 +4881,7 @@ export class Orchestrator {
|
|
|
4771
4881
|
...(scoutInterrupted ? { interrupted: true } : {}),
|
|
4772
4882
|
...continuity.terminalFacts(roResumable),
|
|
4773
4883
|
});
|
|
4884
|
+
launched.delete(tried);
|
|
4774
4885
|
if (opts.deepScan) {
|
|
4775
4886
|
const warningNote = toolWarnings(telemetry).length
|
|
4776
4887
|
? `\n\n> Tool warnings: ${toolWarnings(telemetry)
|
|
@@ -4931,188 +5042,20 @@ export class Orchestrator {
|
|
|
4931
5042
|
harnessId: a.harnessId,
|
|
4932
5043
|
status: a.status,
|
|
4933
5044
|
}));
|
|
4934
|
-
const cancelledTerminal = () => cancelledResult(log, runId, taskId, opts.mode, paths.root, candidateSummaries, () => this.writeRunTelemetry(store, paths, contract, runId, taskId, opts.mode, attemptTelemetries, null), ledger.spend(), input.signal, store, undefined, resumableOf(attempts, "cancelled"));
|
|
5045
|
+
const cancelledTerminal = () => cancelledResult(log, runId, taskId, opts.mode, paths.root, candidateSummaries, () => this.writeRunTelemetry(store, paths, contract, runId, taskId, opts.mode, attemptTelemetries, null), ledger.spend(), input.signal, store, undefined, resumableOf(attempts, "cancelled"), attempts);
|
|
4935
5046
|
if (input.signal?.aborted)
|
|
4936
5047
|
return cancelledTerminal();
|
|
4937
5048
|
const succeededReadonly = attempts.filter((a) => a.status === "success");
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
? classifyBudgetFailure({ denial: budgetDenial, terminal: ledger.terminal() })
|
|
4946
|
-
: null;
|
|
4947
|
-
const singleError = budgetMapping?.safeMessage ??
|
|
4948
|
-
last?.error ??
|
|
4949
|
-
(budgetStopped ? "budget exhausted before any attempt" : "harness failed");
|
|
4950
|
-
if (fallbackOpen || webBlocked) {
|
|
4951
|
-
log.emit("route.fallback.exhausted", {
|
|
4952
|
-
harness_id: last?.harnessId ?? null,
|
|
4953
|
-
attempt_id: last?.attemptId ?? null,
|
|
4954
|
-
reason: "web_evidence_unsatisfied",
|
|
4955
|
-
error: singleError,
|
|
4956
|
-
});
|
|
4957
|
-
fallbackOpen = false;
|
|
4958
|
-
}
|
|
4959
|
-
const partialReport = [...attempts].reverse().find((a) => a.report)?.report ?? "";
|
|
4960
|
-
this.writeRunTelemetry(store, paths, contract, runId, taskId, opts.mode, attemptTelemetries, null);
|
|
4961
|
-
store.writeText(join(paths.contextDir, "context_error.md"), `# ${budgetMapping ? "Budget Denied" : "Harness Error"}\n\n${singleError}\n`);
|
|
4962
|
-
const roEventRefs = attempts.map((a) => `attempts/${a.attemptId}/events.jsonl`);
|
|
4963
|
-
if (budgetMapping) {
|
|
4964
|
-
writeFailure(store, paths, budgetFailureRecord(budgetMapping, { eventRefs: roEventRefs, runDir: paths.root }));
|
|
4965
|
-
}
|
|
4966
|
-
else {
|
|
4967
|
-
// #31: classify the harness cause across the read-only attempts so auth
|
|
4968
|
-
// guidance appears only on a real auth failure.
|
|
4969
|
-
const roCategory = dominantHarnessFailureCategory(attemptTelemetries.flatMap((a) => a.telemetry.transientFailures));
|
|
4970
|
-
// A5: the terminal already speaks with the LAST attempt's message; its
|
|
4971
|
-
// typed refusal rides along machine-readably instead of reducing to it.
|
|
4972
|
-
const roDeclared = webBlocked ? undefined : last?.declaredFailure;
|
|
4973
|
-
writeFailure(store, paths, {
|
|
4974
|
-
phase: "harness",
|
|
4975
|
-
category: webBlocked ? "policy" : (roDeclared?.category ?? "harness_error"),
|
|
4976
|
-
code: roDeclared?.code ?? null,
|
|
4977
|
-
harnessId: last?.harnessId,
|
|
4978
|
-
attemptId: last?.attemptId,
|
|
4979
|
-
safeMessage: singleError,
|
|
4980
|
-
eventRefs: roEventRefs,
|
|
4981
|
-
runDir: paths.root,
|
|
4982
|
-
resetsAt: roDeclared?.resetsAt ?? null,
|
|
4983
|
-
requestRefusal: roDeclared?.requestRefusal,
|
|
4984
|
-
vendorFailure: attemptVendorFailure(attemptTelemetries, last?.attemptId),
|
|
4985
|
-
nextActions: harnessFailureNextActions(roCategory),
|
|
4986
|
-
});
|
|
4987
|
-
}
|
|
4988
|
-
// QA-036: re-check the DELIVERABLE through the shared finalizer helper —
|
|
4989
|
-
// a blocked Ask that produced NO answer can no longer read as a succeeded
|
|
4990
|
-
// "Needs review" run (exit 0); it is an honest failure (exit 1).
|
|
4991
|
-
const roTerminal = readOnlyNoSuccessTerminal({
|
|
4992
|
-
webBlocked,
|
|
4993
|
-
hasDeliverable: partialReport.trim().length > 0,
|
|
4994
|
-
budgetStopped,
|
|
4995
|
-
attemptsCount: attempts.length,
|
|
4996
|
-
});
|
|
4997
|
-
const terminalFacts = makeOutcomeFacts(roTerminal.lifecycle, {
|
|
4998
|
-
...(roTerminal.review ? { review: roTerminal.review } : {}),
|
|
4999
|
-
reason: budgetMapping?.reason ?? roTerminal.reason,
|
|
5000
|
-
});
|
|
5001
|
-
const terminalHarnessId = budgetMapping?.harnessId ?? last?.harnessId;
|
|
5002
|
-
store.writeText(join(paths.finalDir, "summary.md"), `# Run ${runId} (${opts.mode})\n\n- Harness: ${terminalHarnessId ?? "none"}\n- Lifecycle: ${terminalFacts.lifecycle}${terminalFacts.reason ? ` (${terminalFacts.reason})` : ""}\n\n${singleError}\n`);
|
|
5003
|
-
log.emit("output.ready", { kind: "summary", path: "final/summary.md", state: "diagnostic" });
|
|
5004
|
-
if (terminalFacts.lifecycle === "succeeded") {
|
|
5005
|
-
log.emit("run.blocked", {
|
|
5006
|
-
lifecycle: terminalFacts.lifecycle,
|
|
5007
|
-
facts: terminalFacts,
|
|
5008
|
-
harness_id: terminalHarnessId,
|
|
5009
|
-
error: singleError,
|
|
5010
|
-
failure_ref: "final/failure.yaml",
|
|
5011
|
-
...resumableTerminal(store, paths, attempts, true),
|
|
5012
|
-
});
|
|
5013
|
-
}
|
|
5014
|
-
else {
|
|
5015
|
-
log.emit("run.failed", {
|
|
5016
|
-
lifecycle: terminalFacts.lifecycle,
|
|
5017
|
-
facts: terminalFacts,
|
|
5018
|
-
reason: terminalFacts.reason,
|
|
5019
|
-
phase: budgetMapping?.phase,
|
|
5020
|
-
harness_id: terminalHarnessId,
|
|
5021
|
-
error: singleError,
|
|
5022
|
-
failure_ref: "final/failure.yaml",
|
|
5023
|
-
...resumableTerminal(store, paths, attempts, true),
|
|
5024
|
-
});
|
|
5025
|
-
}
|
|
5026
|
-
return {
|
|
5027
|
-
spendUsd: ledger.spend(),
|
|
5028
|
-
runId,
|
|
5029
|
-
taskId,
|
|
5030
|
-
mode: opts.mode,
|
|
5031
|
-
lifecycle: terminalFacts.lifecycle,
|
|
5032
|
-
facts: terminalFacts,
|
|
5033
|
-
winner: null,
|
|
5034
|
-
runDir: paths.root,
|
|
5035
|
-
summary: singleError,
|
|
5036
|
-
candidates: attempts.map((a) => ({
|
|
5037
|
-
attemptId: a.attemptId,
|
|
5038
|
-
harnessId: a.harnessId,
|
|
5039
|
-
status: a.status,
|
|
5040
|
-
})),
|
|
5041
|
-
};
|
|
5042
|
-
}
|
|
5049
|
+
const readonlyTerminal = {
|
|
5050
|
+
...{ store, paths, log, runId, taskId, mode: opts.mode, ledger, attempts },
|
|
5051
|
+
...{ attemptTelemetries, budgetStopped, budgetDenial },
|
|
5052
|
+
writeTelemetry: () => this.writeRunTelemetry(store, paths, contract, runId, taskId, opts.mode, attemptTelemetries, null),
|
|
5053
|
+
};
|
|
5054
|
+
if (!opts.deepScan && succeededReadonly.length === 0)
|
|
5055
|
+
return readOnlyChainFailureResult({ ...readonlyTerminal, fallbackOpen });
|
|
5043
5056
|
const succeeded = succeededReadonly;
|
|
5044
|
-
if (opts.deepScan && succeeded.length === 0)
|
|
5045
|
-
|
|
5046
|
-
// QA-050/QA-019: only a pure-denial scan may claim a budget failure.
|
|
5047
|
-
const scanBudgetMapping = budgetStopped && !blocked && attempts.every((a) => a.budgetDenied === true)
|
|
5048
|
-
? classifyBudgetFailure({ denial: budgetDenial, terminal: ledger.terminal() })
|
|
5049
|
-
: null;
|
|
5050
|
-
// D-16 r8: ALL scouts out of context → the aggregate is interrupted.
|
|
5051
|
-
const allInterrupted = attempts.length > 0 && attempts.every((a) => a.interrupted === true);
|
|
5052
|
-
const scanFailFacts = allInterrupted
|
|
5053
|
-
? makeOutcomeFacts("interrupted", { reason: "context_capacity_exhausted" })
|
|
5054
|
-
: makeOutcomeFacts("failed", {
|
|
5055
|
-
reason: scanBudgetMapping ? scanBudgetMapping.reason : "harness_failed",
|
|
5056
|
-
});
|
|
5057
|
-
const message = scanBudgetMapping
|
|
5058
|
-
? scanBudgetMapping.safeMessage
|
|
5059
|
-
: attempts.map((a) => `${a.attemptId}/${a.harnessId}: ${a.error ?? "failed"}`).join("\n");
|
|
5060
|
-
this.writeRunTelemetry(store, paths, contract, runId, taskId, opts.mode, attemptTelemetries, null);
|
|
5061
|
-
if (scanBudgetMapping) {
|
|
5062
|
-
writeFailure(store, paths, budgetFailureRecord(scanBudgetMapping, {
|
|
5063
|
-
eventRefs: attempts.map((a) => `attempts/${a.attemptId}/events.jsonl`),
|
|
5064
|
-
runDir: paths.root,
|
|
5065
|
-
}));
|
|
5066
|
-
}
|
|
5067
|
-
else {
|
|
5068
|
-
// Mixed causes stay generic; only unanimous evidence may name the scan's cause.
|
|
5069
|
-
const declared = blocked ? null : unanimousDeclaredFailure(attempts);
|
|
5070
|
-
const scanCategory = dominantHarnessFailureCategory(attemptTelemetries.flatMap((a) => a.telemetry.transientFailures));
|
|
5071
|
-
writeFailure(store, paths, {
|
|
5072
|
-
phase: "harness",
|
|
5073
|
-
category: blocked ? "policy" : (declared?.category ?? "harness_error"),
|
|
5074
|
-
code: declared?.code,
|
|
5075
|
-
resetsAt: declared?.resetsAt,
|
|
5076
|
-
requestRefusal: declared?.requestRefusal,
|
|
5077
|
-
safeMessage: message || "all explorers failed",
|
|
5078
|
-
eventRefs: attempts.map((a) => `attempts/${a.attemptId}/events.jsonl`),
|
|
5079
|
-
runDir: paths.root,
|
|
5080
|
-
nextActions: [
|
|
5081
|
-
...harnessFailureNextActions(scanCategory).filter((a) => !a.startsWith("Retry")),
|
|
5082
|
-
"Reduce explore width",
|
|
5083
|
-
"Retry after setup",
|
|
5084
|
-
],
|
|
5085
|
-
});
|
|
5086
|
-
}
|
|
5087
|
-
// QA-036: no successful explorer means no synthesizable deliverable.
|
|
5088
|
-
store.writeText(join(paths.finalDir, "summary.md"), `# Run ${runId} (${opts.mode})\n\n- Lifecycle: ${scanFailFacts.lifecycle}${scanFailFacts.reason ? ` (${scanFailFacts.reason})` : ""}\n\n${message}\n`);
|
|
5089
|
-
log.emit("output.ready", { kind: "summary", path: "final/summary.md", state: "diagnostic" });
|
|
5090
|
-
log.emit("run.failed", {
|
|
5091
|
-
lifecycle: scanFailFacts.lifecycle,
|
|
5092
|
-
facts: scanFailFacts,
|
|
5093
|
-
reason: scanFailFacts.reason,
|
|
5094
|
-
phase: scanBudgetMapping ? scanBudgetMapping.phase : "harness",
|
|
5095
|
-
error: message,
|
|
5096
|
-
failure_ref: "final/failure.yaml",
|
|
5097
|
-
...resumableTerminal(store, paths, attempts, true),
|
|
5098
|
-
});
|
|
5099
|
-
return {
|
|
5100
|
-
spendUsd: ledger.spend(),
|
|
5101
|
-
runId,
|
|
5102
|
-
taskId,
|
|
5103
|
-
mode: opts.mode,
|
|
5104
|
-
lifecycle: scanFailFacts.lifecycle,
|
|
5105
|
-
facts: scanFailFacts,
|
|
5106
|
-
winner: null,
|
|
5107
|
-
runDir: paths.root,
|
|
5108
|
-
summary: message,
|
|
5109
|
-
candidates: attempts.map((a) => ({
|
|
5110
|
-
attemptId: a.attemptId,
|
|
5111
|
-
harnessId: a.harnessId,
|
|
5112
|
-
status: a.status,
|
|
5113
|
-
})),
|
|
5114
|
-
};
|
|
5115
|
-
}
|
|
5057
|
+
if (opts.deepScan && succeeded.length === 0)
|
|
5058
|
+
return deepScanFailureResult(readonlyTerminal);
|
|
5116
5059
|
const unsuccessful = attempts.filter((a) => a.status !== "success");
|
|
5117
5060
|
// #27 / D-6: a multi-scout deep scan runs ONE bounded synthesis reducer over
|
|
5118
5061
|
// the raw scout reports so the final artifact is a real merge, not a
|
|
@@ -5216,7 +5159,9 @@ export class Orchestrator {
|
|
|
5216
5159
|
.map((a) => `${a.attemptId}:${a.harnessId}:${a.status}`)
|
|
5217
5160
|
.join(", ");
|
|
5218
5161
|
const secretLike = secretDiff.answerSecretLikeFinding(answerMatches);
|
|
5219
|
-
|
|
5162
|
+
// A delivered report that still ends unsuccessfully or blocked keeps it and names every failed try.
|
|
5163
|
+
const reportPool = terminalPoolAttempts(terminalFacts, attempts);
|
|
5164
|
+
store.writeText(join(paths.finalDir, "summary.md"), `# Run ${runId} (${opts.mode})\n\n- Harnesses: ${harnessLabel}\n- Lifecycle: ${terminalFacts.lifecycle}${terminalFacts.reason ? ` (${terminalFacts.reason})` : ""}${secretDiff.summaryDisclosure(secretLike)}\n\n${withPoolAttempts(report, reportPool)}\n`);
|
|
5220
5165
|
const reportProducerAttemptId = opts.deepScan &&
|
|
5221
5166
|
deepScanSynthesis?.status === "succeeded" &&
|
|
5222
5167
|
deepScanSynthesis.reducer_attempt_id
|
|
@@ -5243,7 +5188,8 @@ export class Orchestrator {
|
|
|
5243
5188
|
writeFailure(store, paths, {
|
|
5244
5189
|
phase: "executor",
|
|
5245
5190
|
category: terminalFacts.reason === "context_capacity_exhausted" ? "harness_error" : "budget",
|
|
5246
|
-
safeMessage: `read-only report ended ${terminalFacts.lifecycle}${terminalFacts.reason ? ` (${terminalFacts.reason.replaceAll("_", " ")})` : ""}`,
|
|
5191
|
+
safeMessage: withPoolAttempts(`read-only report ended ${terminalFacts.lifecycle}${terminalFacts.reason ? ` (${terminalFacts.reason.replaceAll("_", " ")})` : ""}`, reportPool),
|
|
5192
|
+
poolAttempts: reportPool,
|
|
5247
5193
|
runDir: paths.root,
|
|
5248
5194
|
nextActions: terminalFacts.reason === "context_capacity_exhausted"
|
|
5249
5195
|
? ["Inspect the partial report", "Re-run with a narrower scope"]
|
|
@@ -5254,6 +5200,7 @@ export class Orchestrator {
|
|
|
5254
5200
|
facts: terminalFacts,
|
|
5255
5201
|
reason: terminalFacts.reason,
|
|
5256
5202
|
phase: "executor",
|
|
5203
|
+
pool_attempts: reportPool,
|
|
5257
5204
|
failure_ref: "final/failure.yaml",
|
|
5258
5205
|
...resumableTerminal(store, paths, attempts, true),
|
|
5259
5206
|
});
|
|
@@ -5263,6 +5210,7 @@ export class Orchestrator {
|
|
|
5263
5210
|
lifecycle: terminalFacts.lifecycle,
|
|
5264
5211
|
facts: terminalFacts,
|
|
5265
5212
|
reason: terminalFacts.reason,
|
|
5213
|
+
pool_attempts: reportPool,
|
|
5266
5214
|
...resumableTerminal(store, paths, attempts, true),
|
|
5267
5215
|
});
|
|
5268
5216
|
}
|
|
@@ -5283,7 +5231,7 @@ export class Orchestrator {
|
|
|
5283
5231
|
facts: terminalFacts,
|
|
5284
5232
|
winner: null,
|
|
5285
5233
|
runDir: paths.root,
|
|
5286
|
-
summary: redactSecrets(report).slice(0, 400),
|
|
5234
|
+
summary: withPoolAttempts(redactSecrets(report).slice(0, 400), reportPool),
|
|
5287
5235
|
candidates: candidateSummaries,
|
|
5288
5236
|
};
|
|
5289
5237
|
}
|