@claudexor/orchestrator 3.2.1 → 3.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/attemptUsageCost.d.ts +7 -1
  2. package/dist/attemptUsageCost.d.ts.map +1 -1
  3. package/dist/attemptUsageCost.js +7 -1
  4. package/dist/attemptUsageCost.js.map +1 -1
  5. package/dist/budgetFailure.d.ts +1 -1
  6. package/dist/budgetFailure.d.ts.map +1 -1
  7. package/dist/budgetFailure.js.map +1 -1
  8. package/dist/candidateEvidence.d.ts +24 -0
  9. package/dist/candidateEvidence.d.ts.map +1 -1
  10. package/dist/candidateEvidence.js +26 -0
  11. package/dist/candidateEvidence.js.map +1 -1
  12. package/dist/credential-profile-rotation.d.ts.map +1 -1
  13. package/dist/credential-profile-rotation.js +23 -3
  14. package/dist/credential-profile-rotation.js.map +1 -1
  15. package/dist/credential-profiles.d.ts +60 -1
  16. package/dist/credential-profiles.d.ts.map +1 -1
  17. package/dist/credential-profiles.js +92 -1
  18. package/dist/credential-profiles.js.map +1 -1
  19. package/dist/delegatedHome.d.ts +164 -0
  20. package/dist/delegatedHome.d.ts.map +1 -0
  21. package/dist/delegatedHome.js +180 -0
  22. package/dist/delegatedHome.js.map +1 -0
  23. package/dist/diffReview.d.ts +7 -0
  24. package/dist/diffReview.d.ts.map +1 -1
  25. package/dist/diffReview.js +1 -0
  26. package/dist/diffReview.js.map +1 -1
  27. package/dist/index.d.ts +1 -1
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +1 -1
  30. package/dist/index.js.map +1 -1
  31. package/dist/orchestrator.d.ts +27 -2
  32. package/dist/orchestrator.d.ts.map +1 -1
  33. package/dist/orchestrator.js +135 -103
  34. package/dist/orchestrator.js.map +1 -1
  35. package/dist/requestRequirements.d.ts +15 -2
  36. package/dist/requestRequirements.d.ts.map +1 -1
  37. package/dist/requestRequirements.js +17 -3
  38. package/dist/requestRequirements.js.map +1 -1
  39. package/dist/routing-failure.d.ts +35 -1
  40. package/dist/routing-failure.d.ts.map +1 -1
  41. package/dist/routing-failure.js +70 -3
  42. package/dist/routing-failure.js.map +1 -1
  43. package/dist/runFacts.d.ts.map +1 -1
  44. package/dist/runFacts.js +4 -13
  45. package/dist/runFacts.js.map +1 -1
  46. package/dist/runTerminalResults.d.ts +26 -1
  47. package/dist/runTerminalResults.d.ts.map +1 -1
  48. package/dist/runTerminalResults.js +34 -4
  49. package/dist/runTerminalResults.js.map +1 -1
  50. package/dist/runTerminals.d.ts +2 -2
  51. package/dist/runTerminals.d.ts.map +1 -1
  52. package/dist/runTerminals.js +2 -2
  53. package/dist/runTerminals.js.map +1 -1
  54. package/package.json +17 -17
@@ -1,10 +1,11 @@
1
1
  import { existsSync } from "node:fs";
2
- import { effectiveAuthPreference, observeNativeSessionEvent, preflightCredentialProfile, preflightDefaultSubject, probeCredentialProfileStatus, profileHeadroomBreach, profileStatusAdmits, resolveCredentialProfile, resumeSessionForProfile, rotateSpecOnTypedLimit, selectedProfileAvailability, staticRotationCandidates, } from "./credential-profiles.js";
2
+ import { effectiveAuthPreference, observeNativeSessionEvent, preflightCredentialProfile, preflightDefaultSubject, probeCredentialProfileStatus, profileHeadroomBreach, profileStatusAdmits, resolveCredentialProfile, resumeSessionForProfile, rotateSpecOnTypedLimit, selectedProfileAvailability, staticRotationCandidates, vendorVerifiedProfileStatus, } from "./credential-profiles.js";
3
3
  import { writeRunTelemetryArtifact } from "./runTelemetryWriter.js";
4
4
  import { buildFileBackedSynthesisInput, materializeWinnerOutputs, stageFileBackedContext, writeCandidateAttemptArtifacts, } from "./candidateOutputs.js";
5
5
  import { processAttemptUsage } from "./attemptUsage.js";
6
+ import { appliedAttemptFacts, assertDelegatedEvidence, confinementNotice, externallyConfinedLane, scopedHarnessHome, } from "./delegatedHome.js";
6
7
  import * as AC from "./attemptUsageCost.js";
7
- import { candidateRoster, convergenceOutcomeFacts, isWorkingCandidate, partitionCandidates, toCandidateEvidence, } from "./candidateEvidence.js";
8
+ import { candidateRoster, convergenceOutcomeFacts, isWorkingCandidate, partitionCandidates, toCandidateEvidence, unanimousDeclaredFailure, } from "./candidateEvidence.js";
8
9
  import { capabilityIntents } from "@claudexor/gateway";
9
10
  import { policyFindings } from "./policyFindings.js";
10
11
  import { join } from "node:path";
@@ -17,7 +18,7 @@ import { assertRouteModelsAllowed } from "./modelGovernance.js";
17
18
  import { governRouteEffort } from "./effortGovernance.js";
18
19
  import { isFullAccess, RequestRequirementsResolver } from "./requestRequirements.js";
19
20
  import { activateDelegationParent } from "./delegation-parent-activation.js";
20
- import { routingFailureClassification } from "./routing-failure.js";
21
+ import { writeRoutingFailureTerminal } from "./routing-failure.js";
21
22
  export { routingFailureClassification } from "./routing-failure.js";
22
23
  import { runBounded } from "./run-bounded.js";
23
24
  import { planPrompt } from "./plan-prompt.js";
@@ -26,7 +27,7 @@ import { resolveRunInputDefaults } from "./run-input-resolution.js";
26
27
  import { beginAnnouncedRun } from "./runEventLog.js";
27
28
  import { arbitrationBudgetOptions, decisionBudgetSummary } from "./decisionBudget.js";
28
29
  import { buildRevisePrompt } from "./revisePrompt.js";
29
- import { cancelledResult, failTerminally, guardAnnouncedRun, writeFailure, } from "./runTerminals.js";
30
+ import { cancelledResult, declaredFailure, failTerminally, guardAnnouncedRun, writeFailure, } from "./runTerminals.js";
30
31
  import { budgetFailureRecord, classifyBudgetFailure } from "./budgetFailure.js";
31
32
  import { assertOutputSchemaCompiles, finalizeStructuredOutput } from "./structuredOutput.js";
32
33
  import { transientRetryDelayMs, promptWithEngineConstraints, sleep, redactHarnessEvent, harnessEventPayload, safeErrorMessage, renderSummary, observeBudgetSignals, rotateOnStall, recordCleanAttemptMetrics, envInheritance, transientRetryPolicy, reviewerTimeoutMs, harnessInactivityTimeoutMs, observeAuthSwitch, emitPrimaryDivergence, emitPoolDegraded, deliveryRefusalFailure, winnerNeedsHuman, writeRaceDeliveryDecision, } from "./runSupport.js";
@@ -409,8 +410,19 @@ export class Orchestrator {
409
410
  const policy = this.config(repoRoot)?.global.harnesses?.[harnessId]?.profile_policy;
410
411
  return policy ?? { limit_action: "fail", rotation_eligible: [], headroom_threshold: 0.9 };
411
412
  }
413
+ /** The quota poller's authenticated vendor evidence for THIS decision epoch,
414
+ * read once so every profile in the epoch is judged against one observation
415
+ * set. This is what makes profile readiness at admission mean the same thing
416
+ * it means on the Accounts card (INV-135 honesty). */
417
+ vendorQuotaObservations() {
418
+ return {
419
+ snapshots: this.deps.quotaSnapshots?.() ?? [],
420
+ absences: this.deps.quotaAbsences?.() ?? [],
421
+ };
422
+ }
412
423
  /** Fresh profile readiness for one rotation decision epoch. Accounts uses
413
- * the same probe wrapper + admission predicate when projecting next_up. */
424
+ * the same probe wrapper + vendor overlay + admission predicate when
425
+ * projecting next_up. */
414
426
  async readyProfileIdsForRotation(input, harnessId, current, excluded = new Set()) {
415
427
  const profiles = staticRotationCandidates({
416
428
  registry: this.config(input.repoRoot)?.global.credential_profiles ?? [],
@@ -420,9 +432,12 @@ export class Orchestrator {
420
432
  excluded,
421
433
  });
422
434
  const adapter = this.deps.registry.get(harnessId);
435
+ const quota = this.vendorQuotaObservations();
423
436
  const entries = await Promise.all(profiles.map(async (profile) => ({
424
437
  profile,
425
- status: await probeCredentialProfileStatus(profile, adapter?.probeCredentialProfile?.bind(adapter)),
438
+ // Rotating INTO a profile the vendor has already rejected would spend a
439
+ // whole attempt to rediscover the 401 the poller reported a minute ago.
440
+ status: vendorVerifiedProfileStatus(await probeCredentialProfileStatus(profile, adapter?.probeCredentialProfile?.bind(adapter)), quota),
426
441
  })));
427
442
  return new Set(entries
428
443
  .filter(({ profile, status }) => profileStatusAdmits(profile, status))
@@ -580,6 +595,9 @@ export class Orchestrator {
580
595
  dropped.push(detail);
581
596
  droppedLanes.push({ harnessId, stage, detail });
582
597
  };
598
+ // One observation set for the whole admission pass, so two lanes cannot be
599
+ // judged against different vendor epochs.
600
+ const vendorQuota = this.vendorQuotaObservations();
583
601
  for (const id of ids) {
584
602
  const adapter = this.deps.registry.get(id);
585
603
  if (!adapter) {
@@ -643,6 +661,11 @@ export class Orchestrator {
643
661
  profileId: this.effectiveProfileId(input, id),
644
662
  harnessId: id,
645
663
  probe: profileAdapter?.probeCredentialProfile?.bind(profileAdapter),
664
+ // `verification: passed` from the local store only means a login file
665
+ // is present. The poller's authenticated vendor call is the only
666
+ // liveness evidence we have, and admission is the surface that must
667
+ // act on it — otherwise the run dispatches into a revoked token.
668
+ quota: vendorQuota,
646
669
  });
647
670
  if (profileVerdict !== null) {
648
671
  if (profileVerdict === "available") {
@@ -675,7 +698,7 @@ export class Orchestrator {
675
698
  const readOnlyIntent = intent === "plan" || intent === "spec" || intent === "explain" || intent === "audit";
676
699
  const requiredAccess = this.requestRequirements.adapterAccess(intent, manifest.capabilities.implementation_transport, readOnlyIntent
677
700
  ? "readonly"
678
- : (input.access ?? this.config(input.repoRoot).trust.access_default));
701
+ : (input.access ?? this.config(input.repoRoot).trust.access_default), externallyConfinedLane(input.delegated === true));
679
702
  const accessSupported = !requiredAccess || manifest.access_profiles_supported.includes(requiredAccess);
680
703
  const webSupport = manifest.capabilities.web_policy;
681
704
  // Match routeSpecKnobs: a per-harness web default upgrades run-level auto,
@@ -1381,11 +1404,28 @@ export class Orchestrator {
1381
1404
  return { pointerLine: null };
1382
1405
  }
1383
1406
  }
1384
- async runCandidateInEnvelope(routed, envelope, attemptId, label, contract, prompt, store, paths, wsm, ledger, access = "workspace_write", onHarnessEvent, signal, modelHint, effortHint, intent = "implement", log, effectiveWebMode, interaction, budgetGuard, runInput, streamDeltas = false, fileBackedContext,
1407
+ /**
1408
+ * The ONE place an attempt's HOME and OS boundary are decided.
1409
+ *
1410
+ * Called by every caller of `runCandidateInEnvelope` INSIDE that caller's
1411
+ * try, so a delegated refusal is caught by the same catch that writes the
1412
+ * attempt record, and so the decided home is in scope there.
1413
+ */
1414
+ harnessHomeFor(wsm, envelope, routed, runInput) {
1415
+ // Refuses (never silently degrades) when a delegated run cannot be confined.
1416
+ return scopedHarnessHome(wsm, envelope, envelope.worktree_path === envelope.repo_root, runInput?.delegated === true, routed.adapterAccess);
1417
+ }
1418
+ async runCandidateInEnvelope(routed, envelope, attemptId, label, contract, prompt, store, paths, wsm, ledger, access, onHarnessEvent, signal, modelHint, effortHint, intent, log, effectiveWebMode, interaction, budgetGuard, runInput, streamDeltas, fileBackedContext,
1385
1419
  /** D-16d: when set, the mechanical continuation checkpoint pointer for a
1386
1420
  * one-shot fresh-session continuation — appended to the prompt so the model
1387
1421
  * (and the offline fake) re-grounds in the exhausted attempt's partial work. */
1388
- continuationPointer) {
1422
+ continuationPointer,
1423
+ /** Decided by `harnessHomeFor` in the CALLER, so the per-attempt applied
1424
+ * facts exist in the caller's catch too: an attempt that ran and then threw
1425
+ * must record what it ran under, not just why it stopped. REQUIRED (no
1426
+ * default): a silent fallback here would spawn a delegated child on the
1427
+ * operator's real home while the record still claimed confinement. */
1428
+ harnessHome) {
1389
1429
  const adapter = routed.adapter;
1390
1430
  const knobs = this.routeSpecKnobs(routed, contract, modelHint, effortHint);
1391
1431
  // Isolated scoped-home sessions are never retained after disposal.
@@ -1406,7 +1446,9 @@ export class Orchestrator {
1406
1446
  intent,
1407
1447
  // Engine-derived read-only prompt constraints: protected/auto-protected
1408
1448
  // paths PLUS the exact typed gate argv the run will execute (QA-022 FIX B).
1409
- prompt: promptWithEngineConstraints([prompt, laneContinuity?.pointerLine, continuationPointer]
1449
+ prompt: promptWithEngineConstraints(
1450
+ // A child running without an OS boundary is TOLD so (disclosure 2 of 3).
1451
+ [prompt, confinementNotice(harnessHome), laneContinuity?.pointerLine, continuationPointer]
1410
1452
  .filter((s) => typeof s === "string" && s.length > 0)
1411
1453
  .join("\n\n"), contract.constraints.protected_paths, contract.constraints.auto_protected_paths, contract.constraints.protected_path_approvals, contract.tests.commands),
1412
1454
  attachments: runInput?.attachments ?? [],
@@ -1428,9 +1470,14 @@ export class Orchestrator {
1428
1470
  ...(inPlaceEnvelope && sessionFields?.resume_session_id
1429
1471
  ? { resume_session_id: sessionFields.resume_session_id }
1430
1472
  : {}),
1431
- // Scoped harness home only for isolated envelopes; in-place runs use the
1432
- // native environment so the resumed vendor session is actually reachable.
1433
- ...(inPlaceEnvelope ? {} : { env: wsm.envFor(envelope) }),
1473
+ // Scoped harness home for isolated envelopes AND for every delegated run;
1474
+ // an ordinary in-place run keeps the native environment so the resumed
1475
+ // vendor session is reachable. See scopedHarnessHome for the cost a
1476
+ // delegated in-place attempt pays for that confinement.
1477
+ ...(harnessHome.env ? { env: harnessHome.env } : {}),
1478
+ // The OS boundary itself. The shared CLI run loop wraps the argv with it;
1479
+ // no adapter opts in, so no adapter can forget.
1480
+ confinement: harnessHome.confinement,
1434
1481
  raw_context_packet: rawContextPacket,
1435
1482
  stream_deltas: streamDeltas,
1436
1483
  });
@@ -1441,6 +1488,13 @@ export class Orchestrator {
1441
1488
  const workEnvelope = this.workReportEnvelopeFor(routed, contract, Boolean(interaction));
1442
1489
  const workReportMode = this.applyWorkEnvelope(spec, workEnvelope);
1443
1490
  const inactivityMs = harnessInactivityTimeoutMs(this.config(contract.repo.root));
1491
+ // Named once: the attempt record, the CandidateRun and the terminal gate all
1492
+ // read the SAME applied facts rather than three re-derivations. Derived
1493
+ // from the LIVE spec, so a W5.4 failover that rotates the credential
1494
+ // mid-attempt is re-recorded at the point it becomes true — the receipt
1495
+ // must name the profile that ran, never the one the request asked for.
1496
+ const appliedNow = () => appliedAttemptFacts(harnessHome, spec.access, spec.credential_profile?.profile_id ?? runInput?.credentialProfileId ?? null);
1497
+ let applied = appliedNow();
1444
1498
  const attemptStartedMs = Date.now();
1445
1499
  const budgetSignalState = { quotaPressureDisclosed: false };
1446
1500
  const triedProfiles = new Set(); // W5.4 failover: each profile at most once
@@ -1663,6 +1717,7 @@ export class Orchestrator {
1663
1717
  });
1664
1718
  if (rotated) {
1665
1719
  spec = rotated;
1720
+ applied = appliedNow();
1666
1721
  errors.length = 0;
1667
1722
  harnessErrored = false;
1668
1723
  continue;
@@ -1812,6 +1867,10 @@ export class Orchestrator {
1812
1867
  ...(secretDiffRefusal ? { secret_diff_refusal: secretDiffRefusal } : {}),
1813
1868
  gates: gates.map((g) => ({ id: g.id, status: g.status })),
1814
1869
  branch: envelope.branch_name,
1870
+ // Applied facts, not promises: the caller of a delegated run
1871
+ // verifies the confinement it asked for instead of trusting it.
1872
+ // Built by the SAME shape the failure path writes.
1873
+ ...applied,
1815
1874
  },
1816
1875
  }), {
1817
1876
  totalUsd: cost,
@@ -1835,6 +1894,7 @@ export class Orchestrator {
1835
1894
  telemetry,
1836
1895
  ...(secretDiffRefusal ? { secretDiffRefusal } : {}),
1837
1896
  outcomeClass: finalized.outcomeClass,
1897
+ applied,
1838
1898
  };
1839
1899
  }
1840
1900
  interactionChannelFor(input, log, runId, taskId, attemptId, harnessId,
@@ -1963,26 +2023,11 @@ export class Orchestrator {
1963
2023
  }
1964
2024
  catch (err) {
1965
2025
  const message = safeErrorMessage(err);
1966
- store.writeText(join(paths.contextDir, "context_error.md"), `# Routing Error\n\n${message}\n`);
1967
- // A routing preflight refusal is a config_error, not harness_unavailable
1968
- // (A-1/#22): classify identically across every strategy's routing catch.
1969
- const routingFailure = routingFailureClassification(err);
1970
- writeFailure(store, paths, {
1971
- phase: "routing",
1972
- category: routingFailure.category,
1973
- safeMessage: message,
1974
- runDir: paths.root,
1975
- ...(routingFailure.nextActions ? { nextActions: routingFailure.nextActions } : {}),
1976
- });
1977
- store.writeText(join(paths.finalDir, "summary.md"), `# Run ${runId} (${mode})\n\n- Lifecycle: failed\n- Phase: routing\n\n${message}\n`);
1978
- log.emit("output.ready", { kind: "summary", path: "final/summary.md", state: "diagnostic" });
1979
- log.emit("run.failed", {
1980
- lifecycle: "failed",
1981
- facts: makeOutcomeFacts("failed", { reason: "harness_failed" }),
1982
- reason: "harness_failed",
1983
- phase: "routing",
1984
- error: message,
1985
- failure_ref: "final/failure.yaml",
2026
+ writeRoutingFailureTerminal(store, paths, log, {
2027
+ runId,
2028
+ modeLabel: mode,
2029
+ message,
2030
+ err,
1986
2031
  });
1987
2032
  return {
1988
2033
  runId,
@@ -2104,6 +2149,9 @@ export class Orchestrator {
2104
2149
  const knobs = this.routeSpecKnobs(slot.routed, contract, downgradeModel ?? undefined, input.effort);
2105
2150
  const effectiveWeb = this.discloseWebUpgrade(log, slot.routed, knobs.webPolicy, slot.attemptId);
2106
2151
  let envelope;
2152
+ // Declared OUTSIDE the try so the catch below can state what the attempt
2153
+ // actually ran under, not merely that it stopped.
2154
+ let harnessHome;
2107
2155
  try {
2108
2156
  log.emit("harness.started", {
2109
2157
  harness_id: adapter.id,
@@ -2123,6 +2171,7 @@ export class Orchestrator {
2123
2171
  requestedSingleCandidate &&
2124
2172
  slot.routed.implementationTransport !== "git_patch_envelope",
2125
2173
  });
2174
+ harnessHome = this.harnessHomeFor(wsm, envelope, slot.routed, input);
2126
2175
  const run = await this.runCandidateInEnvelope(slot.routed, envelope, slot.attemptId, slot.label, contract, input.prompt, store, paths, wsm, ledger, candidateAccess, (ev) => {
2127
2176
  const safeEv = redactHarnessEvent(ev);
2128
2177
  safeInvoke(input.onHarnessEvent, safeEv);
@@ -2133,7 +2182,8 @@ export class Orchestrator {
2133
2182
  return false;
2134
2183
  budgetStopped = true;
2135
2184
  return true;
2136
- }, input, requestedSingleCandidate);
2185
+ }, input, requestedSingleCandidate, // W-C4 deltas: single-candidate chat lane only (racing = noise x N)
2186
+ undefined, undefined, harnessHome);
2137
2187
  ledger.settle(slot.leaseId, attemptUsageCostSettlement(run.cost, run.costEstimated, run.attemptId, run.harnessId, run.telemetry.authMode, run.telemetry.usageCost));
2138
2188
  log.emit("harness.completed", {
2139
2189
  harness_id: adapter.id,
@@ -2209,7 +2259,7 @@ export class Orchestrator {
2209
2259
  return false;
2210
2260
  budgetStopped = true;
2211
2261
  return true;
2212
- }, input, requestedSingleCandidate, undefined, packet.pointerLine ?? undefined);
2262
+ }, input, requestedSingleCandidate, undefined, packet.pointerLine ?? undefined, harnessHome);
2213
2263
  ledger.settle(contLeaseId, attemptUsageCostSettlement(contRun.cost, contRun.costEstimated, contRun.attemptId, contRun.harnessId, contRun.telemetry.authMode, contRun.telemetry.usageCost));
2214
2264
  log.emit("harness.completed", {
2215
2265
  harness_id: adapter.id,
@@ -2255,6 +2305,7 @@ export class Orchestrator {
2255
2305
  const failureCost = AC.attemptFailureCost(err, "post-stream-error", 0);
2256
2306
  ledger.settle(slot.leaseId, failureCost.settlement);
2257
2307
  const message = safeErrorMessage(err);
2308
+ const declared = declaredFailure(err);
2258
2309
  const infraPhase = envelope === undefined ? "workspace" : "harness";
2259
2310
  log.emit("harness.completed", {
2260
2311
  harness_id: adapter.id,
@@ -2263,7 +2314,7 @@ export class Orchestrator {
2263
2314
  error: message,
2264
2315
  phase: infraPhase,
2265
2316
  });
2266
- store.writeYaml(join(paths.attemptsDir, slot.attemptId, "attempt.yaml"), AC.attemptFailureRecord(slot.attemptId, adapter.id, failureCost, infraPhase, message));
2317
+ store.writeYaml(join(paths.attemptsDir, slot.attemptId, "attempt.yaml"), AC.attemptFailureRecord(slot.attemptId, adapter.id, failureCost, infraPhase, message, appliedAttemptFacts(harnessHome, slot.routed.adapterAccess, this.effectiveProfileId(input, adapter.id))));
2267
2318
  runsBySlot[slotIdx] = {
2268
2319
  attemptId: slot.attemptId,
2269
2320
  harnessId: adapter.id,
@@ -2280,6 +2331,11 @@ export class Orchestrator {
2280
2331
  slot.routed.delegationRequirement,
2281
2332
  ], knobs.model),
2282
2333
  infraPhase,
2334
+ // Keep a TYPED pre-spawn refusal (spent quota window + its reset)
2335
+ // alive past this catch; `message` alone would force the terminal to
2336
+ // read prose back out.
2337
+ ...(declared.code ? { declaredFailure: declared } : {}),
2338
+ applied: appliedAttemptFacts(harnessHome, slot.routed.adapterAccess, this.effectiveProfileId(input, adapter.id)),
2283
2339
  };
2284
2340
  }
2285
2341
  finally {
@@ -2289,11 +2345,18 @@ export class Orchestrator {
2289
2345
  };
2290
2346
  await runBounded(slots, Math.min(slots.length, MAX_PARALLEL_CANDIDATES), runSlot);
2291
2347
  const runs = runsBySlot.filter((r) => r !== undefined);
2348
+ // Fail-closed terminal: a delegated mutating run whose attempts cannot state
2349
+ // their applied confinement refuses instead of passing.
2350
+ assertDelegatedEvidence(input.delegated === true, candidateAccess, runs);
2292
2351
  const cancelledCandidates = () => runs.map((r) => ({
2293
2352
  attemptId: r.attemptId,
2294
2353
  harnessId: r.harnessId,
2295
2354
  status: gatesPassed(r.gates) && !r.errored ? "green" : "red",
2296
2355
  }));
2356
+ /** The one cancellation terminal this race can reach, from any of its three
2357
+ * abort checks. Every argument is re-read at call time, exactly as it was
2358
+ * when each check spelled the whole call out for itself. */
2359
+ const cancelledRaceResult = () => cancelledResult(log, runId, taskId, mode, paths.root, cancelledCandidates(), () => this.writeRunTelemetry(store, paths, contract, runId, taskId, mode, candidateRoster(runs), null), ledger.spend(), input.signal, store);
2297
2360
  // Revert divergence fence for the single-candidate in-place path: the
2298
2361
  // candidate mutated the LIVE tree during execution above, so the post-turn
2299
2362
  // snapshot must be taken NOW — before review/synthesis/arbitration, which can
@@ -2311,7 +2374,7 @@ export class Orchestrator {
2311
2374
  }
2312
2375
  if (input.signal?.aborted) {
2313
2376
  await disposeReviewEnvelopes();
2314
- return cancelledResult(log, runId, taskId, mode, paths.root, cancelledCandidates(), () => this.writeRunTelemetry(store, paths, contract, runId, taskId, mode, candidateRoster(runs), null), ledger.spend(), input.signal, store);
2377
+ return cancelledRaceResult();
2315
2378
  }
2316
2379
  const failedDelegation = delegateFailure.dominantRaceCandidateFailure(runs);
2317
2380
  if (failedDelegation) {
@@ -2412,15 +2475,21 @@ export class Orchestrator {
2412
2475
  // harness cause (timeout, rate limit, crash, config) gets remediation that
2413
2476
  // fits it, instead of a doomed "Check harness authentication".
2414
2477
  const harnessCategory = dominantHarnessFailureCategory(first.telemetry.transientFailures);
2478
+ // A run speaks with a candidate's TYPED refusal only when EVERY candidate
2479
+ // died of the same one (candidateEvidence owns that rule); mixed causes
2480
+ // keep the honest harness terminal.
2481
+ const unanimous = unanimousDeclaredFailure(runs);
2415
2482
  writeFailure(store, paths, {
2416
2483
  phase,
2417
- category: phase === "workspace" ? "project" : "harness_error",
2484
+ category: unanimous?.category ?? (phase === "workspace" ? "project" : "harness_error"),
2485
+ code: unanimous?.code ?? null,
2418
2486
  harnessId: first.harnessId,
2419
2487
  attemptId: first.attemptId,
2420
2488
  safeMessage: rootCause,
2421
2489
  rawDetailRef: `attempts/${first.attemptId}/attempt.yaml`,
2422
2490
  eventRefs: existingEventRefs,
2423
2491
  runDir: paths.root,
2492
+ resetsAt: unanimous?.resetsAt ?? null,
2424
2493
  nextActions: first.secretDiffRefusal
2425
2494
  ? secretDiff.secretDiffNextActions(first.secretDiffRefusal)
2426
2495
  : phase === "workspace"
@@ -2496,7 +2565,7 @@ export class Orchestrator {
2496
2565
  await disposeReviewEnvelopes();
2497
2566
  }
2498
2567
  if (input.signal?.aborted) {
2499
- return cancelledResult(log, runId, taskId, mode, paths.root, cancelledCandidates(), () => this.writeRunTelemetry(store, paths, contract, runId, taskId, mode, candidateRoster(runs), null), ledger.spend(), input.signal, store);
2568
+ return cancelledRaceResult();
2500
2569
  }
2501
2570
  // Synthesis: if worthwhile, run a synthesizer as a NEW, re-checked candidate.
2502
2571
  const synth = decideSynthesis(evidences, input.synthesis ?? "auto");
@@ -2532,11 +2601,12 @@ export class Orchestrator {
2532
2601
  dirtyPolicy: "snapshot",
2533
2602
  accessProfile: candidateAccess,
2534
2603
  });
2604
+ const synthHome = this.harnessHomeFor(wsm, envelope, synthRouted, input);
2535
2605
  const run = await this.runCandidateInEnvelope(synthRouted, envelope, "synth", "Synthesis", contract, synthesisInput.prompt, store, paths, wsm, ledger, candidateAccess, (ev) => {
2536
2606
  const safeEv = redactHarnessEvent(ev);
2537
2607
  safeInvoke(input.onHarnessEvent, safeEv);
2538
2608
  log.emit("harness.event", harnessEventPayload(synthAdapter.id, "synth", safeEv));
2539
- }, input.signal, undefined, input.effort, "synthesize", log, effectiveWeb, this.interactionChannelFor(input, log, runId, taskId, "synth", synthAdapter.id, synthRouted.supportsInteractive), undefined, input, false, synthesisInput.content);
2609
+ }, input.signal, undefined, input.effort, "synthesize", log, effectiveWeb, this.interactionChannelFor(input, log, runId, taskId, "synth", synthAdapter.id, synthRouted.supportsInteractive), undefined, input, false, synthesisInput.content, undefined, synthHome);
2540
2610
  ledger.settle(lease.lease?.lease_id ?? "", attemptUsageCostSettlement(run.cost, run.costEstimated, run.attemptId, run.harnessId, run.telemetry.authMode, run.telemetry.usageCost));
2541
2611
  reviewEnvelopes.push(envelope);
2542
2612
  envelope = undefined;
@@ -2572,7 +2642,7 @@ export class Orchestrator {
2572
2642
  }
2573
2643
  }
2574
2644
  if (input.signal?.aborted) {
2575
- return cancelledResult(log, runId, taskId, mode, paths.root, cancelledCandidates(), () => this.writeRunTelemetry(store, paths, contract, runId, taskId, mode, candidateRoster(runs), null), ledger.spend(), input.signal, store);
2645
+ return cancelledRaceResult();
2576
2646
  }
2577
2647
  let result;
2578
2648
  try {
@@ -3209,26 +3279,11 @@ export class Orchestrator {
3209
3279
  }
3210
3280
  catch (err) {
3211
3281
  const message = safeErrorMessage(err);
3212
- store.writeText(join(paths.contextDir, "context_error.md"), `# Routing Error\n\n${message}\n`);
3213
- // A routing preflight refusal is a config_error, not harness_unavailable
3214
- // (A-1/#22): classify identically across every strategy's routing catch.
3215
- const routingFailure = routingFailureClassification(err);
3216
- writeFailure(store, paths, {
3217
- phase: "routing",
3218
- category: routingFailure.category,
3219
- safeMessage: message,
3220
- runDir: paths.root,
3221
- ...(routingFailure.nextActions ? { nextActions: routingFailure.nextActions } : {}),
3222
- });
3223
- store.writeText(join(paths.finalDir, "summary.md"), `# Run ${runId} (${mode})\n\n- Lifecycle: failed\n- Phase: routing\n\n${message}\n`);
3224
- log.emit("output.ready", { kind: "summary", path: "final/summary.md", state: "diagnostic" });
3225
- log.emit("run.failed", {
3226
- lifecycle: "failed",
3227
- facts: makeOutcomeFacts("failed", { reason: "harness_failed" }),
3228
- reason: "harness_failed",
3229
- phase: "routing",
3230
- error: message,
3231
- failure_ref: "final/failure.yaml",
3282
+ writeRoutingFailureTerminal(store, paths, log, {
3283
+ runId,
3284
+ modeLabel: mode,
3285
+ message,
3286
+ err,
3232
3287
  });
3233
3288
  return {
3234
3289
  spendUsd: ledger.spend(),
@@ -3379,7 +3434,9 @@ export class Orchestrator {
3379
3434
  const knobs = this.routeSpecKnobs(routed, contract, undefined, input.effort);
3380
3435
  const effectiveWeb = this.discloseWebUpgrade(log, routed, knobs.webPolicy, attemptId);
3381
3436
  let run;
3437
+ let harnessHome;
3382
3438
  try {
3439
+ harnessHome = this.harnessHomeFor(wsm, envelope, routed, input);
3383
3440
  log.emit("harness.started", {
3384
3441
  harness_id: adapter.id,
3385
3442
  attempt_id: attemptId,
@@ -3393,7 +3450,8 @@ export class Orchestrator {
3393
3450
  }, input.signal, undefined, input.effort, "repair", log, effectiveWeb, this.interactionChannelFor(input, log, runId, taskId, attemptId, adapter.id, routed.supportsInteractive), (streamedUsd) => {
3394
3451
  ledger.updateHold(lease.lease?.lease_id ?? "", streamedUsd);
3395
3452
  return ledger.tier() === "hard";
3396
- }, input, true);
3453
+ }, input, true, // convergence runs one candidate: live deltas on (W-C4)
3454
+ undefined, undefined, harnessHome);
3397
3455
  ledger.settle(lease.lease?.lease_id ?? "", attemptUsageCostSettlement(run.cost, run.costEstimated, run.attemptId, run.harnessId, run.telemetry.authMode, run.telemetry.usageCost));
3398
3456
  log.emit("harness.completed", {
3399
3457
  harness_id: adapter.id,
@@ -3416,8 +3474,9 @@ export class Orchestrator {
3416
3474
  status: "failed",
3417
3475
  error: message,
3418
3476
  });
3419
- store.writeYaml(join(paths.attemptsDir, attemptId, "attempt.yaml"), AC.attemptFailureRecord(attemptId, adapter.id, failureCost, "harness", message));
3477
+ store.writeYaml(join(paths.attemptsDir, attemptId, "attempt.yaml"), AC.attemptFailureRecord(attemptId, adapter.id, failureCost, "harness", message, appliedAttemptFacts(harnessHome, routed.adapterAccess, this.effectiveProfileId(input, adapter.id))));
3420
3478
  run = {
3479
+ applied: appliedAttemptFacts(harnessHome, routed.adapterAccess, this.effectiveProfileId(input, adapter.id)),
3421
3480
  attemptId,
3422
3481
  harnessId: adapter.id,
3423
3482
  label: `Attempt ${attempt}`,
@@ -3431,6 +3490,9 @@ export class Orchestrator {
3431
3490
  };
3432
3491
  }
3433
3492
  lastRun = run;
3493
+ // Fail-closed twin of the candidate lane's gate: this loop terminalizes
3494
+ // per attempt, so the proof is spent per attempt.
3495
+ assertDelegatedEvidence(input.delegated === true, convergenceAccess, [run]);
3434
3496
  attemptTelemetries.push({ attemptId, harnessId: adapter.id, telemetry: run.telemetry });
3435
3497
  // Cancellation/deadline keeps priority over a belt failure finalized concurrently.
3436
3498
  if (input.signal?.aborted)
@@ -4032,26 +4094,11 @@ export class Orchestrator {
4032
4094
  catch (err) {
4033
4095
  roHome.dispose();
4034
4096
  const message = safeErrorMessage(err);
4035
- store.writeText(join(paths.contextDir, "context_error.md"), `# Routing Error\n\n${message}\n`);
4036
- // A routing preflight refusal is a config_error, not harness_unavailable
4037
- // (A-1/#22): classify identically across every strategy's routing catch.
4038
- const routingFailure = routingFailureClassification(err);
4039
- writeFailure(store, paths, {
4040
- phase: "routing",
4041
- category: routingFailure.category,
4042
- safeMessage: message,
4043
- runDir: paths.root,
4044
- ...(routingFailure.nextActions ? { nextActions: routingFailure.nextActions } : {}),
4045
- });
4046
- store.writeText(join(paths.finalDir, "summary.md"), `# Run ${runId} (plan)\n\n- Lifecycle: failed\n- Phase: routing\n\n${message}\n`);
4047
- log.emit("output.ready", { kind: "summary", path: "final/summary.md", state: "diagnostic" });
4048
- log.emit("run.failed", {
4049
- lifecycle: "failed",
4050
- facts: makeOutcomeFacts("failed", { reason: "harness_failed" }),
4051
- reason: "harness_failed",
4052
- phase: "routing",
4053
- error: message,
4054
- failure_ref: "final/failure.yaml",
4097
+ writeRoutingFailureTerminal(store, paths, log, {
4098
+ runId,
4099
+ modeLabel: "plan",
4100
+ message,
4101
+ err,
4055
4102
  });
4056
4103
  return {
4057
4104
  spendUsd: ledger.spend(),
@@ -4428,26 +4475,11 @@ export class Orchestrator {
4428
4475
  catch (err) {
4429
4476
  roHome.dispose();
4430
4477
  const message = safeErrorMessage(err);
4431
- store.writeText(join(paths.contextDir, "context_error.md"), `# Routing Error\n\n${message}\n`);
4432
- // A routing preflight refusal is a config_error, not harness_unavailable
4433
- // (A-1/#22): classify identically across every strategy's routing catch.
4434
- const routingFailure = routingFailureClassification(err);
4435
- writeFailure(store, paths, {
4436
- phase: "routing",
4437
- category: routingFailure.category,
4438
- safeMessage: message,
4439
- runDir: paths.root,
4440
- ...(routingFailure.nextActions ? { nextActions: routingFailure.nextActions } : {}),
4441
- });
4442
- store.writeText(join(paths.finalDir, "summary.md"), `# Run ${runId} (${opts.mode})\n\n- Lifecycle: failed\n- Phase: routing\n\n${message}\n`);
4443
- log.emit("output.ready", { kind: "summary", path: "final/summary.md", state: "diagnostic" });
4444
- log.emit("run.failed", {
4445
- lifecycle: "failed",
4446
- facts: makeOutcomeFacts("failed", { reason: "harness_failed" }),
4447
- reason: "harness_failed",
4448
- phase: "routing",
4449
- error: message,
4450
- failure_ref: "final/failure.yaml",
4478
+ writeRoutingFailureTerminal(store, paths, log, {
4479
+ runId,
4480
+ modeLabel: opts.mode,
4481
+ message,
4482
+ err,
4451
4483
  });
4452
4484
  return {
4453
4485
  runId,