@claudexor/orchestrator 6.1.1 → 6.1.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.
Files changed (83) hide show
  1. package/dist/attemptUsageCost.d.ts +2 -2
  2. package/dist/attemptUsageCost.d.ts.map +1 -1
  3. package/dist/attemptUsageCost.js +3 -3
  4. package/dist/attemptUsageCost.js.map +1 -1
  5. package/dist/candidateOutputs.d.ts +3 -2
  6. package/dist/candidateOutputs.d.ts.map +1 -1
  7. package/dist/candidateOutputs.js +5 -4
  8. package/dist/candidateOutputs.js.map +1 -1
  9. package/dist/candidateReview.js +3 -3
  10. package/dist/candidateReview.js.map +1 -1
  11. package/dist/candidateTerminals.js +3 -3
  12. package/dist/candidateTerminals.js.map +1 -1
  13. package/dist/candidateWorkProduct.js +2 -2
  14. package/dist/candidateWorkProduct.js.map +1 -1
  15. package/dist/continuation-custody.d.ts +15 -3
  16. package/dist/continuation-custody.d.ts.map +1 -1
  17. package/dist/continuation-custody.js +42 -14
  18. package/dist/continuation-custody.js.map +1 -1
  19. package/dist/continue-from.d.ts.map +1 -1
  20. package/dist/continue-from.js +6 -3
  21. package/dist/continue-from.js.map +1 -1
  22. package/dist/deepScanReducer.d.ts +1 -1
  23. package/dist/deepScanReducer.d.ts.map +1 -1
  24. package/dist/deepScanReducer.js +7 -5
  25. package/dist/deepScanReducer.js.map +1 -1
  26. package/dist/delegationFailure.js +1 -1
  27. package/dist/delegationFailure.js.map +1 -1
  28. package/dist/directoryCandidate.js +4 -4
  29. package/dist/directoryCandidate.js.map +1 -1
  30. package/dist/git-precondition.js +1 -1
  31. package/dist/git-precondition.js.map +1 -1
  32. package/dist/inrun-continuity.d.ts.map +1 -1
  33. package/dist/inrun-continuity.js +5 -6
  34. package/dist/inrun-continuity.js.map +1 -1
  35. package/dist/interaction.d.ts +4 -3
  36. package/dist/interaction.d.ts.map +1 -1
  37. package/dist/interaction.js +10 -6
  38. package/dist/interaction.js.map +1 -1
  39. package/dist/orchestrator.d.ts.map +1 -1
  40. package/dist/orchestrator.js +64 -61
  41. package/dist/orchestrator.js.map +1 -1
  42. package/dist/planRun.d.ts +1 -1
  43. package/dist/planRun.d.ts.map +1 -1
  44. package/dist/planRun.js +5 -5
  45. package/dist/planRun.js.map +1 -1
  46. package/dist/plannerAttempt.d.ts.map +1 -1
  47. package/dist/plannerAttempt.js +6 -5
  48. package/dist/plannerAttempt.js.map +1 -1
  49. package/dist/retainedOutput.d.ts.map +1 -1
  50. package/dist/retainedOutput.js +4 -3
  51. package/dist/retainedOutput.js.map +1 -1
  52. package/dist/reviewerPanel.js +2 -2
  53. package/dist/reviewerPanel.js.map +1 -1
  54. package/dist/routeContext.d.ts +1 -1
  55. package/dist/routeContext.d.ts.map +1 -1
  56. package/dist/runEventLog.d.ts +11 -7
  57. package/dist/runEventLog.d.ts.map +1 -1
  58. package/dist/runEventLog.js +17 -13
  59. package/dist/runEventLog.js.map +1 -1
  60. package/dist/runScope.d.ts +3 -2
  61. package/dist/runScope.d.ts.map +1 -1
  62. package/dist/runScope.js +5 -4
  63. package/dist/runScope.js.map +1 -1
  64. package/dist/runSupport.d.ts +18 -1
  65. package/dist/runSupport.d.ts.map +1 -1
  66. package/dist/runSupport.js +16 -1
  67. package/dist/runSupport.js.map +1 -1
  68. package/dist/runTerminalHelpers.d.ts +14 -0
  69. package/dist/runTerminalHelpers.d.ts.map +1 -1
  70. package/dist/runTerminalHelpers.js +54 -1
  71. package/dist/runTerminalHelpers.js.map +1 -1
  72. package/dist/runTerminalResults.d.ts +5 -1
  73. package/dist/runTerminalResults.d.ts.map +1 -1
  74. package/dist/runTerminalResults.js +37 -4
  75. package/dist/runTerminalResults.js.map +1 -1
  76. package/dist/runTerminals.d.ts.map +1 -1
  77. package/dist/runTerminals.js +140 -138
  78. package/dist/runTerminals.js.map +1 -1
  79. package/dist/secretDiff.d.ts +4 -3
  80. package/dist/secretDiff.d.ts.map +1 -1
  81. package/dist/secretDiff.js +4 -3
  82. package/dist/secretDiff.js.map +1 -1
  83. package/package.json +17 -17
@@ -51,7 +51,7 @@ import { budgetFailureRecord, classifyBudgetFailure } from "./budgetFailure.js";
51
51
  import { finalizeStructuredOutput } from "./structuredOutput.js";
52
52
  import { prepareRunAdmission } from "./runAdmission.js";
53
53
  import { dropDeltaPastBudget, emitPlanProgress, emitTransientExhausted, observeUsageSpend, recordAdapterThrow, } from "./laneStreamEvents.js";
54
- import { promptWithEngineConstraints, sleep, harnessEventPayload, safeErrorMessage, renderSummary, observeBudgetSignals, rotateOnStall, recordCleanAttemptMetrics, envInheritance, transientRetryPolicy, reviewerTimeoutMs, harnessInactivityTimeoutMs, observeAuthSwitch, emitPoolDegraded, deliveryRefusalFailure, winnerNeedsHuman, writeRaceDeliveryDecision, } from "./runSupport.js";
54
+ import { promptWithEngineConstraints, sleep, harnessEventPayload, safeErrorMessage, renderSummary, observeBudgetSignals, rotateOnStall, recordCleanAttemptMetrics, envInheritance, transientRetryPolicy, reviewerTimeoutMs, harnessInactivityTimeoutMs, laneSuspension, observeAuthSwitch, emitPoolDegraded, deliveryRefusalFailure, winnerNeedsHuman, writeRaceDeliveryDecision, } from "./runSupport.js";
55
55
  import { candidateStatusInRouteContext, resolveReadOnlyRouteContext, } from "./routeContext.js";
56
56
  import { resolveAutoReviewerPanel, resolveExplicitReviewerPanel } from "./reviewerPanel.js";
57
57
  import { ensureWriteModeGitBoundary } from "./git-precondition.js";
@@ -84,7 +84,7 @@ import { ReadinessLedger, evaluateConvergence, failureSignature, gatesPassed, re
84
84
  import { arbitrate } from "@claudexor/arbitration";
85
85
  import { buildSynthesisPlan, decideSynthesis } from "@claudexor/synthesis";
86
86
  import { billingKnowledgeForAuthRoute, attemptUsageCostSettlement, BudgetLedger, isBudgetTerminal, explainRanking, loadHarnessMetrics, promptFingerprint, rankHarnesses, } from "@claudexor/budget";
87
- import { hashJson, newId, noProjectRepoRoot, redactSecrets, safeInvoke, userConfigDir, } from "@claudexor/util";
87
+ import { hashJson, measureSync, newId, noProjectRepoRoot, pacedByAppends, redactSecrets, safeInvoke, userConfigDir, } from "@claudexor/util";
88
88
  const LABELS = "ABCDEFGHIJ".split("");
89
89
  const NO_PROJECT_ROOT = noProjectRepoRoot();
90
90
  export class Orchestrator {
@@ -231,14 +231,14 @@ export class Orchestrator {
231
231
  }
232
232
  catch (err) {
233
233
  const message = safeErrorMessage(err);
234
- store.writeText(join(paths.contextDir, "context_error.md"), `# Reviewer Panel Error\n\n${message}\n`);
234
+ await store.writeTextAsync(join(paths.contextDir, "context_error.md"), `# Reviewer Panel Error\n\n${message}\n`);
235
235
  writeFailure(store, paths, {
236
236
  phase: "review_preflight",
237
237
  category: "harness_unavailable",
238
238
  safeMessage: message,
239
239
  runDir: paths.root,
240
240
  });
241
- store.writeText(join(paths.finalDir, "summary.md"), `# Run ${runId} (${mode})\n\n- Lifecycle: failed\n- Phase: review preflight\n\n${message}\n`);
241
+ await store.writeTextAsync(join(paths.finalDir, "summary.md"), `# Run ${runId} (${mode})\n\n- Lifecycle: failed\n- Phase: review preflight\n\n${message}\n`);
242
242
  log.emit("output.ready", { kind: "summary", path: "final/summary.md", state: "diagnostic" });
243
243
  log.emit("run.failed", {
244
244
  lifecycle: "failed",
@@ -971,7 +971,7 @@ export class Orchestrator {
971
971
  include: projectCtx.include,
972
972
  exclude: projectCtx.exclude,
973
973
  });
974
- store.writeYaml(join(paths.contextDir, "context_pack.yaml"), pack);
974
+ await store.writeYamlAsync(join(paths.contextDir, "context_pack.yaml"), pack);
975
975
  log.emit("context.pack.created", {
976
976
  hash: pack.hash,
977
977
  files: pack.atlas.length,
@@ -1188,7 +1188,7 @@ export class Orchestrator {
1188
1188
  if (!result.packetMarkdown)
1189
1189
  return { pointerLine: null };
1190
1190
  const briefPath = join(paths.contextDir, "THREAD.md");
1191
- store.writeText(briefPath, result.packetMarkdown);
1191
+ await store.writeTextAsync(briefPath, result.packetMarkdown);
1192
1192
  return {
1193
1193
  pointerLine: threadContextPointer(briefPath),
1194
1194
  };
@@ -1419,6 +1419,7 @@ export class Orchestrator {
1419
1419
  const rateLimitStart = telemetry.rateLimits.length;
1420
1420
  rawLane.beginTry();
1421
1421
  try {
1422
+ const pacing = { waiting: false, version: 0 };
1422
1423
  const watched = withInactivityWatchdog(runModelGovernedRoute(routed, runSpec, dispatch), {
1423
1424
  timeoutMs: inactivityMs,
1424
1425
  countsAsProgress: countsAsAgentProgress,
@@ -1428,12 +1429,11 @@ export class Orchestrator {
1428
1429
  },
1429
1430
  // Waiting on the USER is legitimate silence. The interaction policy
1430
1431
  // either enforces a finite deadline or waits until answer/cancel/
1431
- // terminal/restart when the timeout is disabled.
1432
- isSuspended: () => (interaction?.pendingCount?.() ?? 0) > 0,
1433
- suspensionVersion: () => interaction?.suspensionVersion?.() ?? 0,
1432
+ // terminal/restart when the timeout is disabled. So is the journal wait.
1433
+ ...laneSuspension(pacing, interaction),
1434
1434
  stopSignal: signal, // after Stop: drained events are recorded, never used
1435
1435
  });
1436
- for await (const ev of watched) {
1436
+ for await (const ev of pacedByAppends(watched, [paths.eventsPath], pacing)) {
1437
1437
  // After Stop: stored evidence only, but file paths are still collected for the capture.
1438
1438
  const stopped = signal?.aborted === true;
1439
1439
  if (!stopped)
@@ -1708,7 +1708,7 @@ export class Orchestrator {
1708
1708
  aborted: signal?.aborted === true,
1709
1709
  authMode: telemetry.authMode,
1710
1710
  });
1711
- const { produced: producedFiles, omitted: producedOmitted } = AC.withAttemptFailureCost(() => writeCandidateAttemptArtifacts({
1711
+ const { produced: producedFiles, omitted: producedOmitted } = await AC.withAttemptFailureCost(() => writeCandidateAttemptArtifacts({
1712
1712
  store,
1713
1713
  attemptDir,
1714
1714
  worktreePath: envelope.worktree_path,
@@ -1838,7 +1838,7 @@ export class Orchestrator {
1838
1838
  const mutatingRun = isMutatingAccess(contract.access.effective_profile);
1839
1839
  const planBrief = verifiedPlanBrief(input);
1840
1840
  const quotaSnapshots = this.quotaSnapshotPreflight();
1841
- const { store, paths, log, ledger, runContext } = beginAnnouncedRun({
1841
+ const { store, paths, log, ledger, runContext } = await beginAnnouncedRun({
1842
1842
  input,
1843
1843
  contract,
1844
1844
  quotaSnapshots,
@@ -1860,7 +1860,7 @@ export class Orchestrator {
1860
1860
  const projectRoot = input.repoRoot;
1861
1861
  const wsm = new WorkspaceManager(execRoot, { projectRoot, runtimeRoot: runRuntimeRoot(input) });
1862
1862
  const anchors = secretDiff.revertAnchors(execRoot, projectRoot);
1863
- store.writeYaml(join(paths.contextDir, "task.yaml"), contract);
1863
+ await store.writeYamlAsync(join(paths.contextDir, "task.yaml"), contract);
1864
1864
  log.emit("task.contract.created", { task_contract_hash: hashJson(contract) });
1865
1865
  // Write modes need a git boundary for worktree isolation and honest diffs.
1866
1866
  // A non-git project folder is initialized automatically (baseline commit),
@@ -2208,7 +2208,7 @@ export class Orchestrator {
2208
2208
  error: message,
2209
2209
  phase: infraPhase,
2210
2210
  });
2211
- store.writeYaml(join(paths.attemptsDir, slot.attemptId, "attempt.yaml"), AC.attemptFailureRecord(slot.attemptId, adapter.id, failureCost, infraPhase, message, applied));
2211
+ await store.writeYamlAsync(join(paths.attemptsDir, slot.attemptId, "attempt.yaml"), AC.attemptFailureRecord(slot.attemptId, adapter.id, failureCost, infraPhase, message, applied));
2212
2212
  runsBySlot[slotIdx] = {
2213
2213
  attemptId: slot.attemptId,
2214
2214
  harnessId: adapter.id,
@@ -2405,7 +2405,7 @@ export class Orchestrator {
2405
2405
  sources: evidences.map((evidence) => evidence.attemptId),
2406
2406
  };
2407
2407
  try {
2408
- store.writeYaml(join(paths.arbitrationDir, "synthesis.yaml"), synth);
2408
+ await store.writeYamlAsync(join(paths.arbitrationDir, "synthesis.yaml"), synth);
2409
2409
  log.emit("synthesis.started", { synthesize: synth.synthesize, reason: synth.reason });
2410
2410
  if (synth.synthesize && !budgetStopped) {
2411
2411
  const synthRouted = adapters[0];
@@ -2578,7 +2578,7 @@ export class Orchestrator {
2578
2578
  facts = { ...facts, checks: "failed", reason: "checks_failed" };
2579
2579
  }
2580
2580
  const needsDec = facts.review === "blocked" || facts.checks === "failed";
2581
- store.writeYaml(join(paths.arbitrationDir, "decision.yaml"), {
2581
+ await store.writeYamlAsync(join(paths.arbitrationDir, "decision.yaml"), {
2582
2582
  ...result.decision,
2583
2583
  ...(needsDec
2584
2584
  ? blockedDecisionOverride(result.decision.evidence_facts, facts, finalVerify)
@@ -2586,7 +2586,7 @@ export class Orchestrator {
2586
2586
  review_verified: actualReviewVerified,
2587
2587
  final_verify: finalVerify,
2588
2588
  });
2589
- store.writeYaml(join(paths.arbitrationDir, "pairwise.yaml"), result.pairwise);
2589
+ await store.writeYamlAsync(join(paths.arbitrationDir, "pairwise.yaml"), result.pairwise);
2590
2590
  const decisionPath = join(paths.arbitrationDir, "decision.yaml");
2591
2591
  // Every attempt's failed tries by the FINAL outcome (delivery below can
2592
2592
  // still fail the checks); a review or check block is not an account failure.
@@ -2601,7 +2601,7 @@ export class Orchestrator {
2601
2601
  }
2602
2602
  const winnerAnswer = winnerRun.answerText ?? "";
2603
2603
  if (winnerAnswer.length > 0) {
2604
- store.writeText(join(paths.finalDir, "answer.md"), winnerAnswer + "\n");
2604
+ await store.writeTextAsync(join(paths.finalDir, "answer.md"), winnerAnswer + "\n");
2605
2605
  }
2606
2606
  if (contract.output_schema) {
2607
2607
  finalizeStructuredOutput({
@@ -2623,7 +2623,7 @@ export class Orchestrator {
2623
2623
  const delivered = await verifyAndDeliverFiles(execRoot, winnerRun.files, {}, gateSpecsFromContract(contract, input.runScopeMarker), (freshVerify) => finalVerifyBlocks(freshVerify)
2624
2624
  ? (freshVerify.reason ?? "final verify failed before directory race adoption")
2625
2625
  : null, log);
2626
- store.writeYaml(join(paths.finalDir, "delivery_receipt.yaml"), delivered);
2626
+ await store.writeYamlAsync(join(paths.finalDir, "delivery_receipt.yaml"), delivered);
2627
2627
  directoryDeliveryReceipt = true;
2628
2628
  directoryDelivery = delivered;
2629
2629
  finalVerify = delivered.finalVerify;
@@ -2655,7 +2655,7 @@ export class Orchestrator {
2655
2655
  });
2656
2656
  }
2657
2657
  else if (mutatingRun) {
2658
- const savedPatch = secretDiff.persistFinalPatch(store, paths.finalDir, winnerRun);
2658
+ const savedPatch = await secretDiff.persistFinalPatch(store, paths.finalDir, winnerRun);
2659
2659
  const patchSha256 = savedPatch.patchSha256;
2660
2660
  const wstats = secretDiff.resultDiffStats(winnerRun);
2661
2661
  const hasDiff = secretDiff.hasCandidateChanges(winnerRun);
@@ -2690,7 +2690,7 @@ export class Orchestrator {
2690
2690
  : null;
2691
2691
  }, log);
2692
2692
  raceDeliveryReceipt = applied;
2693
- store.writeYaml(join(paths.finalDir, "delivery_receipt.yaml"), applied);
2693
+ await store.writeYamlAsync(join(paths.finalDir, "delivery_receipt.yaml"), applied);
2694
2694
  finalVerify = applied.finalVerify;
2695
2695
  if (applied.applied) {
2696
2696
  adopted = true;
@@ -2736,7 +2736,7 @@ export class Orchestrator {
2736
2736
  revertAnchorId = await anchors.turn(preTurnSha, postTurnSha);
2737
2737
  }
2738
2738
  const capturedNoGitChange = capturedNoGitChangeValue(winnerRun);
2739
- store.writeYaml(join(paths.finalDir, "work_product.yaml"), {
2739
+ await store.writeYamlAsync(join(paths.finalDir, "work_product.yaml"), {
2740
2740
  id: newId("wp"),
2741
2741
  kind: input.create === true ? "new_repo" : "patch",
2742
2742
  source_task_id: taskId,
@@ -2771,7 +2771,7 @@ export class Orchestrator {
2771
2771
  },
2772
2772
  });
2773
2773
  }
2774
- store.writeText(join(paths.finalDir, "summary.md"), withPoolAttempts(renderSummary(runId, mode, { ...result.decision, facts }, evidences, synth.reason, actualReviewVerified, delegateFailure.raceSummaryDisclosure(winnerRun, runs)), racePool()));
2774
+ await store.writeTextAsync(join(paths.finalDir, "summary.md"), withPoolAttempts(renderSummary(runId, mode, { ...result.decision, facts }, evidences, synth.reason, actualReviewVerified, delegateFailure.raceSummaryDisclosure(winnerRun, runs)), racePool()));
2775
2775
  // summary.md is a DIAGNOSTIC artifact only (V8/PLAN addendum 2): it no
2776
2776
  // longer carries primary-output authority. A clean applyable success or a
2777
2777
  // winner answer still marks it ready for legacy INV-116 ordering; any
@@ -2883,7 +2883,7 @@ export class Orchestrator {
2883
2883
  ...(decisionBlock ? {} : erroredWinnerFailure(winnerRun, keptSources)),
2884
2884
  });
2885
2885
  if (!winnerRun) {
2886
- 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`);
2886
+ await store.writeTextAsync(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`);
2887
2887
  log.emit("output.ready", {
2888
2888
  kind: "summary",
2889
2889
  path: "final/summary.md",
@@ -2949,9 +2949,10 @@ export class Orchestrator {
2949
2949
  writeRunTelemetry(store, paths, contract, runId, taskId, mode, attempts, finalAttemptId, deepScanSynthesis) {
2950
2950
  // QA-034: attach the routing rationale recorded at pool ordering (if this
2951
2951
  // run computed one), then clear it — telemetry is written once at terminal.
2952
+ // It stays synchronous (several callers are synchronous callbacks), labeled (#688).
2952
2953
  const routingRationale = this.routingRationaleByRun.get(runId) ?? null;
2953
2954
  this.routingRationaleByRun.delete(runId);
2954
- writeRunTelemetryArtifact({
2955
+ measureSync("run.telemetry_write", () => writeRunTelemetryArtifact({
2955
2956
  store,
2956
2957
  finalDir: paths.finalDir,
2957
2958
  contract,
@@ -2963,7 +2964,7 @@ export class Orchestrator {
2963
2964
  routingRationale,
2964
2965
  deepScanSynthesis: deepScanSynthesis ?? null,
2965
2966
  resolveAuthPreference: (harnessId) => this.authPreferenceForHarness(contract.repo.root, harnessId, contract.auth_preference),
2966
- });
2967
+ }));
2967
2968
  }
2968
2969
  /**
2969
2970
  * SINGLE funnel for every reviewer-panel invocation: run it inside a per-review
@@ -3017,7 +3018,7 @@ export class Orchestrator {
3017
3018
  const contract = this.buildContract(input, taskId, mode);
3018
3019
  const planBrief = verifiedPlanBrief(input);
3019
3020
  const quotaSnapshots = this.quotaSnapshotPreflight();
3020
- const { store, paths, log, ledger, runContext } = beginAnnouncedRun({
3021
+ const { store, paths, log, ledger, runContext } = await beginAnnouncedRun({
3021
3022
  input,
3022
3023
  contract,
3023
3024
  quotaSnapshots,
@@ -3042,7 +3043,7 @@ export class Orchestrator {
3042
3043
  });
3043
3044
  const anchors = secretDiff.revertAnchors(execRoot, input.repoRoot);
3044
3045
  const readiness = new ReadinessLedger();
3045
- store.writeYaml(join(paths.contextDir, "task.yaml"), contract);
3046
+ await store.writeYamlAsync(join(paths.contextDir, "task.yaml"), contract);
3046
3047
  // Legacy Git envelopes need their boundary; explicit directory execution
3047
3048
  // keeps the requested strategy without initializing the selected folder.
3048
3049
  if (!input.inPlace && input.workspaceKind !== "directory") {
@@ -3108,7 +3109,7 @@ export class Orchestrator {
3108
3109
  if (contract.convergence.require_final_cross_family_clean_review && !reviewVerified) {
3109
3110
  const message = `convergence requires a cross-family clean review (>=2 healthy reviewer provider families); found ${new Set(reviewers.map((r) => r.providerFamily)).size}. ` +
3110
3111
  "Configure reviewers from a second provider family and check `claudexor doctor` for reviewer readiness.";
3111
- store.writeText(join(paths.contextDir, "context_error.md"), `# Convergence Preflight Error\n\n${message}\n`);
3112
+ await store.writeTextAsync(join(paths.contextDir, "context_error.md"), `# Convergence Preflight Error\n\n${message}\n`);
3112
3113
  writeFailure(store, paths, {
3113
3114
  phase: "review",
3114
3115
  category: "policy",
@@ -3119,7 +3120,7 @@ export class Orchestrator {
3119
3120
  "Check harness doctor for reviewer readiness",
3120
3121
  ],
3121
3122
  });
3122
- store.writeText(join(paths.finalDir, "summary.md"), `# Run ${runId} (${mode})\n\n- Lifecycle: failed\n- Phase: review preflight\n\n${message}\n`);
3123
+ await store.writeTextAsync(join(paths.finalDir, "summary.md"), `# Run ${runId} (${mode})\n\n- Lifecycle: failed\n- Phase: review preflight\n\n${message}\n`);
3123
3124
  log.emit("output.ready", { kind: "summary", path: "final/summary.md", state: "diagnostic" });
3124
3125
  log.emit("run.failed", {
3125
3126
  lifecycle: "failed",
@@ -3284,7 +3285,7 @@ export class Orchestrator {
3284
3285
  status: "failed",
3285
3286
  error: message,
3286
3287
  });
3287
- store.writeYaml(join(paths.attemptsDir, attemptId, "attempt.yaml"), AC.attemptFailureRecord(attemptId, adapter.id, failureCost, "harness", message, applied));
3288
+ await store.writeYamlAsync(join(paths.attemptsDir, attemptId, "attempt.yaml"), AC.attemptFailureRecord(attemptId, adapter.id, failureCost, "harness", message, applied));
3288
3289
  run = {
3289
3290
  applied,
3290
3291
  attemptId,
@@ -3540,7 +3541,7 @@ export class Orchestrator {
3540
3541
  toCandidateEvidence(lastRun, contract, lastFindings, lastFinalReviewClean, actualReviewVerified),
3541
3542
  ], { ...arbitrationBudgetOptions(ledger), reviewRequested: contract.review_requested });
3542
3543
  decision = arb.decision;
3543
- store.writeYaml(join(paths.arbitrationDir, "decision.yaml"), decision);
3544
+ await store.writeYamlAsync(join(paths.arbitrationDir, "decision.yaml"), decision);
3544
3545
  // A converged run adopts the arbitration axes (checks/review); an
3545
3546
  // otherwise not-converged loop that nonetheless produced an applyable
3546
3547
  // decision adopts it too.
@@ -3616,7 +3617,7 @@ export class Orchestrator {
3616
3617
  : { facts }),
3617
3618
  final_verify: convFinalVerify,
3618
3619
  };
3619
- store.writeYaml(join(paths.arbitrationDir, "decision.yaml"), decision);
3620
+ await store.writeYamlAsync(join(paths.arbitrationDir, "decision.yaml"), decision);
3620
3621
  }
3621
3622
  this.writeRunTelemetry(store, paths, contract, runId, taskId, mode, attemptTelemetries, lastRun?.attemptId ?? null);
3622
3623
  // Deliver the converged/last work to final/ so `apply` and `inspect` can
@@ -3638,7 +3639,7 @@ export class Orchestrator {
3638
3639
  });
3639
3640
  }
3640
3641
  else {
3641
- const savedPatch = secretDiff.persistFinalPatch(store, paths.finalDir, lastRun);
3642
+ const savedPatch = await secretDiff.persistFinalPatch(store, paths.finalDir, lastRun);
3642
3643
  const patchSha256 = savedPatch.patchSha256;
3643
3644
  // Honest apply-state (parity with runRace single-candidate in-place): a
3644
3645
  // convergence run with inPlace mutated the live tree directly across its
@@ -3652,7 +3653,7 @@ export class Orchestrator {
3652
3653
  : "not_applied";
3653
3654
  const revertAnchorId = convAdopted === true ? await anchors.turn(preTurnSha, lastPostTurnSha) : null;
3654
3655
  const capturedNoGitChange = capturedNoGitChangeValue(lastRun);
3655
- store.writeYaml(join(paths.finalDir, "work_product.yaml"), {
3656
+ await store.writeYamlAsync(join(paths.finalDir, "work_product.yaml"), {
3656
3657
  id: newId("wp"),
3657
3658
  kind: "patch",
3658
3659
  source_task_id: taskId,
@@ -3678,10 +3679,10 @@ export class Orchestrator {
3678
3679
  }
3679
3680
  // The exact answer is kept for every convergence result, patch or files.
3680
3681
  if (lastRun.answerText) {
3681
- store.writeText(join(paths.finalDir, "answer.md"), lastRun.answerText);
3682
+ await store.writeTextAsync(join(paths.finalDir, "answer.md"), lastRun.answerText);
3682
3683
  log.emit("output.ready", { kind: "answer", path: "final/answer.md" });
3683
3684
  }
3684
- 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}` : ""}${convCause ? `\n- Failure: ${convCause}` : ""}${secretDiff.resultSummaryDisclosure(lastRun)}`, convPool)}\n`);
3685
+ await store.writeTextAsync(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}` : ""}${convCause ? `\n- Failure: ${convCause}` : ""}${secretDiff.resultSummaryDisclosure(lastRun)}`, convPool)}\n`);
3685
3686
  // Lifecycle invariant (all modes): output.ready precedes the terminal
3686
3687
  // event so a client that applied the terminal event has the output.
3687
3688
  log.emit("output.ready", {
@@ -3764,7 +3765,7 @@ export class Orchestrator {
3764
3765
  // ARCHITECTURE event contract guarantees output.ready precedes the
3765
3766
  // terminal in every mode.
3766
3767
  if (!lastRun || (interrupted && input.inPlace !== true && !lastRun.files)) {
3767
- 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`);
3768
+ await store.writeTextAsync(join(paths.finalDir, "summary.md"), `${withPoolAttempts(`# Run ${runId} (${mode})\n\n- Lifecycle: ${facts.lifecycle}${facts.reason ? ` (${facts.reason})` : ""}\n- Attempts: ${attempt}`, convPool)}\n`);
3768
3769
  log.emit("output.ready", {
3769
3770
  kind: "summary",
3770
3771
  path: "final/summary.md",
@@ -3908,7 +3909,7 @@ export class Orchestrator {
3908
3909
  // contract validation runs BEFORE the run is announced (see runRace).
3909
3910
  const contract = this.buildContract(input, taskId, "plan");
3910
3911
  const quotaSnapshots = this.quotaSnapshotPreflight();
3911
- const { store, paths, log, ledger } = beginAnnouncedRun({
3912
+ const { store, paths, log, ledger } = await beginAnnouncedRun({
3912
3913
  input,
3913
3914
  contract,
3914
3915
  quotaSnapshots,
@@ -3920,7 +3921,7 @@ export class Orchestrator {
3920
3921
  phase: "plan",
3921
3922
  prompt: input.prompt,
3922
3923
  }, announce);
3923
- store.writeYaml(join(paths.contextDir, "task.yaml"), contract);
3924
+ await store.writeYamlAsync(join(paths.contextDir, "task.yaml"), contract);
3924
3925
  log.emit("task.contract.created", { task_contract_hash: hashJson(contract) });
3925
3926
  // W3.3: ONE resolved read-only context — the routing point-probe and every
3926
3927
  // planner spawn consume the SAME scoped env (see routeContext.ts).
@@ -3936,7 +3937,7 @@ export class Orchestrator {
3936
3937
  adapters = await this.resolveCandidateAdapters({ ...input, n: undefined }, "plan", ledger, log, roHome, runId);
3937
3938
  }
3938
3939
  catch (err) {
3939
- roHome.dispose();
3940
+ await roHome.dispose();
3940
3941
  const message = safeErrorMessage(err);
3941
3942
  writeRoutingFailureTerminal(store, paths, log, {
3942
3943
  runId,
@@ -3963,16 +3964,16 @@ export class Orchestrator {
3963
3964
  contextSection = await this.lazyContextSection(input, contract, store, paths, log);
3964
3965
  }
3965
3966
  catch (err) {
3966
- roHome.dispose();
3967
+ await roHome.dispose();
3967
3968
  const message = safeErrorMessage(err);
3968
- store.writeText(join(paths.contextDir, "context_error.md"), `# Context Error\n\n${message}\n`);
3969
+ await store.writeTextAsync(join(paths.contextDir, "context_error.md"), `# Context Error\n\n${message}\n`);
3969
3970
  writeFailure(store, paths, {
3970
3971
  phase: "context",
3971
3972
  category: "project",
3972
3973
  safeMessage: message,
3973
3974
  runDir: paths.root,
3974
3975
  });
3975
- store.writeText(join(paths.finalDir, "summary.md"), `# Run ${runId} (plan)\n\n- Lifecycle: failed\n- Phase: context\n\n${message}\n`);
3976
+ await store.writeTextAsync(join(paths.finalDir, "summary.md"), `# Run ${runId} (plan)\n\n- Lifecycle: failed\n- Phase: context\n\n${message}\n`);
3976
3977
  log.emit("output.ready", { kind: "summary", path: "final/summary.md", state: "diagnostic" });
3977
3978
  log.emit("run.failed", {
3978
3979
  lifecycle: "failed",
@@ -4085,7 +4086,7 @@ export class Orchestrator {
4085
4086
  const text = outcome.text ?? "(no output)";
4086
4087
  plans.push({ id: outcome.harnessId, text });
4087
4088
  // The planner's own saved copy keeps its exact text (INV-146).
4088
- store.writeText(join(paths.root, "plans", `${outcome.harnessId}.md`), text + "\n");
4089
+ await store.writeTextAsync(join(paths.root, "plans", `${outcome.harnessId}.md`), text + "\n");
4089
4090
  // Solo planning (D31): the FIRST successful planner is the plan; later
4090
4091
  // pool members are a sequential fallback chain (ask parity), not
4091
4092
  // parallel co-authors. Council re-enables the multi-draft round.
@@ -4102,7 +4103,7 @@ export class Orchestrator {
4102
4103
  }
4103
4104
  finally {
4104
4105
  // Planners done (or threw) — reclaim scoped scratch/API-route state.
4105
- roHome.dispose();
4106
+ await roHome.dispose();
4106
4107
  }
4107
4108
  if (input.signal?.aborted) {
4108
4109
  return cancelledResult(log, runId, taskId, "plan", paths.root, planAttempts.map((p) => ({
@@ -4272,7 +4273,7 @@ export class Orchestrator {
4272
4273
  // recorded user intent is the CALLER's goal.
4273
4274
  const contract = this.buildContract({ ...input, prompt: opts.contractIntent ?? prompt }, taskId, opts.mode);
4274
4275
  const quotaSnapshots = this.quotaSnapshotPreflight();
4275
- const { store, paths, log, ledger, runContext } = beginAnnouncedRun({
4276
+ const { store, paths, log, ledger, runContext } = await beginAnnouncedRun({
4276
4277
  input,
4277
4278
  contract,
4278
4279
  quotaSnapshots,
@@ -4284,7 +4285,7 @@ export class Orchestrator {
4284
4285
  phase: "report",
4285
4286
  prompt,
4286
4287
  }, announce);
4287
- store.writeYaml(join(paths.contextDir, "task.yaml"), contract);
4288
+ await store.writeYamlAsync(join(paths.contextDir, "task.yaml"), contract);
4288
4289
  log.emit("task.contract.created", { task_contract_hash: hashJson(contract) });
4289
4290
  // The ask/deep-scan report stays bare (its scouts read the tree themselves);
4290
4291
  // no lazy ContextPack section is attached here.
@@ -4311,7 +4312,7 @@ export class Orchestrator {
4311
4312
  }));
4312
4313
  }
4313
4314
  catch (err) {
4314
- roHome.dispose();
4315
+ await roHome.dispose();
4315
4316
  const message = safeErrorMessage(err);
4316
4317
  writeRoutingFailureTerminal(store, paths, log, {
4317
4318
  runId,
@@ -4539,7 +4540,7 @@ export class Orchestrator {
4539
4540
  });
4540
4541
  attemptTelemetries.push({ attemptId, harnessId: adapter.id, telemetry });
4541
4542
  if (opts.deepScan) {
4542
- store.writeText(join(paths.findingsDir, `${attemptId}-error.md`), `# Explorer ${attemptId} failed\n\n${message}\n`);
4543
+ await store.writeTextAsync(join(paths.findingsDir, `${attemptId}-error.md`), `# Explorer ${attemptId} failed\n\n${message}\n`);
4543
4544
  }
4544
4545
  return { status: "launched" };
4545
4546
  }
@@ -4608,6 +4609,7 @@ export class Orchestrator {
4608
4609
  ...(knobs.ignored.length > 0 ? { ignored_settings: knobs.ignored } : {}),
4609
4610
  });
4610
4611
  try {
4612
+ const pacing = { waiting: false, version: 0 };
4611
4613
  const watchedReport = withInactivityWatchdog(runModelGovernedRoute(routed, runSpec), {
4612
4614
  timeoutMs: harnessInactivityTimeoutMs(this.config(input.repoRoot)),
4613
4615
  countsAsProgress: countsAsAgentProgress,
@@ -4615,11 +4617,12 @@ export class Orchestrator {
4615
4617
  tryAbort.abort();
4616
4618
  void adapter.cancel?.(activeSessionId)?.catch(() => { });
4617
4619
  },
4618
- isSuspended: () => (reportInteraction?.pendingCount?.() ?? 0) > 0,
4619
- suspensionVersion: () => reportInteraction?.suspensionVersion?.() ?? 0,
4620
+ ...laneSuspension(pacing, reportInteraction),
4620
4621
  stopSignal: input.signal, // after Stop: drained events are recorded, never used
4621
4622
  });
4622
- for await (const ev of watchedReport) {
4623
+ const files = [paths.eventsPath, attemptEventsPath];
4624
+ const read = pacedByAppends(watchedReport, files, pacing);
4625
+ for await (const ev of read) {
4623
4626
  // A read after Stop is stored evidence only; a callback may itself press Stop.
4624
4627
  const shown = !input.signal?.aborted && ev.payload?.["buffered"] !== true;
4625
4628
  if (shown)
@@ -4804,7 +4807,7 @@ export class Orchestrator {
4804
4807
  });
4805
4808
  launched.delete(tried);
4806
4809
  if (opts.deepScan) {
4807
- store.writeText(join(paths.findingsDir, `${attemptId}-error.md`), `# Explorer ${attemptId} failed\n\n${harnessError}\n`);
4810
+ await store.writeTextAsync(join(paths.findingsDir, `${attemptId}-error.md`), `# Explorer ${attemptId} failed\n\n${harnessError}\n`);
4808
4811
  }
4809
4812
  return { status: "launched" };
4810
4813
  }
@@ -4836,7 +4839,7 @@ export class Orchestrator {
4836
4839
  const scoutTag = scoutInterrupted
4837
4840
  ? ` interrupted (${interruptionDetail(roFinalized.reason)})`
4838
4841
  : ` (${adapter.id})`;
4839
- store.writeText(join(paths.findingsDir, `${attemptId}${scoutInterrupted ? "-interrupted" : ""}.md`), `# Explorer ${attemptId}${scoutTag}\n\n${report || "(no output)"}${warningNote}\n`);
4842
+ await store.writeTextAsync(join(paths.findingsDir, `${attemptId}${scoutInterrupted ? "-interrupted" : ""}.md`), `# Explorer ${attemptId}${scoutTag}\n\n${report || "(no output)"}${warningNote}\n`);
4840
4843
  }
4841
4844
  return { status: "launched" };
4842
4845
  };
@@ -4981,7 +4984,7 @@ export class Orchestrator {
4981
4984
  finally {
4982
4985
  // All read-only attempts done (or threw) — reclaim scoped scratch and
4983
4986
  // injected API-route state. Vendor-owned native credentials were not copied.
4984
- roHome.dispose();
4987
+ await roHome.dispose();
4985
4988
  }
4986
4989
  const candidateSummaries = attempts.map((a) => ({
4987
4990
  attemptId: a.attemptId,
@@ -5037,7 +5040,7 @@ export class Orchestrator {
5037
5040
  : reducedReport !== null
5038
5041
  ? reducedReport
5039
5042
  : rawScoutBundle({ succeeded, unsuccessful, status: deepScanSynthesis });
5040
- store.writeText(join(paths.finalDir, opts.artifactName), `# ${opts.title}\n\n${report}\n`);
5043
+ await store.writeTextAsync(join(paths.finalDir, opts.artifactName), `# ${opts.title}\n\n${report}\n`);
5041
5044
  // ask is the only read-only strategy that can carry a structured-output
5042
5045
  // contract (the boundary refuses the rest); validate the FINAL aggregate
5043
5046
  // (the reduced synthesis, or the honest bundle for a degraded scan) — never
@@ -5058,7 +5061,7 @@ export class Orchestrator {
5058
5061
  path: `final/${opts.artifactName}`,
5059
5062
  });
5060
5063
  if (opts.deepScan) {
5061
- store.writeYaml(join(paths.finalDir, "explore-findings.yaml"), {
5064
+ await store.writeYamlAsync(join(paths.finalDir, "explore-findings.yaml"), {
5062
5065
  mode: "explore",
5063
5066
  width,
5064
5067
  attempts: attempts.map((a) => ({
@@ -5077,7 +5080,7 @@ export class Orchestrator {
5077
5080
  })),
5078
5081
  read_only: true,
5079
5082
  });
5080
- store.writeText(join(paths.finalDir, "omissions.md"), `# Omissions\n\n${unsuccessful.map((a) => `- ${a.attemptId} / ${a.harnessId} (${a.status}): ${a.error}`).join("\n") || "- None recorded by the runner. Synthesis claims still require evidence checks."}\n`);
5083
+ await store.writeTextAsync(join(paths.finalDir, "omissions.md"), `# Omissions\n\n${unsuccessful.map((a) => `- ${a.attemptId} / ${a.harnessId} (${a.status}): ${a.error}`).join("\n") || "- None recorded by the runner. Synthesis claims still require evidence checks."}\n`);
5081
5084
  }
5082
5085
  let terminalFacts = makeOutcomeFacts("succeeded");
5083
5086
  const reportBudgetTerminal = ledger.terminal();
@@ -5106,13 +5109,13 @@ export class Orchestrator {
5106
5109
  .join(", ");
5107
5110
  // A delivered report that still ends unsuccessfully or blocked keeps it and names every failed try.
5108
5111
  const reportPool = terminalPoolAttempts(terminalFacts, attempts);
5109
- store.writeText(join(paths.finalDir, "summary.md"), `# Run ${runId} (${opts.mode})\n\n- Harnesses: ${harnessLabel}\n- Lifecycle: ${terminalFacts.lifecycle}${terminalFacts.reason ? ` (${terminalFacts.reason})` : ""}\n\n${withPoolAttempts(report, reportPool)}\n`);
5112
+ await store.writeTextAsync(join(paths.finalDir, "summary.md"), `# Run ${runId} (${opts.mode})\n\n- Harnesses: ${harnessLabel}\n- Lifecycle: ${terminalFacts.lifecycle}${terminalFacts.reason ? ` (${terminalFacts.reason})` : ""}\n\n${withPoolAttempts(report, reportPool)}\n`);
5110
5113
  const reportProducerAttemptId = opts.deepScan &&
5111
5114
  deepScanSynthesis?.status === "succeeded" &&
5112
5115
  deepScanSynthesis.reducer_attempt_id
5113
5116
  ? deepScanSynthesis.reducer_attempt_id
5114
5117
  : (succeeded[0]?.attemptId ?? "a01");
5115
- store.writeYaml(join(paths.finalDir, "work_product.yaml"), {
5118
+ await store.writeYamlAsync(join(paths.finalDir, "work_product.yaml"), {
5116
5119
  id: newId("wp"),
5117
5120
  kind: "report",
5118
5121
  source_task_id: taskId,