@claudexor/orchestrator 3.8.0 → 3.8.2
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 +1 -0
- package/dist/account-pool.d.ts.map +1 -1
- package/dist/account-pool.js +2 -0
- package/dist/account-pool.js.map +1 -1
- package/dist/account-resolution.d.ts +2 -0
- package/dist/account-resolution.d.ts.map +1 -1
- package/dist/account-resolution.js +32 -12
- package/dist/account-resolution.js.map +1 -1
- package/dist/candidateEvidence.d.ts +3 -3
- package/dist/candidateEvidence.d.ts.map +1 -1
- package/dist/candidateOutputs.d.ts +2 -2
- package/dist/candidateOutputs.d.ts.map +1 -1
- package/dist/candidateOutputs.js +11 -6
- package/dist/candidateOutputs.js.map +1 -1
- package/dist/contract-gates.d.ts +4 -4
- package/dist/contract-gates.d.ts.map +1 -1
- package/dist/contract-gates.js +2 -0
- package/dist/contract-gates.js.map +1 -1
- package/dist/credential-profiles.d.ts +12 -1
- package/dist/credential-profiles.d.ts.map +1 -1
- package/dist/credential-profiles.js +13 -3
- package/dist/credential-profiles.js.map +1 -1
- package/dist/delegatedHome.d.ts +21 -69
- package/dist/delegatedHome.d.ts.map +1 -1
- package/dist/delegatedHome.js +29 -85
- package/dist/delegatedHome.js.map +1 -1
- package/dist/diffReview.d.ts +2 -1
- package/dist/diffReview.d.ts.map +1 -1
- package/dist/diffReview.js +14 -2
- package/dist/diffReview.js.map +1 -1
- package/dist/orchestrator-credentials.d.ts +13 -2
- package/dist/orchestrator-credentials.d.ts.map +1 -1
- package/dist/orchestrator-credentials.js +64 -3
- package/dist/orchestrator-credentials.js.map +1 -1
- package/dist/orchestrator.d.ts +5 -6
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +204 -211
- package/dist/orchestrator.js.map +1 -1
- package/dist/planRun.d.ts +5 -5
- package/dist/planRun.d.ts.map +1 -1
- package/dist/planRun.js.map +1 -1
- package/dist/plannerAttempt.d.ts +2 -2
- package/dist/plannerAttempt.d.ts.map +1 -1
- package/dist/requestRequirements.d.ts +3 -15
- package/dist/requestRequirements.d.ts.map +1 -1
- package/dist/requestRequirements.js +11 -21
- package/dist/requestRequirements.js.map +1 -1
- package/dist/reviewerPanel.d.ts +9 -1
- package/dist/reviewerPanel.d.ts.map +1 -1
- package/dist/reviewerPanel.js +276 -133
- package/dist/reviewerPanel.js.map +1 -1
- package/dist/runAdmission.d.ts +29 -0
- package/dist/runAdmission.d.ts.map +1 -0
- package/dist/runAdmission.js +63 -0
- package/dist/runAdmission.js.map +1 -0
- package/dist/task-contract-builder.d.ts +2 -2
- package/dist/task-contract-builder.d.ts.map +1 -1
- package/dist/task-contract-builder.js +5 -6
- package/dist/task-contract-builder.js.map +1 -1
- package/package.json +17 -17
package/dist/orchestrator.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { effectiveAuthPreference, observeNativeSessionEvent, resumeSessionForProfile, rotateSpecOnTypedLimit, selectedProfileAvailability, } from "./credential-profiles.js";
|
|
3
|
-
import { OrchestratorCredentials, rotatedSpecInLaneHome } from "./orchestrator-credentials.js";
|
|
3
|
+
import { OrchestratorCredentials, reviewerProfileResolver, rotatedSpecInLaneHome, } from "./orchestrator-credentials.js";
|
|
4
4
|
import { accountPoolRows } from "./account-pool.js";
|
|
5
5
|
import { writeRunTelemetryArtifact } from "./runTelemetryWriter.js";
|
|
6
6
|
import { buildFileBackedSynthesisInput, materializeWinnerOutputs, stageFileBackedContext, writeCandidateAttemptArtifacts, } from "./candidateOutputs.js";
|
|
7
7
|
import { processAttemptUsage } from "./attemptUsage.js";
|
|
8
|
-
import { appliedAttemptFacts, assertDelegatedEvidence,
|
|
8
|
+
import { appliedAttemptFacts, assertDelegatedEvidence, isMutatingAccess, outerBoundaryNotice, scopedHarnessHome, } from "./delegatedHome.js";
|
|
9
9
|
import * as AC from "./attemptUsageCost.js";
|
|
10
10
|
import { candidateRoster, convergenceOutcomeFacts, isWorkingCandidate, partitionCandidates, toCandidateEvidence, unanimousDeclaredFailure, } from "./candidateEvidence.js";
|
|
11
11
|
import { capabilityIntents } from "@claudexor/gateway";
|
|
@@ -13,9 +13,9 @@ import { policyFindings } from "./policyFindings.js";
|
|
|
13
13
|
import { join } from "node:path";
|
|
14
14
|
import { finalizePlanRun, runCouncilPlan, writePlanHarnessFailure, } from "./planRun.js";
|
|
15
15
|
import { runPlannerAttempt as executePlannerAttempt, } from "./plannerAttempt.js";
|
|
16
|
-
import { HarnessRunSpec, ModeKind as ModeKindSchema, QuotaSnapshot as QuotaSnapshotSchema, isBlocking, makeOutcomeFacts,
|
|
16
|
+
import { HarnessRunSpec, ModeKind as ModeKindSchema, QuotaSnapshot as QuotaSnapshotSchema, isBlocking, makeOutcomeFacts, strictifyOutputSchema, estimateEffectiveAuthRoute, } from "@claudexor/schema";
|
|
17
17
|
import { globalConfigDir, loadConfig } from "@claudexor/config";
|
|
18
|
-
import { acceptedTryOutput, AnswerAssembly, CLAUDEXOR_BROWSER_ARTIFACT_SUBDIR, countsAsAgentProgress, HarnessUnavailableError, summarizeDiffPaths as diffStats, withInactivityWatchdog, } from "@claudexor/core";
|
|
18
|
+
import { acceptedTryOutput, AccessProfileIncompatibleError, AnswerAssembly, CLAUDEXOR_BROWSER_ARTIFACT_SUBDIR, countsAsAgentProgress, HarnessUnavailableError, summarizeDiffPaths as diffStats, withInactivityWatchdog, } from "@claudexor/core";
|
|
19
19
|
import { assertRouteModelsAllowed, runModelGovernedRoute } from "./modelGovernance.js";
|
|
20
20
|
import { authModeForCredentialRoute, authModeForPreference } from "./auth-route-classification.js";
|
|
21
21
|
import { governRouteEffort } from "./effortGovernance.js";
|
|
@@ -32,7 +32,8 @@ import { arbitrationBudgetOptions, decisionBudgetSummary } from "./decisionBudge
|
|
|
32
32
|
import { buildRevisePrompt } from "./revisePrompt.js";
|
|
33
33
|
import { cancelledResult, declaredFailure, failTerminally, guardAnnouncedRun, writeFailure, } from "./runTerminals.js";
|
|
34
34
|
import { budgetFailureRecord, classifyBudgetFailure } from "./budgetFailure.js";
|
|
35
|
-
import {
|
|
35
|
+
import { finalizeStructuredOutput } from "./structuredOutput.js";
|
|
36
|
+
import { admitRun } from "./runAdmission.js";
|
|
36
37
|
import { dropDeltaPastBudget, emitPlanProgress, emitTransientExhausted, emitTransientRetryPlan, observeReadonlySpend, } from "./laneStreamEvents.js";
|
|
37
38
|
import { promptWithEngineConstraints, sleep, redactHarnessEvent, harnessEventPayload, safeErrorMessage, renderSummary, observeBudgetSignals, rotateOnStall, recordCleanAttemptMetrics, envInheritance, transientRetryPolicy, reviewerTimeoutMs, harnessInactivityTimeoutMs, observeAuthSwitch, emitPrimaryDivergence, emitPoolDegraded, deliveryRefusalFailure, winnerNeedsHuman, writeRaceDeliveryDecision, } from "./runSupport.js";
|
|
38
39
|
import { candidateStatusInRouteContext, resolveReadOnlyRouteContext, } from "./routeContext.js";
|
|
@@ -53,7 +54,7 @@ import { interactionChannelFor } from "./interaction.js";
|
|
|
53
54
|
import { gateSpecsFromContract, renderTestsEvidence } from "./contract-gates.js";
|
|
54
55
|
import { buildTaskContract } from "./task-contract-builder.js";
|
|
55
56
|
import { ArtifactStore } from "@claudexor/artifact-store";
|
|
56
|
-
import {
|
|
57
|
+
import { buildContextPack, rawContextForEnvelope, preflightEvidence, writeEvidencePacket, } from "@claudexor/context";
|
|
57
58
|
import { WorkspaceManager, captureRawPatchEnvelope, createRevertAnchorFromPatchOrNull, createRevertAnchorOrNull, ensureClaudeBridge, consumeRawPatchEnvelope, snapshotTree, } from "@claudexor/workspace";
|
|
58
59
|
import { blockedDecisionOverride, finalVerifyBlocks, finalVerifyPatch, verifyAndDeliver, } from "@claudexor/delivery";
|
|
59
60
|
import { HarnessGateway } from "@claudexor/gateway";
|
|
@@ -62,7 +63,6 @@ import { arbitrate } from "@claudexor/arbitration";
|
|
|
62
63
|
import { buildSynthesisPlan, decideSynthesis } from "@claudexor/synthesis";
|
|
63
64
|
import { attemptCostEvidence, attemptUsageCostSettlement, BudgetLedger, isBudgetTerminal, explainRanking, loadHarnessMetrics, promptFingerprint, rankHarnesses, reviewUsageCostSettlement, } from "@claudexor/budget";
|
|
64
65
|
import { readTextSafe, appendLine, assertNoInlineSecretValues, DELEGATION_ENV, hashJson, newId, noProjectRepoRoot, redactSecrets, safeInvoke, sha256, userConfigDir, writeText, } from "@claudexor/util";
|
|
65
|
-
import { assertWriteIsolation } from "./write-isolation.js";
|
|
66
66
|
const LABELS = "ABCDEFGHIJ".split("");
|
|
67
67
|
const NO_PROJECT_ROOT = noProjectRepoRoot();
|
|
68
68
|
/** Concurrency cap for parallel candidates/explorers (locked decision: min(n, 4)). */
|
|
@@ -89,7 +89,7 @@ export class Orchestrator {
|
|
|
89
89
|
/** Scoped DIFF review — thin delegate; mechanics live in diffReview.ts. */
|
|
90
90
|
async reviewDiff(input) {
|
|
91
91
|
return runDiffReview(input, {
|
|
92
|
-
resolveReviewers:
|
|
92
|
+
resolveReviewers: this.resolveReviewers.bind(this),
|
|
93
93
|
reviewScoped: (i) => this.reviewScoped(i),
|
|
94
94
|
execRootOf: (root) => this.execRootOf({ repoRoot: root }),
|
|
95
95
|
envInheritance: (root) => envInheritance(this.config(root)),
|
|
@@ -134,42 +134,11 @@ export class Orchestrator {
|
|
|
134
134
|
delegationParentRunId: runId,
|
|
135
135
|
};
|
|
136
136
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
denyPaths: resolved.denyPaths,
|
|
142
|
-
inPlace: resolved.inPlace,
|
|
143
|
-
repoRoot: resolved.repoRoot,
|
|
144
|
-
executionRoot: this.execRootOf(resolved),
|
|
137
|
+
resolved.outputSchema = admitRun(resolved, mode, {
|
|
138
|
+
accessDefault: this.config(resolved.repoRoot).trust.access_default,
|
|
139
|
+
projectProtectedPaths: () => this.projectConfig(resolved.repoRoot).constraints.protected_paths,
|
|
140
|
+
mandatoryFiles: () => this.projectConfig(resolved.repoRoot).context.mandatory_files,
|
|
145
141
|
});
|
|
146
|
-
// outputSchema constrains the run's final ANSWER. It is honored exactly
|
|
147
|
-
// where a final answer is delivered (agent race incl. synthesis, and ask);
|
|
148
|
-
// every other strategy refuses loudly rather than carrying a contract the
|
|
149
|
-
// engine would not validate (INV-023). The schema itself is normalized for
|
|
150
|
-
// the native structured-output routes here at the boundary — unsupported
|
|
151
|
-
// shapes (external/cyclic $ref, non-object root) are a typed refusal, not a mid-run 400.
|
|
152
|
-
if (resolved.outputSchema !== undefined && resolved.outputSchema !== null) {
|
|
153
|
-
if (mode !== "agent" && mode !== "ask") {
|
|
154
|
-
throw new Error(`outputSchema constrains the final answer and applies to agent/ask runs (got mode=${mode}); drop the schema or switch modes`);
|
|
155
|
-
}
|
|
156
|
-
if (resolved.untilClean || (resolved.attempts !== undefined && resolved.attempts !== null)) {
|
|
157
|
-
throw new Error("outputSchema is not supported with convergence flags (--until-clean/--attempts): convergence delivers a gated patch, not a structured answer; drop the schema or the convergence flags");
|
|
158
|
-
}
|
|
159
|
-
// Shape-refuse unsupported schemas, then PROVE it compiles under the same
|
|
160
|
-
// ajv the engine validator uses — a malformed schema is a preflight
|
|
161
|
-
// refusal here (before any run dir), never a mid-run validator crash. The
|
|
162
|
-
// contract keeps the ORIGINAL (conformance authority); local-ref inlining
|
|
163
|
-
// and strictification are transport-only transforms in harnessSpecKnobs.
|
|
164
|
-
resolved.outputSchema = normalizeUserOutputSchema(resolved.outputSchema);
|
|
165
|
-
assertOutputSchemaCompiles(resolved.outputSchema);
|
|
166
|
-
}
|
|
167
|
-
// P1: a versioned `mandatory_files` contract is enforced UNIFORMLY here, for
|
|
168
|
-
// every mode, so the same repo state can't pass `run`/`ask` while failing
|
|
169
|
-
// `audit`. No-op when the list is empty (the default) or for no-project runs.
|
|
170
|
-
if (resolved.repoRoot !== NO_PROJECT_ROOT) {
|
|
171
|
-
assertMandatoryContext(resolved.repoRoot, this.projectConfig(resolved.repoRoot).context.mandatory_files);
|
|
172
|
-
}
|
|
173
142
|
// Reviewer panels are validated only inside Agent strategies that actually
|
|
174
143
|
// review (race/convergence; Plan Council is the plan critique path) — AFTER run-dir
|
|
175
144
|
// creation, so a doomed explicit panel yields typed failure ARTIFACTS
|
|
@@ -239,6 +208,7 @@ export class Orchestrator {
|
|
|
239
208
|
registry: this.deps.registry,
|
|
240
209
|
harnessSettings: this.config(cwd)?.global.harnesses ?? {},
|
|
241
210
|
authPreferenceFor: (id) => this.authPreferenceForHarness(cwd, id, runAuthPreference),
|
|
211
|
+
resolveReviewerProfile: reviewerProfileResolver(this.credentials, cwd),
|
|
242
212
|
onIgnoredSetting,
|
|
243
213
|
}, { reviewerModels: this.deps.reviewerModels, reviewerEfforts: this.deps.reviewerEfforts });
|
|
244
214
|
}
|
|
@@ -295,6 +265,7 @@ export class Orchestrator {
|
|
|
295
265
|
registry: this.deps.registry,
|
|
296
266
|
harnessSettings: this.config(cwd)?.global.harnesses ?? {},
|
|
297
267
|
authPreferenceFor: (id) => this.authPreferenceForHarness(cwd, id, runAuthPreference),
|
|
268
|
+
resolveReviewerProfile: reviewerProfileResolver(this.credentials, cwd),
|
|
298
269
|
}, panel);
|
|
299
270
|
}
|
|
300
271
|
authPreferenceForHarness(repoRoot, harnessId, runAuthPreference) {
|
|
@@ -449,9 +420,9 @@ export class Orchestrator {
|
|
|
449
420
|
: `credential profile "${input.credentialProfileId}" belongs to unavailable harness(es): ${registered.map((p) => p.harness_id).join(", ")}`);
|
|
450
421
|
}
|
|
451
422
|
else {
|
|
452
|
-
// Auto-pools take doctor-OK
|
|
453
|
-
// readiness; a key string or degraded route is visible
|
|
454
|
-
// routable) PLUS harnesses with enabled account rows: the default
|
|
423
|
+
// Auto-pools take doctor-OK default routes (BIBLE §2: doctor decides
|
|
424
|
+
// default-route readiness; a key string or degraded route is visible
|
|
425
|
+
// but not routable) PLUS harnesses with enabled account rows: the default
|
|
455
426
|
// doctor only sees the default store, and a signed-in registry row is
|
|
456
427
|
// invisible to it (cursor after the host-Keychain retirement, or a
|
|
457
428
|
// named-rows-only claude/codex install). Row-bearing lanes still pass
|
|
@@ -463,7 +434,7 @@ export class Orchestrator {
|
|
|
463
434
|
accountPoolRows(registry, s.id).length > 0))
|
|
464
435
|
.map((s) => s.id);
|
|
465
436
|
if (ids.length === 0) {
|
|
466
|
-
throw new HarnessUnavailableError("no doctor-
|
|
437
|
+
throw new HarnessUnavailableError("no doctor-OK default route or enabled account row for this mode; inspect `claudexor doctor` and `claudexor accounts`, or pass --harness explicitly");
|
|
467
438
|
}
|
|
468
439
|
}
|
|
469
440
|
}
|
|
@@ -491,8 +462,7 @@ export class Orchestrator {
|
|
|
491
462
|
const policy = input.web ?? input.externalContextPolicy ?? "auto";
|
|
492
463
|
const pool = [];
|
|
493
464
|
const dropped = [];
|
|
494
|
-
//
|
|
495
|
-
// drop is recorded with its typed STAGE so the disclosure preserves the
|
|
465
|
+
// Every auto-pool drop is recorded with its typed STAGE so the disclosure preserves the
|
|
496
466
|
// real cause instead of collapsing to one reason.
|
|
497
467
|
const droppedLanes = [];
|
|
498
468
|
// The ONE explicit-lane admission gate shared by every drop site (QA-043 /
|
|
@@ -501,14 +471,18 @@ export class Orchestrator {
|
|
|
501
471
|
// substitution or self-race duplication; an AUTO lane is dropped with a
|
|
502
472
|
// typed omission recorded for the degradation receipt.
|
|
503
473
|
const dropLane = (harnessId, stage, detail) => {
|
|
504
|
-
if (explicitPool)
|
|
474
|
+
if (explicitPool) {
|
|
475
|
+
if (stage === "access")
|
|
476
|
+
throw new AccessProfileIncompatibleError(detail);
|
|
505
477
|
throw new HarnessUnavailableError(detail);
|
|
478
|
+
}
|
|
506
479
|
dropped.push(detail);
|
|
507
480
|
droppedLanes.push({ harnessId, stage, detail });
|
|
508
481
|
};
|
|
509
482
|
// One observation set for the whole admission pass, so two lanes cannot be
|
|
510
483
|
// judged against different vendor epochs.
|
|
511
484
|
const vendorQuota = this.credentials.vendorQuotaObservations();
|
|
485
|
+
const liveUnusable = this.deps.credentialUnusable?.() ?? [];
|
|
512
486
|
for (const id of ids) {
|
|
513
487
|
const adapter = this.deps.registry.get(id);
|
|
514
488
|
if (!adapter) {
|
|
@@ -577,14 +551,8 @@ export class Orchestrator {
|
|
|
577
551
|
const profileAdapter = this.deps.registry.get(id);
|
|
578
552
|
const profileProbe = profileAdapter?.probeCredentialProfile?.bind(profileAdapter);
|
|
579
553
|
const explicitPin = this.credentials.effectiveProfileId(input, id);
|
|
580
|
-
|
|
581
|
-
//
|
|
582
|
-
// admission — the thread-bound row followed by the harness's enabled
|
|
583
|
-
// pool rows, consulted only when the default doctor did not already
|
|
584
|
-
// admit the lane. A signed-in registry row is invisible to the default
|
|
585
|
-
// doctor (cursor after the host-Keychain retirement, or a named-rows-
|
|
586
|
-
// only claude/codex install), so an unpinned run must be admitted on
|
|
587
|
-
// row readiness exactly the way explicit pins already are.
|
|
554
|
+
const model = input.models?.[id] ?? cfgEntry?.default_model ?? null;
|
|
555
|
+
// Pins and bound/pool rows are checked against their own readiness.
|
|
588
556
|
const rowCandidateIds = [];
|
|
589
557
|
if (explicitPin) {
|
|
590
558
|
rowCandidateIds.push(explicitPin);
|
|
@@ -603,9 +571,12 @@ export class Orchestrator {
|
|
|
603
571
|
probe: profileProbe,
|
|
604
572
|
// `verification: passed` from the local store only means a login file
|
|
605
573
|
// is present. The poller's authenticated vendor call is the only
|
|
606
|
-
// liveness evidence
|
|
607
|
-
// act on it — otherwise the run dispatches into a revoked token.
|
|
574
|
+
// liveness evidence; admission must act on it or dispatch may use a revoked token.
|
|
608
575
|
quota: vendorQuota,
|
|
576
|
+
unusable: liveUnusable,
|
|
577
|
+
model,
|
|
578
|
+
// Only an explicit/bound route may consume bounded stale LKG evidence.
|
|
579
|
+
allowStale: explicitPin !== null || input.threadAccountBindings?.[id] === candidateId,
|
|
609
580
|
});
|
|
610
581
|
if (verdict === "available") {
|
|
611
582
|
profileAdmitted = true;
|
|
@@ -645,7 +616,7 @@ export class Orchestrator {
|
|
|
645
616
|
const readOnlyIntent = intent === "plan" || intent === "spec" || intent === "explain" || intent === "audit";
|
|
646
617
|
const requiredAccess = this.requestRequirements.adapterAccess(intent, manifest.capabilities.implementation_transport, readOnlyIntent
|
|
647
618
|
? "readonly"
|
|
648
|
-
: (input.access ?? this.config(input.repoRoot).trust.access_default)
|
|
619
|
+
: (input.access ?? this.config(input.repoRoot).trust.access_default));
|
|
649
620
|
const accessSupported = !requiredAccess || manifest.access_profiles_supported.includes(requiredAccess);
|
|
650
621
|
const webSupport = manifest.capabilities.web_policy;
|
|
651
622
|
// Match routeSpecKnobs: a per-harness web default upgrades run-level auto,
|
|
@@ -679,6 +650,7 @@ export class Orchestrator {
|
|
|
679
650
|
harnessId: id,
|
|
680
651
|
requested: input.browser === true,
|
|
681
652
|
manifestCapable: manifest.capabilities.browser_tool,
|
|
653
|
+
requiresFullAccess: manifest.capability_profile.mcp_injection_requires_full_access,
|
|
682
654
|
webPolicy: routePolicy,
|
|
683
655
|
access: requiredAccess,
|
|
684
656
|
}),
|
|
@@ -730,6 +702,9 @@ export class Orchestrator {
|
|
|
730
702
|
}
|
|
731
703
|
}
|
|
732
704
|
if (pool.length === 0) {
|
|
705
|
+
if (droppedLanes.length > 0 && droppedLanes.every((lane) => lane.stage === "access")) {
|
|
706
|
+
throw new AccessProfileIncompatibleError(`no harness can enforce the requested access profile (${droppedLanes.map((lane) => lane.detail).join(", ")})`);
|
|
707
|
+
}
|
|
733
708
|
throw new HarnessUnavailableError(`no harness can perform '${intent}' for this mode${dropped.length ? ` (skipped: ${dropped.join(", ")})` : ""}`);
|
|
734
709
|
}
|
|
735
710
|
// Quota admission must use the account that will actually spawn. In
|
|
@@ -1382,8 +1357,8 @@ export class Orchestrator {
|
|
|
1382
1357
|
/** Decided by `harnessHomeFor` in the CALLER, so the per-attempt applied
|
|
1383
1358
|
* facts exist in the caller's catch too: an attempt that ran and then threw
|
|
1384
1359
|
* must record what it ran under, not just why it stopped. REQUIRED (no
|
|
1385
|
-
* default): a silent fallback here would spawn a delegated
|
|
1386
|
-
* operator's real home while the record still claimed
|
|
1360
|
+
* default): a silent fallback here would spawn a delegated attempt on the
|
|
1361
|
+
* operator's real home while the record still claimed scoped state. */
|
|
1387
1362
|
harnessHome) {
|
|
1388
1363
|
const adapter = routed.adapter;
|
|
1389
1364
|
const knobs = this.routeSpecKnobs(routed, contract, modelHint, effortHint);
|
|
@@ -1412,8 +1387,8 @@ export class Orchestrator {
|
|
|
1412
1387
|
// Engine-derived read-only prompt constraints: protected/auto-protected
|
|
1413
1388
|
// paths PLUS the exact typed gate argv the run will execute (QA-022 FIX B).
|
|
1414
1389
|
prompt: promptWithEngineConstraints(
|
|
1415
|
-
//
|
|
1416
|
-
[prompt,
|
|
1390
|
+
// The child is told that Claudexor adds no outer OS boundary (disclosure 2 of 3).
|
|
1391
|
+
[prompt, outerBoundaryNotice(harnessHome), laneContinuity?.pointerLine, continuationPointer]
|
|
1417
1392
|
.filter((s) => typeof s === "string" && s.length > 0)
|
|
1418
1393
|
.join("\n\n"), contract.constraints.protected_paths, contract.constraints.auto_protected_paths, contract.constraints.protected_path_approvals, contract.tests.commands),
|
|
1419
1394
|
attachments: runInput?.attachments ?? [],
|
|
@@ -1440,11 +1415,8 @@ export class Orchestrator {
|
|
|
1440
1415
|
// Scoped harness home for isolated envelopes AND for every delegated run;
|
|
1441
1416
|
// an ordinary in-place run keeps the native environment so the resumed
|
|
1442
1417
|
// vendor session is reachable. See scopedHarnessHome for the cost a
|
|
1443
|
-
// delegated in-place attempt pays for that
|
|
1418
|
+
// delegated in-place attempt pays for that scoped state.
|
|
1444
1419
|
...(harnessHome.env ? { env: harnessHome.env } : {}),
|
|
1445
|
-
// The OS boundary itself. The shared CLI run loop wraps the argv with it;
|
|
1446
|
-
// no adapter opts in, so no adapter can forget.
|
|
1447
|
-
confinement: harnessHome.confinement,
|
|
1448
1420
|
raw_context_packet: rawContextPacket,
|
|
1449
1421
|
stream_deltas: streamDeltas,
|
|
1450
1422
|
});
|
|
@@ -1791,7 +1763,7 @@ export class Orchestrator {
|
|
|
1791
1763
|
worktreePath: envelope.worktree_path,
|
|
1792
1764
|
artifactRelativeDir,
|
|
1793
1765
|
diff,
|
|
1794
|
-
persistPatch: secretDiffRefusal === undefined,
|
|
1766
|
+
persistPatch: secretDiffRefusal === undefined && isMutatingAccess(access),
|
|
1795
1767
|
persistProducedMedia: secretDiffRefusal === undefined,
|
|
1796
1768
|
answerText,
|
|
1797
1769
|
record: {
|
|
@@ -1807,8 +1779,8 @@ export class Orchestrator {
|
|
|
1807
1779
|
...(secretDiffRefusal ? { secret_diff_refusal: secretDiffRefusal } : {}),
|
|
1808
1780
|
gates: gates.map((g) => ({ id: g.id, status: g.status })),
|
|
1809
1781
|
branch: envelope.branch_name,
|
|
1810
|
-
// Applied facts, not promises:
|
|
1811
|
-
//
|
|
1782
|
+
// Applied facts, not promises: historical proofs stay readable and
|
|
1783
|
+
// current delegated runs state deliberate outer-boundary absence.
|
|
1812
1784
|
// Built by the SAME shape the failure path writes.
|
|
1813
1785
|
...applied,
|
|
1814
1786
|
},
|
|
@@ -1888,6 +1860,7 @@ export class Orchestrator {
|
|
|
1888
1860
|
// announced: a refused run must fail the request loudly, not 200 a runId
|
|
1889
1861
|
// and leave an orphaned run dir without a terminal event.
|
|
1890
1862
|
const contract = this.buildContract(input, taskId, mode);
|
|
1863
|
+
const mutatingRun = isMutatingAccess(contract.access.effective_profile);
|
|
1891
1864
|
const planBrief = verifiedPlanBrief(input);
|
|
1892
1865
|
const quotaSnapshots = this.quotaSnapshotPreflight();
|
|
1893
1866
|
const { store, paths, log, ledger } = beginAnnouncedRun({
|
|
@@ -1918,28 +1891,32 @@ export class Orchestrator {
|
|
|
1918
1891
|
// directory, a filesystem root, or one that cannot be classified), which
|
|
1919
1892
|
// gets a typed refusal BEFORE any mutation (INV-075). For an isolated
|
|
1920
1893
|
// thread the execution root is already a git worktree: a no-op there.
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1894
|
+
if (mutatingRun) {
|
|
1895
|
+
const gitPreconditionError = await ensureWriteModeGitBoundary(execRoot, log, store, paths, runId, mode);
|
|
1896
|
+
if (gitPreconditionError) {
|
|
1897
|
+
return {
|
|
1898
|
+
runId,
|
|
1899
|
+
taskId,
|
|
1900
|
+
mode,
|
|
1901
|
+
lifecycle: "failed",
|
|
1902
|
+
facts: makeOutcomeFacts("failed", { reason: gitPreconditionError.reason }),
|
|
1903
|
+
winner: null,
|
|
1904
|
+
runDir: paths.root,
|
|
1905
|
+
summary: gitPreconditionError.message,
|
|
1906
|
+
candidates: [],
|
|
1907
|
+
};
|
|
1908
|
+
}
|
|
1934
1909
|
}
|
|
1935
1910
|
// Same run-prep stage as the git boundary: if the PROJECT root uses AGENTS.md
|
|
1936
1911
|
// with no CLAUDE.md, bridge it so a Claude Code candidate reads it (INV-113).
|
|
1937
|
-
|
|
1912
|
+
if (mutatingRun) {
|
|
1913
|
+
this.ensureClaudeBridgeForRun(input.repoRoot, input.inPlace === true, log);
|
|
1914
|
+
}
|
|
1938
1915
|
// Pre-turn snapshot of the live tree for in-place runs: the revert restore
|
|
1939
1916
|
// target (server-owned revertInPlace). A snapshot failure must never fail the
|
|
1940
1917
|
// run — revert is simply unavailable then.
|
|
1941
1918
|
let preTurnSha = null;
|
|
1942
|
-
if (input.inPlace === true) {
|
|
1919
|
+
if (mutatingRun && input.inPlace === true) {
|
|
1943
1920
|
try {
|
|
1944
1921
|
preTurnSha = await snapshotTree(execRoot);
|
|
1945
1922
|
}
|
|
@@ -2109,9 +2086,10 @@ export class Orchestrator {
|
|
|
2109
2086
|
accessProfile: candidateAccess,
|
|
2110
2087
|
// Direct-workspace singletons run in place. Races and patch-envelope
|
|
2111
2088
|
// transports stay isolated and adopt through the delivery service.
|
|
2112
|
-
inPlace:
|
|
2113
|
-
|
|
2114
|
-
|
|
2089
|
+
inPlace: !mutatingRun ||
|
|
2090
|
+
(input.inPlace === true &&
|
|
2091
|
+
requestedSingleCandidate &&
|
|
2092
|
+
slot.routed.implementationTransport !== "git_patch_envelope"),
|
|
2115
2093
|
});
|
|
2116
2094
|
harnessHome = this.harnessHomeFor(wsm, envelope, slot.routed, input);
|
|
2117
2095
|
const run = await this.runCandidateInEnvelope(slot.routed, envelope, slot.attemptId, slot.label, contract, input.prompt, store, paths, wsm, ledger, candidateAccess, (ev) => {
|
|
@@ -2287,8 +2265,8 @@ export class Orchestrator {
|
|
|
2287
2265
|
};
|
|
2288
2266
|
await runBounded(slots, Math.min(slots.length, MAX_PARALLEL_CANDIDATES), runSlot);
|
|
2289
2267
|
const runs = runsBySlot.filter((r) => r !== undefined);
|
|
2290
|
-
// Fail-closed terminal: a delegated mutating run whose attempts
|
|
2291
|
-
//
|
|
2268
|
+
// Fail-closed terminal: a delegated mutating run whose attempts state
|
|
2269
|
+
// neither historical proof nor deliberate absence refuses instead of passing.
|
|
2292
2270
|
assertDelegatedEvidence(input.delegated === true, candidateAccess, runs);
|
|
2293
2271
|
const cancelledCandidates = () => runs.map((r) => ({
|
|
2294
2272
|
attemptId: r.attemptId,
|
|
@@ -2306,7 +2284,10 @@ export class Orchestrator {
|
|
|
2306
2284
|
// arbitration (as the race-adoption path does) would fold those user edits
|
|
2307
2285
|
// into the revert target and let a later revert clobber them.
|
|
2308
2286
|
let earlyPostTurnSha = null;
|
|
2309
|
-
if (
|
|
2287
|
+
if (mutatingRun &&
|
|
2288
|
+
input.inPlace &&
|
|
2289
|
+
requestedSingleCandidate &&
|
|
2290
|
+
runs.every((run) => !run.secretDiffRefusal)) {
|
|
2310
2291
|
try {
|
|
2311
2292
|
earlyPostTurnSha = await snapshotTree(execRoot);
|
|
2312
2293
|
}
|
|
@@ -2322,13 +2303,15 @@ export class Orchestrator {
|
|
|
2322
2303
|
if (failedDelegation) {
|
|
2323
2304
|
const failure = delegateFailure.candidateFailureTerminal(failedDelegation, "race");
|
|
2324
2305
|
await disposeReviewEnvelopes();
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2306
|
+
if (mutatingRun) {
|
|
2307
|
+
await delegateFailure.persistFailedInPlaceWorkProduct({
|
|
2308
|
+
...{ store, log, paths, execRoot, preTurnSha, taskId, mode },
|
|
2309
|
+
live: input.inPlace === true && failedDelegation.reviewCwd === execRoot,
|
|
2310
|
+
run: failedDelegation,
|
|
2311
|
+
postTurnSha: earlyPostTurnSha,
|
|
2312
|
+
kind: input.create === true ? "new_repo" : "patch",
|
|
2313
|
+
});
|
|
2314
|
+
}
|
|
2332
2315
|
this.writeRunTelemetry(store, paths, contract, runId, taskId, mode, candidateRoster(runs), null);
|
|
2333
2316
|
return failTerminally(log, store, paths, runId, taskId, mode, failure.phase, failure.error, ledger.spend(), failure.metadata);
|
|
2334
2317
|
}
|
|
@@ -2510,7 +2493,13 @@ export class Orchestrator {
|
|
|
2510
2493
|
return cancelledRaceResult();
|
|
2511
2494
|
}
|
|
2512
2495
|
// Synthesis: if worthwhile, run a synthesizer as a NEW, re-checked candidate.
|
|
2513
|
-
const synth =
|
|
2496
|
+
const synth = mutatingRun
|
|
2497
|
+
? decideSynthesis(evidences, input.synthesis ?? "auto")
|
|
2498
|
+
: {
|
|
2499
|
+
synthesize: false,
|
|
2500
|
+
reason: "readonly access has no write-backed synthesis lifecycle",
|
|
2501
|
+
sources: evidences.map((evidence) => evidence.attemptId),
|
|
2502
|
+
};
|
|
2514
2503
|
store.writeYaml(join(paths.arbitrationDir, "synthesis.yaml"), synth);
|
|
2515
2504
|
log.emit("synthesis.started", { synthesize: synth.synthesize, reason: synth.reason });
|
|
2516
2505
|
if (synth.synthesize && !budgetStopped) {
|
|
@@ -2639,7 +2628,8 @@ export class Orchestrator {
|
|
|
2639
2628
|
// instead defers verification until immediately before delivery.
|
|
2640
2629
|
const inPlaceWinner = winnerRun?.reviewCwd === execRoot;
|
|
2641
2630
|
const deferredRaceVerify = input.inPlace === true && !inPlaceWinner;
|
|
2642
|
-
if (
|
|
2631
|
+
if (mutatingRun &&
|
|
2632
|
+
winnerRun &&
|
|
2643
2633
|
!inPlaceWinner &&
|
|
2644
2634
|
!deferredRaceVerify &&
|
|
2645
2635
|
winnerRun.diff.trim().length > 0 &&
|
|
@@ -2674,17 +2664,7 @@ export class Orchestrator {
|
|
|
2674
2664
|
})) {
|
|
2675
2665
|
log.emit("output.ready", { kind: "artifact", path });
|
|
2676
2666
|
}
|
|
2677
|
-
secretDiff.assertNoSecretLikeTokens("final patch diff", winnerRun.diff);
|
|
2678
|
-
const patchSha256 = sha256(winnerRun.diff);
|
|
2679
|
-
store.writeText(join(paths.finalDir, "patch.diff"), winnerRun.diff);
|
|
2680
|
-
const wstats = diffStats(winnerRun.diff);
|
|
2681
|
-
const hasDiff = winnerRun.diff.trim().length > 0;
|
|
2682
|
-
const blockers = winnerEvidence
|
|
2683
|
-
? winnerEvidence.findings.filter((f) => isBlocking(f)).length
|
|
2684
|
-
: 0;
|
|
2685
|
-
// Prose from an empty-diff winner is an answer, never a patch.
|
|
2686
2667
|
const winnerAnswer = winnerRun.answerText ?? "";
|
|
2687
|
-
const resultKind = hasDiff ? "patch" : winnerAnswer.length > 0 ? "answer" : "none";
|
|
2688
2668
|
// The winner's final MESSAGE is the human-facing answer and materializes
|
|
2689
2669
|
// for diff-ful runs too: the chat renders final/answer.md (the projection
|
|
2690
2670
|
// prefers it), never the arbitration summary — "Run … Winner: a01 …" is
|
|
@@ -2705,107 +2685,120 @@ export class Orchestrator {
|
|
|
2705
2685
|
answerText: winnerAnswer,
|
|
2706
2686
|
});
|
|
2707
2687
|
}
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
}, log);
|
|
2731
|
-
raceDeliveryReceipt = applied;
|
|
2732
|
-
store.writeYaml(join(paths.finalDir, "delivery_receipt.yaml"), applied);
|
|
2733
|
-
finalVerify = applied.finalVerify;
|
|
2734
|
-
if (applied.applied) {
|
|
2688
|
+
if (mutatingRun) {
|
|
2689
|
+
secretDiff.assertNoSecretLikeTokens("final patch diff", winnerRun.diff);
|
|
2690
|
+
const patchSha256 = sha256(winnerRun.diff);
|
|
2691
|
+
store.writeText(join(paths.finalDir, "patch.diff"), winnerRun.diff);
|
|
2692
|
+
const wstats = diffStats(winnerRun.diff);
|
|
2693
|
+
const hasDiff = winnerRun.diff.trim().length > 0;
|
|
2694
|
+
const blockers = winnerEvidence
|
|
2695
|
+
? winnerEvidence.findings.filter((f) => isBlocking(f)).length
|
|
2696
|
+
: 0;
|
|
2697
|
+
const resultKind = hasDiff ? "patch" : winnerAnswer.length > 0 ? "answer" : "none";
|
|
2698
|
+
// Only a fully verified, applyable success may auto-adopt; a not-verified
|
|
2699
|
+
// or needs-decision terminal remains an inspectable artifact.
|
|
2700
|
+
const adoptable = facts.lifecycle === "succeeded" &&
|
|
2701
|
+
facts.review === "approved" &&
|
|
2702
|
+
facts.checks !== "failed";
|
|
2703
|
+
let adopted = null;
|
|
2704
|
+
let applyState = "not_applied";
|
|
2705
|
+
let postTurnSha = null;
|
|
2706
|
+
let revertAnchorId = null;
|
|
2707
|
+
if (input.inPlace === true && hasDiff) {
|
|
2708
|
+
if (inPlaceWinner) {
|
|
2709
|
+
// Already live: the candidate ran in-place and wrote the tree itself.
|
|
2735
2710
|
adopted = true;
|
|
2736
|
-
applyState = "applied";
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2711
|
+
applyState = adoptable ? "applied" : "applied_review_blocked";
|
|
2712
|
+
// The pre-review fence excludes later user edits from the target.
|
|
2713
|
+
postTurnSha = earlyPostTurnSha;
|
|
2714
|
+
}
|
|
2715
|
+
else if (adoptable) {
|
|
2716
|
+
// Protected apply preserves the live tree or reports tree_mutated.
|
|
2717
|
+
const applied = await verifyAndDeliver(execRoot, winnerRun.diff, { mode: "apply", protectedApply: true }, gateSpecsFromContract(contract), (freshVerify) => {
|
|
2718
|
+
finalVerify = freshVerify;
|
|
2719
|
+
return finalVerifyBlocks(freshVerify)
|
|
2720
|
+
? (freshVerify.reason ?? "final verify failed before race adoption")
|
|
2721
|
+
: null;
|
|
2722
|
+
}, log);
|
|
2723
|
+
raceDeliveryReceipt = applied;
|
|
2724
|
+
store.writeYaml(join(paths.finalDir, "delivery_receipt.yaml"), applied);
|
|
2725
|
+
finalVerify = applied.finalVerify;
|
|
2726
|
+
if (applied.applied) {
|
|
2727
|
+
adopted = true;
|
|
2728
|
+
applyState = "applied";
|
|
2729
|
+
log.emit("work_product.adopted", {
|
|
2730
|
+
applied: true,
|
|
2731
|
+
patch_sha256: patchSha256,
|
|
2732
|
+
winner: winnerRun.attemptId,
|
|
2733
|
+
});
|
|
2734
|
+
try {
|
|
2735
|
+
postTurnSha = await snapshotTree(execRoot);
|
|
2736
|
+
}
|
|
2737
|
+
catch {
|
|
2738
|
+
postTurnSha = null;
|
|
2739
|
+
}
|
|
2740
|
+
revertAnchorId = createRevertAnchorFromPatchOrNull(execRoot, winnerRun.diff);
|
|
2744
2741
|
}
|
|
2745
|
-
|
|
2746
|
-
|
|
2742
|
+
else {
|
|
2743
|
+
adopted = false;
|
|
2744
|
+
applyState = "not_applied";
|
|
2745
|
+
deliveryFailureReason = applied.detail ?? "race adoption delivery was refused";
|
|
2746
|
+
facts = { ...facts, checks: "failed", reason: "checks_failed" };
|
|
2747
|
+
if (finalVerifyBlocks(finalVerify))
|
|
2748
|
+
finalVerifyFailed = true;
|
|
2749
|
+
log.emit("work_product.adopted", {
|
|
2750
|
+
applied: false,
|
|
2751
|
+
patch_sha256: patchSha256,
|
|
2752
|
+
detail: redactSecrets(applied.detail ?? "apply failed"),
|
|
2753
|
+
tree_mutated: applied.treeMutated,
|
|
2754
|
+
});
|
|
2747
2755
|
}
|
|
2748
|
-
revertAnchorId = createRevertAnchorFromPatchOrNull(execRoot, winnerRun.diff);
|
|
2749
|
-
}
|
|
2750
|
-
else {
|
|
2751
|
-
adopted = false;
|
|
2752
|
-
applyState = "not_applied";
|
|
2753
|
-
deliveryFailureReason = applied.detail ?? "race adoption delivery was refused";
|
|
2754
|
-
facts = { ...facts, checks: "failed", reason: "checks_failed" };
|
|
2755
|
-
if (finalVerifyBlocks(finalVerify))
|
|
2756
|
-
finalVerifyFailed = true;
|
|
2757
|
-
log.emit("work_product.adopted", {
|
|
2758
|
-
applied: false,
|
|
2759
|
-
patch_sha256: patchSha256,
|
|
2760
|
-
detail: redactSecrets(applied.detail ?? "apply failed"),
|
|
2761
|
-
tree_mutated: applied.treeMutated,
|
|
2762
|
-
});
|
|
2763
2756
|
}
|
|
2764
2757
|
}
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
: {
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2758
|
+
writeRaceDeliveryDecision(store, decisionPath, {
|
|
2759
|
+
decision: result.decision,
|
|
2760
|
+
facts,
|
|
2761
|
+
reviewVerified: actualReviewVerified,
|
|
2762
|
+
finalVerify,
|
|
2763
|
+
deliveryFailureReason,
|
|
2764
|
+
deliveryReceiptPath: raceDeliveryReceipt ? "final/delivery_receipt.yaml" : null,
|
|
2765
|
+
});
|
|
2766
|
+
if (inPlaceWinner && requestedSingleCandidate && adopted === true) {
|
|
2767
|
+
revertAnchorId = await createRevertAnchorOrNull(execRoot, preTurnSha, postTurnSha);
|
|
2768
|
+
}
|
|
2769
|
+
store.writeYaml(join(paths.finalDir, "work_product.yaml"), {
|
|
2770
|
+
id: newId("wp"),
|
|
2771
|
+
kind: input.create === true ? "new_repo" : "patch",
|
|
2772
|
+
source_task_id: taskId,
|
|
2773
|
+
producer_attempt_id: winnerRun.attemptId,
|
|
2774
|
+
...(raceDeliveryReceipt
|
|
2775
|
+
? { files: { delivery_receipt: "final/delivery_receipt.yaml" } }
|
|
2776
|
+
: {}),
|
|
2777
|
+
meta: {
|
|
2778
|
+
harness_id: winnerRun.harnessId,
|
|
2779
|
+
synthesis: synth,
|
|
2780
|
+
mode,
|
|
2781
|
+
// Artifact-only apply reads the same terminal axes as the daemon (D8).
|
|
2782
|
+
lifecycle: facts.lifecycle,
|
|
2783
|
+
outcome_facts: facts,
|
|
2784
|
+
review_verified: actualReviewVerified,
|
|
2785
|
+
budget_stopped: budgetStopped,
|
|
2786
|
+
patch_sha256: patchSha256,
|
|
2787
|
+
result_kind: resultKind,
|
|
2788
|
+
diffstat: {
|
|
2789
|
+
files: wstats.paths.length,
|
|
2790
|
+
additions: wstats.additions,
|
|
2791
|
+
deletions: wstats.deletions,
|
|
2792
|
+
},
|
|
2793
|
+
blockers,
|
|
2794
|
+
adopted,
|
|
2795
|
+
apply_state: applyState,
|
|
2796
|
+
pre_turn_sha: preTurnSha,
|
|
2797
|
+
post_turn_sha: postTurnSha,
|
|
2798
|
+
revert_anchor_id: revertAnchorId,
|
|
2800
2799
|
},
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
apply_state: applyState,
|
|
2804
|
-
pre_turn_sha: preTurnSha,
|
|
2805
|
-
post_turn_sha: postTurnSha,
|
|
2806
|
-
revert_anchor_id: revertAnchorId,
|
|
2807
|
-
},
|
|
2808
|
-
});
|
|
2800
|
+
});
|
|
2801
|
+
}
|
|
2809
2802
|
store.writeText(join(paths.finalDir, "summary.md"), renderSummary(runId, mode, { ...result.decision, facts }, evidences, synth.reason, actualReviewVerified));
|
|
2810
2803
|
// summary.md is a DIAGNOSTIC artifact only (V8/PLAN addendum 2): it no
|
|
2811
2804
|
// longer carries primary-output authority. A clean applyable success or a
|
|
@@ -2906,7 +2899,9 @@ export class Orchestrator {
|
|
|
2906
2899
|
});
|
|
2907
2900
|
}
|
|
2908
2901
|
}
|
|
2909
|
-
|
|
2902
|
+
if (mutatingRun && winnerRun) {
|
|
2903
|
+
log.emit("work_product.emitted", { winner: result.decision.winner });
|
|
2904
|
+
}
|
|
2910
2905
|
if (!isFailureTerminal) {
|
|
2911
2906
|
log.emit("run.completed", { lifecycle: facts.lifecycle, facts, reason: facts.reason });
|
|
2912
2907
|
}
|
|
@@ -2983,13 +2978,11 @@ export class Orchestrator {
|
|
|
2983
2978
|
* panel settles (resolve OR reject).
|
|
2984
2979
|
*/
|
|
2985
2980
|
reviewScoped(input) {
|
|
2986
|
-
const reviewHome = new WorkspaceManager(input.cwd).readOnlyHomeEnv();
|
|
2987
2981
|
return reviewCandidate({
|
|
2988
2982
|
...input,
|
|
2989
2983
|
reviewerTimeoutMs: input.reviewerTimeoutMs ?? reviewerTimeoutMs(this.config(input.cwd)),
|
|
2990
2984
|
transientRetryPolicy: input.transientRetryPolicy ?? transientRetryPolicy(this.config(input.cwd)),
|
|
2991
|
-
|
|
2992
|
-
}).finally(() => reviewHome.dispose());
|
|
2985
|
+
});
|
|
2993
2986
|
}
|
|
2994
2987
|
async reviewRuns(runs, reviewers, reviewVerified, reviewDir, cwd, contract, store, paths, log, ledger, taskId, signal, reservationEstimateUsd) {
|
|
2995
2988
|
const evidences = [];
|