@lcv-ideas-software/cross-review 4.0.7 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,7 +11,7 @@ export declare function readCacheManifest(dataDir: string, sessionId: string): C
11
11
  * by tests; production callers should append entries via
12
12
  * appendCacheManifestEntry.
13
13
  */
14
- export declare function writeCacheManifest(dataDir: string, sessionId: string, manifest: CacheManifest): void;
14
+ export declare function writeCacheManifest(dataDir: string, sessionId: string, manifest: CacheManifest): Promise<void>;
15
15
  /**
16
16
  * Append a single entry to the session manifest. Lazily creates the
17
17
  * manifest if it does not exist. Each call performs (a) read-current,
@@ -19,4 +19,4 @@ export declare function writeCacheManifest(dataDir: string, sessionId: string, m
19
19
  * process; concurrent calls in the same process must be awaited in
20
20
  * order by the caller.
21
21
  */
22
- export declare function appendCacheManifestEntry(dataDir: string, sessionId: string, entry: CacheManifestEntry, cacheSchemaVersion?: string): void;
22
+ export declare function appendCacheManifestEntry(dataDir: string, sessionId: string, entry: CacheManifestEntry, cacheSchemaVersion?: string): Promise<void>;
@@ -23,7 +23,7 @@ const TMP_NONCE_BYTES = 2;
23
23
  function manifestPath(dataDir, sessionId) {
24
24
  return path.resolve(dataDir, "sessions", sessionId, MANIFEST_FILENAME);
25
25
  }
26
- function writeJsonAtomic(file, data) {
26
+ async function writeJsonAtomic(file, data) {
27
27
  fs.mkdirSync(path.dirname(file), { recursive: true });
28
28
  const nonce = crypto.randomBytes(TMP_NONCE_BYTES).toString("hex");
29
29
  const tmp = `${file}.${process.pid}.${Date.now()}.${nonce}.tmp`;
@@ -39,11 +39,17 @@ function writeJsonAtomic(file, data) {
39
39
  const code = err.code;
40
40
  if (!code || !ATOMIC_WRITE_RETRY_CODES.has(code))
41
41
  break;
42
+ // v4.1.0 hardening (Codex R1 catch on the broader F3 grep): this
43
+ // second atomic-write helper had the SAME CPU-burning busy-wait
44
+ // as session-store.ts writeJson (now fixed). The single Node
45
+ // event loop was being blocked for up to 310 ms (10+20+40+80+160)
46
+ // per cache_manifest append under Windows-AV-induced
47
+ // EPERM/EBUSY contention. Promise + setTimeout: event loop
48
+ // remains fully responsive.
42
49
  const wait = 10 * 2 ** attempt;
43
- const start = Date.now();
44
- while (Date.now() - start < wait) {
45
- /* spin */
46
- }
50
+ await new Promise((resolve) => {
51
+ setTimeout(resolve, wait);
52
+ });
47
53
  }
48
54
  }
49
55
  try {
@@ -77,8 +83,8 @@ export function readCacheManifest(dataDir, sessionId) {
77
83
  * by tests; production callers should append entries via
78
84
  * appendCacheManifestEntry.
79
85
  */
80
- export function writeCacheManifest(dataDir, sessionId, manifest) {
81
- writeJsonAtomic(manifestPath(dataDir, sessionId), manifest);
86
+ export async function writeCacheManifest(dataDir, sessionId, manifest) {
87
+ await writeJsonAtomic(manifestPath(dataDir, sessionId), manifest);
82
88
  }
83
89
  /**
84
90
  * Append a single entry to the session manifest. Lazily creates the
@@ -87,7 +93,7 @@ export function writeCacheManifest(dataDir, sessionId, manifest) {
87
93
  * process; concurrent calls in the same process must be awaited in
88
94
  * order by the caller.
89
95
  */
90
- export function appendCacheManifestEntry(dataDir, sessionId, entry, cacheSchemaVersion = CACHE_SCHEMA_VERSION_DEFAULT) {
96
+ export async function appendCacheManifestEntry(dataDir, sessionId, entry, cacheSchemaVersion = CACHE_SCHEMA_VERSION_DEFAULT) {
91
97
  const file = manifestPath(dataDir, sessionId);
92
98
  const nowIso = new Date().toISOString();
93
99
  let current;
@@ -128,6 +134,6 @@ export function appendCacheManifestEntry(dataDir, sessionId, entry, cacheSchemaV
128
134
  }
129
135
  current.entries.push(entry);
130
136
  current.updated_at = nowIso;
131
- writeJsonAtomic(file, current);
137
+ await writeJsonAtomic(file, current);
132
138
  }
133
139
  //# sourceMappingURL=cache-manifest.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cache-manifest.js","sourceRoot":"","sources":["../../../src/core/cache-manifest.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cache-manifest.js","sourceRoot":"","sources":["../../../src/core/cache-manifest.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import type { AppConfig, PeerId } from "./types.js";
2
- export declare const VERSION = "4.0.7";
3
- export declare const RELEASE_DATE = "2026-05-16";
2
+ export declare const VERSION = "4.1.0";
3
+ export declare const RELEASE_DATE = "2026-05-17";
4
4
  export declare const DEFAULT_MAX_OUTPUT_TOKENS = 20000;
5
5
  export declare function getLastFileConfigResult(): import("./file-config.js").ApplyFileConfigResult | undefined;
6
6
  export declare function loadConfig(): AppConfig;
@@ -17,8 +17,8 @@ function expandHome(rawPath) {
17
17
  }
18
18
  return rawPath;
19
19
  }
20
- export const VERSION = "4.0.7";
21
- export const RELEASE_DATE = "2026-05-16";
20
+ export const VERSION = "4.1.0";
21
+ export const RELEASE_DATE = "2026-05-17";
22
22
  export const DEFAULT_MAX_OUTPUT_TOKENS = 20_000;
23
23
  const COST_RATE_ENV_PREFIX = {
24
24
  codex: "CROSS_REVIEW_OPENAI",
@@ -1005,7 +1005,7 @@ export class CrossReviewOrchestrator {
1005
1005
  per_peer_verdict: perPeerVerdict,
1006
1006
  });
1007
1007
  if (unanimousVerifiedSatisfied && mode === "active") {
1008
- const result = this.store.markEvidenceItemAddressedByJudge(params.session_id, item.id, {
1008
+ const result = await this.store.markEvidenceItemAddressedByJudge(params.session_id, item.id, {
1009
1009
  round: judgmentRound,
1010
1010
  rationale: Object.values(rationales).join(" || "),
1011
1011
  judge_peer: params.judge_peers[0],
@@ -1231,7 +1231,7 @@ export class CrossReviewOrchestrator {
1231
1231
  });
1232
1232
  }
1233
1233
  else {
1234
- const result = this.store.markEvidenceItemAddressedByJudge(params.session_id, item.id, {
1234
+ const result = await this.store.markEvidenceItemAddressedByJudge(params.session_id, item.id, {
1235
1235
  round: judgmentRound,
1236
1236
  rationale: judgment.rationale,
1237
1237
  judge_peer: params.judge_peer,
@@ -1375,7 +1375,7 @@ export class CrossReviewOrchestrator {
1375
1375
  async initSession(task, caller = "operator", reviewFocus) {
1376
1376
  const snapshot = await this.probeAll();
1377
1377
  const normalizedReviewFocus = normalizeReviewFocus(reviewFocus, this.config);
1378
- const meta = this.store.init(task, caller, snapshot, normalizedReviewFocus);
1378
+ const meta = await this.store.init(task, caller, snapshot, normalizedReviewFocus);
1379
1379
  this.emit({
1380
1380
  type: "session.created",
1381
1381
  session_id: meta.session_id,
@@ -1393,7 +1393,7 @@ export class CrossReviewOrchestrator {
1393
1393
  .filter((model) => model && model !== adapter.model)
1394
1394
  .map((model) => createAdapters(this.config, { [adapter.id]: model })[adapter.id]);
1395
1395
  }
1396
- recordFallback(sessionId, adapter, fallback, reason) {
1396
+ async recordFallback(sessionId, adapter, fallback, reason) {
1397
1397
  const event = {
1398
1398
  peer: adapter.id,
1399
1399
  provider: adapter.provider,
@@ -1402,7 +1402,7 @@ export class CrossReviewOrchestrator {
1402
1402
  reason,
1403
1403
  ts: now(),
1404
1404
  };
1405
- this.store.appendFallbackEvent(sessionId, event);
1405
+ await this.store.appendFallbackEvent(sessionId, event);
1406
1406
  this.emit({
1407
1407
  type: "peer.fallback.started",
1408
1408
  session_id: sessionId,
@@ -1416,7 +1416,7 @@ export class CrossReviewOrchestrator {
1416
1416
  // peer call surfaced cache telemetry, and append a row to the
1417
1417
  // session cache manifest. Best-effort; never throws — manifest
1418
1418
  // failures should not break the review loop.
1419
- recordCacheTelemetry(sessionId, round, peerResult) {
1419
+ async recordCacheTelemetry(sessionId, round, peerResult) {
1420
1420
  try {
1421
1421
  if (!this.config.cache.enabled)
1422
1422
  return;
@@ -1449,7 +1449,7 @@ export class CrossReviewOrchestrator {
1449
1449
  savings_unknown: savings.unknown,
1450
1450
  },
1451
1451
  });
1452
- appendCacheManifestEntry(this.config.data_dir, sessionId, {
1452
+ await appendCacheManifestEntry(this.config.data_dir, sessionId, {
1453
1453
  ts: new Date().toISOString(),
1454
1454
  round,
1455
1455
  peer: peerResult.peer,
@@ -1478,7 +1478,7 @@ export class CrossReviewOrchestrator {
1478
1478
  // session has no ceiling, when cumulative cost is below threshold, or
1479
1479
  // when the warning has already fired. Best-effort writeback — manifest
1480
1480
  // failures should not break the review loop.
1481
- checkBudgetWarning(sessionId, round) {
1481
+ async checkBudgetWarning(sessionId, round) {
1482
1482
  try {
1483
1483
  const meta = this.store.read(sessionId);
1484
1484
  const ceiling = meta.cost_ceiling_usd;
@@ -1493,7 +1493,7 @@ export class CrossReviewOrchestrator {
1493
1493
  // Persist the one-shot guard FIRST so an emit-throw cannot cause
1494
1494
  // re-emission on a retry; we accept "warning persisted but emit
1495
1495
  // observably failed" as the safer drift mode.
1496
- this.store.markBudgetWarningEmitted(sessionId);
1496
+ await this.store.markBudgetWarningEmitted(sessionId);
1497
1497
  this.emit({
1498
1498
  type: "session.budget_warning",
1499
1499
  session_id: sessionId,
@@ -1529,7 +1529,7 @@ export class CrossReviewOrchestrator {
1529
1529
  let lastFallbackFailure;
1530
1530
  for (const fallback of this.fallbackAdapters(adapter)) {
1531
1531
  fallbackWasTried = true;
1532
- const fallbackEvent = this.recordFallback(context.session_id, adapter, fallback, failure.failure_class);
1532
+ const fallbackEvent = await this.recordFallback(context.session_id, adapter, fallback, failure.failure_class);
1533
1533
  // v2.5.0 fix (Codex audit P3, 2026-05-03): every paid retry path
1534
1534
  // must emit a cost_alert so FinOps consumers can preregister
1535
1535
  // unexpected spend. Pre-v2.5.0 only `peer.format_recovery`
@@ -1802,7 +1802,7 @@ export class CrossReviewOrchestrator {
1802
1802
  const session = existingSession
1803
1803
  ? existingSession
1804
1804
  : missingFinancialVars.length
1805
- ? this.store.init(input.task, effectivePetitioner, [], normalizeReviewFocus(input.review_focus, this.config))
1805
+ ? await this.store.init(input.task, effectivePetitioner, [], normalizeReviewFocus(input.review_focus, this.config))
1806
1806
  : await this.initSession(input.task, effectivePetitioner, input.review_focus);
1807
1807
  const petitioner = effectivePetitioner;
1808
1808
  const roundNumber = session.rounds.length + 1;
@@ -1845,7 +1845,7 @@ export class CrossReviewOrchestrator {
1845
1845
  const prompt = buildReviewPrompt(session, input.draft, this.config, input.review_focus, attachments);
1846
1846
  const moderationSafePrompt = buildModerationSafeReviewPrompt(session, input.draft, this.config, input.review_focus);
1847
1847
  const promptFile = this.store.savePrompt(session.session_id, roundNumber, prompt);
1848
- this.store.markInFlight(session.session_id, {
1848
+ await this.store.markInFlight(session.session_id, {
1849
1849
  round: roundNumber,
1850
1850
  peers: selectedPeers,
1851
1851
  started_at: startedAt,
@@ -1862,10 +1862,10 @@ export class CrossReviewOrchestrator {
1862
1862
  const message = financialControlsMissingMessage(missingFinancialVars);
1863
1863
  const rejected = selectAdapters(adapters, selectedPeers).map((adapter) => budgetPreflightFailure(adapter.id, adapter.provider, adapter.model, message));
1864
1864
  for (const failure of rejected) {
1865
- this.store.savePeerFailure(session.session_id, roundNumber, failure);
1865
+ await this.store.savePeerFailure(session.session_id, roundNumber, failure);
1866
1866
  }
1867
1867
  const convergence = checkConvergence(selectedPeers, callerStatus, [], rejected);
1868
- const round = this.store.appendRound(session.session_id, {
1868
+ const round = await this.store.appendRound(session.session_id, {
1869
1869
  caller_status: callerStatus,
1870
1870
  draft_file: draftFile,
1871
1871
  prompt_file: promptFile,
@@ -1875,7 +1875,7 @@ export class CrossReviewOrchestrator {
1875
1875
  convergence_scope: convergenceScope,
1876
1876
  started_at: startedAt,
1877
1877
  });
1878
- const updated = this.store.finalize(session.session_id, "max-rounds", "financial_controls_missing");
1878
+ const updated = await this.store.finalize(session.session_id, "max-rounds", "financial_controls_missing");
1879
1879
  this.emit({
1880
1880
  type: "round.blocked.financial_controls_missing",
1881
1881
  session_id: session.session_id,
@@ -1904,10 +1904,10 @@ export class CrossReviewOrchestrator {
1904
1904
  if (message) {
1905
1905
  const rejected = selectAdapters(adapters, selectedPeers).map((adapter) => budgetPreflightFailure(adapter.id, adapter.provider, adapter.model, message));
1906
1906
  for (const failure of rejected) {
1907
- this.store.savePeerFailure(session.session_id, roundNumber, failure);
1907
+ await this.store.savePeerFailure(session.session_id, roundNumber, failure);
1908
1908
  }
1909
1909
  const convergence = checkConvergence(selectedPeers, callerStatus, [], rejected);
1910
- const round = this.store.appendRound(session.session_id, {
1910
+ const round = await this.store.appendRound(session.session_id, {
1911
1911
  caller_status: callerStatus,
1912
1912
  draft_file: draftFile,
1913
1913
  prompt_file: promptFile,
@@ -1917,7 +1917,7 @@ export class CrossReviewOrchestrator {
1917
1917
  convergence_scope: convergenceScope,
1918
1918
  started_at: startedAt,
1919
1919
  });
1920
- const updated = this.store.finalize(session.session_id, "max-rounds", "budget_preflight");
1920
+ const updated = await this.store.finalize(session.session_id, "max-rounds", "budget_preflight");
1921
1921
  this.emit({
1922
1922
  type: "round.blocked.budget_preflight",
1923
1923
  session_id: session.session_id,
@@ -1935,7 +1935,7 @@ export class CrossReviewOrchestrator {
1935
1935
  }
1936
1936
  if (this.isCancelled(session.session_id, input.signal)) {
1937
1937
  const rejected = selectAdapters(adapters, selectedPeers).map((adapter) => cancellationFailure(adapter.id, adapter.provider, adapter.model, "Session cancellation was requested before this round started."));
1938
- const round = this.store.appendRound(session.session_id, {
1938
+ const round = await this.store.appendRound(session.session_id, {
1939
1939
  caller_status: callerStatus,
1940
1940
  draft_file: draftFile,
1941
1941
  prompt_file: promptFile,
@@ -1945,7 +1945,7 @@ export class CrossReviewOrchestrator {
1945
1945
  convergence_scope: convergenceScope,
1946
1946
  started_at: startedAt,
1947
1947
  });
1948
- const updated = this.store.markCancelled(session.session_id, "session_cancelled");
1948
+ const updated = await this.store.markCancelled(session.session_id, "session_cancelled");
1949
1949
  return { session: updated, round, converged: false };
1950
1950
  }
1951
1951
  const settled = await Promise.all(selectAdapters(adapters, selectedPeers).map((adapter) => this.callPeerForReview(adapter, prompt, moderationSafePrompt, {
@@ -2027,14 +2027,14 @@ export class CrossReviewOrchestrator {
2027
2027
  latency_ms: peerResult.latency_ms,
2028
2028
  };
2029
2029
  rejected.push(failure);
2030
- this.store.savePeerFailure(session.session_id, roundNumber, failure);
2030
+ await this.store.savePeerFailure(session.session_id, roundNumber, failure);
2031
2031
  peers.push(peerResult);
2032
- this.store.savePeerResult(session.session_id, roundNumber, peerResult);
2032
+ await this.store.savePeerResult(session.session_id, roundNumber, peerResult);
2033
2033
  continue;
2034
2034
  }
2035
2035
  recoveriesUsedThisCall += 1;
2036
2036
  const decisionRetry = !containsReviewDecisionLexeme(peerResult.text);
2037
- this.store.savePeerResult(session.session_id, roundNumber, peerResult, "unparsed-response");
2037
+ await this.store.savePeerResult(session.session_id, roundNumber, peerResult, "unparsed-response");
2038
2038
  this.emit({
2039
2039
  type: "peer.format_recovery.started",
2040
2040
  session_id: session.session_id,
@@ -2093,7 +2093,7 @@ export class CrossReviewOrchestrator {
2093
2093
  latency_ms: peerResult.latency_ms,
2094
2094
  };
2095
2095
  rejected.push(failure);
2096
- this.store.savePeerFailure(session.session_id, roundNumber, failure);
2096
+ await this.store.savePeerFailure(session.session_id, roundNumber, failure);
2097
2097
  this.emit({
2098
2098
  type: "peer.format_recovery.budget_blocked",
2099
2099
  session_id: session.session_id,
@@ -2107,7 +2107,7 @@ export class CrossReviewOrchestrator {
2107
2107
  },
2108
2108
  });
2109
2109
  peers.push(peerResult);
2110
- this.store.savePeerResult(session.session_id, roundNumber, peerResult);
2110
+ await this.store.savePeerResult(session.session_id, roundNumber, peerResult);
2111
2111
  continue;
2112
2112
  }
2113
2113
  const recovered = await adapter.call(recoveryPrompt, {
@@ -2140,25 +2140,25 @@ export class CrossReviewOrchestrator {
2140
2140
  if (peerResult.status == null) {
2141
2141
  const failure = unparseableAfterRecoveryFailure(peerResult);
2142
2142
  rejected.push(failure);
2143
- this.store.savePeerFailure(session.session_id, roundNumber, failure);
2143
+ await this.store.savePeerFailure(session.session_id, roundNumber, failure);
2144
2144
  }
2145
2145
  }
2146
2146
  catch (error) {
2147
2147
  const failure = classifyProviderError(adapter.id, adapter.provider, adapter.model, error, this.config.retry.max_attempts, Date.parse(startedAt));
2148
2148
  rejected.push(failure);
2149
- this.store.savePeerFailure(session.session_id, roundNumber, failure);
2149
+ await this.store.savePeerFailure(session.session_id, roundNumber, failure);
2150
2150
  }
2151
2151
  }
2152
2152
  peers.push(peerResult);
2153
- this.store.savePeerResult(session.session_id, roundNumber, peerResult);
2153
+ await this.store.savePeerResult(session.session_id, roundNumber, peerResult);
2154
2154
  // v2.21.0 (caching): emit telemetry + persist manifest entry
2155
2155
  // when the peer call surfaced any cache activity. Best-effort —
2156
2156
  // failures here must not break the orchestrator critical path.
2157
- this.recordCacheTelemetry(session.session_id, roundNumber, peerResult);
2157
+ await this.recordCacheTelemetry(session.session_id, roundNumber, peerResult);
2158
2158
  if (peerResult.model_match === false) {
2159
2159
  const failure = silentModelDowngradeFailure(peerResult);
2160
2160
  rejected.push(failure);
2161
- this.store.savePeerFailure(session.session_id, roundNumber, failure);
2161
+ await this.store.savePeerFailure(session.session_id, roundNumber, failure);
2162
2162
  }
2163
2163
  }
2164
2164
  else if (item.failure) {
@@ -2170,7 +2170,7 @@ export class CrossReviewOrchestrator {
2170
2170
  // badly, or a policy/budget/content stop, stays in `rejected`.
2171
2171
  if (isSkippableFailure(failure)) {
2172
2172
  skipped.push(failure);
2173
- this.store.savePeerFailure(session.session_id, roundNumber, failure);
2173
+ await this.store.savePeerFailure(session.session_id, roundNumber, failure);
2174
2174
  this.emit({
2175
2175
  type: "session.peer_skipped_unavailable",
2176
2176
  session_id: session.session_id,
@@ -2187,7 +2187,7 @@ export class CrossReviewOrchestrator {
2187
2187
  }
2188
2188
  else {
2189
2189
  rejected.push(failure);
2190
- this.store.savePeerFailure(session.session_id, roundNumber, failure);
2190
+ await this.store.savePeerFailure(session.session_id, roundNumber, failure);
2191
2191
  }
2192
2192
  }
2193
2193
  }
@@ -2208,7 +2208,7 @@ export class CrossReviewOrchestrator {
2208
2208
  recovery_converged: isRecoveryRound && quorumConvergence.converged,
2209
2209
  quorum_peers: quorumPeers,
2210
2210
  };
2211
- const round = this.store.appendRound(session.session_id, {
2211
+ const round = await this.store.appendRound(session.session_id, {
2212
2212
  caller_status: callerStatus,
2213
2213
  draft_file: draftFile,
2214
2214
  prompt_file: promptFile,
@@ -2227,7 +2227,7 @@ export class CrossReviewOrchestrator {
2227
2227
  // v2.22.0 (B.P3): emit `session.budget_warning` if cumulative cost
2228
2228
  // crossed 75% of the session ceiling on this round. One-shot;
2229
2229
  // subsequent rounds in the same session won't re-emit.
2230
- this.checkBudgetWarning(session.session_id, round.round);
2230
+ await this.checkBudgetWarning(session.session_id, round.round);
2231
2231
  // v2.7.0 Evidence Broker: aggregate NEEDS_EVIDENCE asks from this
2232
2232
  // round into the session-level checklist. Each peer that returned
2233
2233
  // NEEDS_EVIDENCE with `caller_requests` contributes its asks; the
@@ -2244,7 +2244,7 @@ export class CrossReviewOrchestrator {
2244
2244
  }
2245
2245
  }
2246
2246
  if (evidenceAsks.length > 0) {
2247
- const checklist = this.store.appendEvidenceChecklistItems(session.session_id, round.round, evidenceAsks);
2247
+ const checklist = await this.store.appendEvidenceChecklistItems(session.session_id, round.round, evidenceAsks);
2248
2248
  this.emit({
2249
2249
  type: "session.evidence_checklist_updated",
2250
2250
  session_id: session.session_id,
@@ -2266,7 +2266,7 @@ export class CrossReviewOrchestrator {
2266
2266
  // to addressed. Skipping the call when evidenceAsks is empty would
2267
2267
  // miss exactly the case the inference is designed for.
2268
2268
  if ((this.store.read(session.session_id).evidence_checklist ?? []).length > 0) {
2269
- const addressDetection = this.store.runEvidenceChecklistAddressDetection(session.session_id, round.round);
2269
+ const addressDetection = await this.store.runEvidenceChecklistAddressDetection(session.session_id, round.round);
2270
2270
  if (addressDetection.not_resurfaced.length > 0) {
2271
2271
  // v3.5.0 (CRV2-2): event renamed + message corrected. The prior
2272
2272
  // `session.evidence_checklist_addressed` falsely implied the
@@ -2442,7 +2442,7 @@ export class CrossReviewOrchestrator {
2442
2442
  let updated = this.store.read(session.session_id);
2443
2443
  if (convergence.converged) {
2444
2444
  this.store.saveFinal(session.session_id, input.draft);
2445
- updated = this.store.finalize(session.session_id, "converged", convergence.recovery_converged ? "recovered_unanimity" : "unanimous_ready");
2445
+ updated = await this.store.finalize(session.session_id, "converged", convergence.recovery_converged ? "recovered_unanimity" : "unanimous_ready");
2446
2446
  }
2447
2447
  this.store.saveReport(session.session_id, sessionReportMarkdown(this.store.read(session.session_id), this.store.readEvents(session.session_id)));
2448
2448
  this.emit({
@@ -2482,7 +2482,7 @@ export class CrossReviewOrchestrator {
2482
2482
  // no-self-immediate-output invariant: between any peer's turn and
2483
2483
  // their next turn, at least one different peer must hold custody.
2484
2484
  if (sessionPeers.length < 2) {
2485
- this.store.finalize(session.session_id, "aborted", "circular_rotation_too_small");
2485
+ await this.store.finalize(session.session_id, "aborted", "circular_rotation_too_small");
2486
2486
  this.emit({
2487
2487
  type: "session.circular_rotation_too_small",
2488
2488
  session_id: session.session_id,
@@ -2512,7 +2512,7 @@ export class CrossReviewOrchestrator {
2512
2512
  let consecutiveNoChangeCount = 0;
2513
2513
  let lastRevisionRound = null;
2514
2514
  let cursor = 0;
2515
- this.store.setCircularState(session.session_id, {
2515
+ await this.store.setCircularState(session.session_id, {
2516
2516
  rotation_order: rotationOrder,
2517
2517
  consecutive_no_change_count: 0,
2518
2518
  last_revision_round: null,
@@ -2534,7 +2534,7 @@ export class CrossReviewOrchestrator {
2534
2534
  // no-self-immediate-output across the initial-draft → round 1 hop.
2535
2535
  if (!draft) {
2536
2536
  if (this.isCancelled(session.session_id, input.signal)) {
2537
- this.store.markCancelled(session.session_id, "session_cancelled");
2537
+ await this.store.markCancelled(session.session_id, "session_cancelled");
2538
2538
  return {
2539
2539
  session: this.store.read(session.session_id),
2540
2540
  final_text: draft,
@@ -2554,7 +2554,7 @@ export class CrossReviewOrchestrator {
2554
2554
  reasoning_effort_override: input.reasoning_effort_overrides?.[initRotator],
2555
2555
  caller: callerForLottery,
2556
2556
  });
2557
- this.store.saveGeneration(session.session_id, 0, initGeneration, "initial-draft");
2557
+ await this.store.saveGeneration(session.session_id, 0, initGeneration, "initial-draft");
2558
2558
  if (detectLeadDrift(initGeneration.text) || initGeneration.text.trim() === "") {
2559
2559
  this.emit({
2560
2560
  type: "session.lead_drift_detected",
@@ -2569,7 +2569,7 @@ export class CrossReviewOrchestrator {
2569
2569
  first_chars: initGeneration.text.slice(0, 100),
2570
2570
  },
2571
2571
  });
2572
- this.store.finalize(session.session_id, "aborted", "lead_meta_review_drift");
2572
+ await this.store.finalize(session.session_id, "aborted", "lead_meta_review_drift");
2573
2573
  return {
2574
2574
  session: this.store.read(session.session_id),
2575
2575
  final_text: undefined,
@@ -2591,7 +2591,7 @@ export class CrossReviewOrchestrator {
2591
2591
  : circularMaxRotations * rotationOrder.length;
2592
2592
  for (let round = 1; round <= maxCircularRounds; round++) {
2593
2593
  if (this.isCancelled(session.session_id, input.signal)) {
2594
- this.store.markCancelled(session.session_id, "session_cancelled");
2594
+ await this.store.markCancelled(session.session_id, "session_cancelled");
2595
2595
  return {
2596
2596
  session: this.store.read(session.session_id),
2597
2597
  final_text: draft,
@@ -2600,7 +2600,7 @@ export class CrossReviewOrchestrator {
2600
2600
  };
2601
2601
  }
2602
2602
  if (budgetExceeded(session, costLimit)) {
2603
- this.store.finalize(session.session_id, "max-rounds", "budget_exceeded");
2603
+ await this.store.finalize(session.session_id, "max-rounds", "budget_exceeded");
2604
2604
  this.emit({
2605
2605
  type: "session.budget_exceeded",
2606
2606
  session_id: session.session_id,
@@ -2630,7 +2630,7 @@ export class CrossReviewOrchestrator {
2630
2630
  reasoning_effort_override: input.reasoning_effort_overrides?.[rotator],
2631
2631
  caller: callerForLottery,
2632
2632
  });
2633
- this.store.saveGeneration(session.session_id, round, generation, "rotation");
2633
+ await this.store.saveGeneration(session.session_id, round, generation, "rotation");
2634
2634
  // Drift / empty / fabrication detection — identical contract to
2635
2635
  // ship mode's relator-revision branch. Two consecutive trips abort.
2636
2636
  const emptyText = generation.text.trim() === "";
@@ -2692,7 +2692,7 @@ export class CrossReviewOrchestrator {
2692
2692
  : fabricationDetected
2693
2693
  ? "lead_fabrication_repeated"
2694
2694
  : "lead_meta_review_drift";
2695
- this.store.finalize(session.session_id, "aborted", finalizeReason);
2695
+ await this.store.finalize(session.session_id, "aborted", finalizeReason);
2696
2696
  return {
2697
2697
  session: this.store.read(session.session_id),
2698
2698
  final_text: draft,
@@ -2777,7 +2777,7 @@ export class CrossReviewOrchestrator {
2777
2777
  reviewer_peers: rotationOrder,
2778
2778
  lead_peer: rotator,
2779
2779
  };
2780
- this.store.appendRound(session.session_id, {
2780
+ await this.store.appendRound(session.session_id, {
2781
2781
  caller_status: "READY",
2782
2782
  prompt_file: promptFile,
2783
2783
  peers: [peerResult],
@@ -2786,7 +2786,7 @@ export class CrossReviewOrchestrator {
2786
2786
  convergence_scope: convergenceScope,
2787
2787
  started_at: startedAt,
2788
2788
  });
2789
- this.store.setCircularState(session.session_id, {
2789
+ await this.store.setCircularState(session.session_id, {
2790
2790
  rotation_order: rotationOrder,
2791
2791
  consecutive_no_change_count: consecutiveNoChangeCount,
2792
2792
  last_revision_round: lastRevisionRound,
@@ -2820,7 +2820,7 @@ export class CrossReviewOrchestrator {
2820
2820
  last_revision_round: lastRevisionRound,
2821
2821
  },
2822
2822
  });
2823
- this.store.finalize(session.session_id, "converged", "circular_full_rotation_no_change");
2823
+ await this.store.finalize(session.session_id, "converged", "circular_full_rotation_no_change");
2824
2824
  return {
2825
2825
  session: this.store.read(session.session_id),
2826
2826
  final_text: draft,
@@ -2831,7 +2831,7 @@ export class CrossReviewOrchestrator {
2831
2831
  cursor = (cursor + 1) % rotationOrder.length;
2832
2832
  }
2833
2833
  // Exhausted max rotations without convergence.
2834
- this.store.finalize(session.session_id, "max-rounds", "circular_max_rotations_exceeded");
2834
+ await this.store.finalize(session.session_id, "max-rounds", "circular_max_rotations_exceeded");
2835
2835
  this.emit({
2836
2836
  type: "session.circular_max_rotations_exceeded",
2837
2837
  session_id: session.session_id,
@@ -2998,8 +2998,8 @@ export class CrossReviewOrchestrator {
2998
2998
  });
2999
2999
  if (missingFinancialVars.length) {
3000
3000
  const blockedSession = existingSession ??
3001
- this.store.init(input.task, callerForLottery, [], normalizeReviewFocus(input.review_focus, this.config));
3002
- this.store.finalize(blockedSession.session_id, "max-rounds", "financial_controls_missing");
3001
+ (await this.store.init(input.task, callerForLottery, [], normalizeReviewFocus(input.review_focus, this.config)));
3002
+ await this.store.finalize(blockedSession.session_id, "max-rounds", "financial_controls_missing");
3003
3003
  this.emit({
3004
3004
  type: "session.blocked.financial_controls_missing",
3005
3005
  session_id: blockedSession.session_id,
@@ -3019,7 +3019,7 @@ export class CrossReviewOrchestrator {
3019
3019
  let draft = input.initial_draft;
3020
3020
  // v3.5.0 (CRV2-1 + CRV2-6): persist requested-vs-effective budget +
3021
3021
  // max_rounds traceability once, before any round runs.
3022
- this.store.setSessionTraceability(session.session_id, {
3022
+ await this.store.setSessionTraceability(session.session_id, {
3023
3023
  requested_max_rounds: input.max_rounds ?? null,
3024
3024
  effective_max_rounds: input.until_stopped ? null : effectiveMaxRounds,
3025
3025
  requested_max_cost_usd: input.max_cost_usd ?? null,
@@ -3041,7 +3041,7 @@ export class CrossReviewOrchestrator {
3041
3041
  attachmentsPresent,
3042
3042
  });
3043
3043
  if (!preflight.pass) {
3044
- this.store.finalize(session.session_id, "aborted", "needs_evidence_preflight");
3044
+ await this.store.finalize(session.session_id, "aborted", "needs_evidence_preflight");
3045
3045
  this.emit({
3046
3046
  type: "session.evidence_preflight_failed",
3047
3047
  session_id: session.session_id,
@@ -3065,7 +3065,7 @@ export class CrossReviewOrchestrator {
3065
3065
  if (this.config.budget.require_rates_for_budget && costLimit != null) {
3066
3066
  const missingRates = selectedPeers.filter((peer) => !this.config.cost_rates[peer]);
3067
3067
  if (missingRates.length) {
3068
- this.store.finalize(session.session_id, "max-rounds", "budget_requires_rates");
3068
+ await this.store.finalize(session.session_id, "max-rounds", "budget_requires_rates");
3069
3069
  this.emit({
3070
3070
  type: "session.blocked.budget_requires_rates",
3071
3071
  session_id: session.session_id,
@@ -3102,7 +3102,7 @@ export class CrossReviewOrchestrator {
3102
3102
  let consecutiveLeadDrifts = 0;
3103
3103
  if (!draft) {
3104
3104
  if (this.isCancelled(session.session_id, input.signal)) {
3105
- this.store.markCancelled(session.session_id, "session_cancelled");
3105
+ await this.store.markCancelled(session.session_id, "session_cancelled");
3106
3106
  return {
3107
3107
  session: this.store.read(session.session_id),
3108
3108
  converged: false,
@@ -3120,7 +3120,7 @@ export class CrossReviewOrchestrator {
3120
3120
  reasoning_effort_override: input.reasoning_effort_overrides?.[leadPeer],
3121
3121
  caller: callerForLottery,
3122
3122
  });
3123
- this.store.saveGeneration(session.session_id, 0, generation, "initial-draft");
3123
+ await this.store.saveGeneration(session.session_id, 0, generation, "initial-draft");
3124
3124
  // v2.13.0: drift detection on initial-draft path. There is no
3125
3125
  // prior draft to fall back to here, so a drifted initial generation
3126
3126
  // aborts immediately. Only fires in `ship` mode — in `review` mode
@@ -3138,7 +3138,7 @@ export class CrossReviewOrchestrator {
3138
3138
  first_chars: generation.text.slice(0, 100),
3139
3139
  },
3140
3140
  });
3141
- this.store.finalize(session.session_id, "aborted", "lead_meta_review_drift");
3141
+ await this.store.finalize(session.session_id, "aborted", "lead_meta_review_drift");
3142
3142
  return {
3143
3143
  session: this.store.read(session.session_id),
3144
3144
  final_text: undefined,
@@ -3150,7 +3150,7 @@ export class CrossReviewOrchestrator {
3150
3150
  }
3151
3151
  for (let round = 1; round <= effectiveMaxRounds; round++) {
3152
3152
  if (this.isCancelled(session.session_id, input.signal)) {
3153
- this.store.markCancelled(session.session_id, "session_cancelled");
3153
+ await this.store.markCancelled(session.session_id, "session_cancelled");
3154
3154
  return {
3155
3155
  session: this.store.read(session.session_id),
3156
3156
  final_text: draft,
@@ -3181,7 +3181,7 @@ export class CrossReviewOrchestrator {
3181
3181
  };
3182
3182
  }
3183
3183
  if (budgetExceeded(session, costLimit)) {
3184
- this.store.finalize(session.session_id, "max-rounds", "budget_exceeded");
3184
+ await this.store.finalize(session.session_id, "max-rounds", "budget_exceeded");
3185
3185
  return {
3186
3186
  session: this.store.read(session.session_id),
3187
3187
  final_text: draft,
@@ -3247,7 +3247,7 @@ export class CrossReviewOrchestrator {
3247
3247
  reasoning_effort_override: input.reasoning_effort_overrides?.[leadPeer],
3248
3248
  caller: callerForLottery,
3249
3249
  });
3250
- this.store.saveGeneration(session.session_id, round, generation, "revision");
3250
+ await this.store.saveGeneration(session.session_id, round, generation, "revision");
3251
3251
  // v2.23.0: empty-text degeneracy detection. Provider-side parser
3252
3252
  // diagnostics (e.g. Anthropic extended-thinking returning only
3253
3253
  // `thinking`/`redacted_thinking` blocks with no final `text` block,
@@ -3402,7 +3402,7 @@ export class CrossReviewOrchestrator {
3402
3402
  finalizeReason = "lead_meta_audit_repeated";
3403
3403
  else
3404
3404
  finalizeReason = "lead_meta_review_drift";
3405
- this.store.finalize(session.session_id, "aborted", finalizeReason);
3405
+ await this.store.finalize(session.session_id, "aborted", finalizeReason);
3406
3406
  return {
3407
3407
  session: this.store.read(session.session_id),
3408
3408
  final_text: draft,
@@ -3418,7 +3418,7 @@ export class CrossReviewOrchestrator {
3418
3418
  }
3419
3419
  }
3420
3420
  }
3421
- this.store.finalize(session.session_id, "max-rounds", "max_rounds_without_unanimity");
3421
+ await this.store.finalize(session.session_id, "max-rounds", "max_rounds_without_unanimity");
3422
3422
  return {
3423
3423
  session: this.store.read(session.session_id),
3424
3424
  final_text: draft,