@lcv-ideas-software/cross-review 4.0.8 → 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.
@@ -175,7 +175,7 @@ const events = [];
175
175
  const holder = {};
176
176
  const orchestrator = new CrossReviewOrchestrator(config, (event) => {
177
177
  events.push(event.type);
178
- holder.orchestrator?.store.appendEvent(event);
178
+ void holder.orchestrator?.store.appendEvent(event);
179
179
  });
180
180
  holder.orchestrator = orchestrator;
181
181
  const adapterExpectations = [
@@ -260,27 +260,32 @@ const overlappingPem = [
260
260
  pemMarker("END", "RSA PRIVATE KEY"),
261
261
  ].join("\n");
262
262
  assert.equal(redact(`before ${overlappingPem} after`), "before [REDACTED] after");
263
+ // v4.1.0 / F4 security hardening: pre-v4.1.0 LEAKED unterminated PRIVATE
264
+ // KEY blocks (BEGIN without matching END — e.g. truncated logs). v4.1.0
265
+ // redacts from `begin.index` to end-of-string for unterminated blocks.
266
+ // Test was previously pinning the leak as expected behavior; updated to
267
+ // pin the no-leak contract.
263
268
  const unterminatedPem = `${pemMarker("BEGIN", "EC PRIVATE KEY")}\nmissing end`;
264
- assert.equal(redact(unterminatedPem), unterminatedPem);
269
+ assert.equal(redact(unterminatedPem), "[REDACTED]");
265
270
  const completeThenUnterminated = [
266
271
  pemBlock("RSA PRIVATE KEY", "first"),
267
272
  "preserve this middle text",
268
273
  pemMarker("BEGIN", "RSA PRIVATE KEY"),
269
274
  "missing end",
270
275
  ].join("\n");
271
- assert.equal(redact(completeThenUnterminated), [
272
- "[REDACTED]",
273
- "preserve this middle text",
274
- pemMarker("BEGIN", "RSA PRIVATE KEY"),
275
- "missing end",
276
- ].join("\n"));
276
+ assert.equal(redact(completeThenUnterminated), ["[REDACTED]", "preserve this middle text", "[REDACTED]"].join("\n"));
277
+ // v4.1.0 / F4 security hardening: pre-v4.1.0 left these adversarial
278
+ // inputs UNREDACTED (returning the original) because the unterminated-
279
+ // BEGIN branch silently fell through. v4.1.0 redacts from the first
280
+ // BEGIN marker to end-of-string. The performance budget (< 1 s for 2 000
281
+ // nested begins) is preserved.
277
282
  const adversarialPem = `${pemMarker("BEGIN", "EC PRIVATE KEY")}\n${pemMarker("BEGIN", "DSA PRIVATE KEY").repeat(2_000)}`;
278
283
  const adversarialStarted = Date.now();
279
- assert.equal(redact(adversarialPem), adversarialPem);
284
+ assert.equal(redact(adversarialPem), "[REDACTED]");
280
285
  assert.equal(Date.now() - adversarialStarted < 1_000, true);
281
286
  const repeatedSameLabelStarted = Date.now();
282
287
  const repeatedSameLabel = pemMarker("BEGIN", "RSA PRIVATE KEY").repeat(2_000);
283
- assert.equal(redact(repeatedSameLabel), repeatedSameLabel);
288
+ assert.equal(redact(repeatedSameLabel), "[REDACTED]");
284
289
  assert.equal(Date.now() - repeatedSameLabelStarted < 1_000, true);
285
290
  const constructedToken = ["sk", "test", "A".repeat(24)].join("-");
286
291
  assert.equal(redact(`token ${constructedToken}`), "token [REDACTED]");
@@ -601,28 +606,28 @@ assert.match(reviewPrompt, /not as instructions that override/);
601
606
  assert.match(reviewPrompt, /OUT OF SCOPE/);
602
607
  assert.ok(reviewPrompt.indexOf("## Review Focus") < reviewPrompt.indexOf("## Original Task"), "Review Focus must be front-loaded before the task body");
603
608
  assert.doesNotMatch(reviewPrompt, /\/focus\s+services\/billing/);
604
- const evidence = orchestrator.store.attachEvidence(result.session.session_id, {
609
+ const evidence = await orchestrator.store.attachEvidence(result.session.session_id, {
605
610
  label: "smoke evidence",
606
611
  content: "smoke evidence body",
607
612
  content_type: "text/markdown",
608
613
  extension: "md",
609
614
  });
610
615
  assert.equal(fs.existsSync(path.join(config.data_dir, "sessions", result.session.session_id, evidence.path)), true);
611
- const escalated = orchestrator.store.escalateToOperator(result.session.session_id, {
616
+ const escalated = await orchestrator.store.escalateToOperator(result.session.session_id, {
612
617
  reason: "smoke operator escalation",
613
618
  severity: "info",
614
619
  });
615
620
  assert.equal(escalated.operator_escalations?.at(-1)?.severity, "info");
616
- const fresh = orchestrator.store.init("fresh unfinished smoke session", "operator", probes);
621
+ const fresh = await orchestrator.store.init("fresh unfinished smoke session", "operator", probes);
617
622
  assert.equal(SWEEP_MIN_IDLE_MS, 24 * 60 * 60 * 1000);
618
- assert.equal(orchestrator.store.sweepIdle(0, "aborted", "fresh_smoke_stale").length, 0);
623
+ assert.equal((await orchestrator.store.sweepIdle(0, "aborted", "fresh_smoke_stale")).length, 0);
619
624
  assert.equal(orchestrator.store.read(fresh.session_id).outcome, undefined);
620
- const stale = orchestrator.store.init("old unfinished smoke session", "operator", probes);
625
+ const stale = await orchestrator.store.init("old unfinished smoke session", "operator", probes);
621
626
  const staleMetaPath = orchestrator.store.metaPath(stale.session_id);
622
627
  const staleMeta = JSON.parse(fs.readFileSync(staleMetaPath, "utf8"));
623
628
  staleMeta.updated_at = new Date(Date.now() - 25 * 60 * 60 * 1000).toISOString();
624
629
  fs.writeFileSync(staleMetaPath, `${JSON.stringify(staleMeta, null, 2)}\n`, "utf8");
625
- const swept = orchestrator.store.sweepIdle(0, "aborted", "smoke_stale");
630
+ const swept = await orchestrator.store.sweepIdle(0, "aborted", "smoke_stale");
626
631
  assert.equal(swept.some((session) => session.session_id === stale.session_id), true);
627
632
  assert.equal(orchestrator.store.read(stale.session_id).outcome, "aborted");
628
633
  assert.equal(orchestrator.store.read(fresh.session_id).outcome, undefined);
@@ -861,8 +866,8 @@ assert.equal(untilStoppedDefaultBudget.converged, false);
861
866
  assert.equal(untilStoppedDefaultBudget.session.outcome, "max-rounds");
862
867
  assert.equal(untilStoppedDefaultBudget.session.outcome_reason, "budget_exceeded");
863
868
  assert.equal(untilStoppedDefaultBudget.rounds, 1);
864
- const recoverySession = orchestrator.store.init("interrupted smoke session", "operator", probes);
865
- orchestrator.store.markInFlight(recoverySession.session_id, {
869
+ const recoverySession = await orchestrator.store.init("interrupted smoke session", "operator", probes);
870
+ await orchestrator.store.markInFlight(recoverySession.session_id, {
866
871
  round: 1,
867
872
  peers: ["codex"],
868
873
  started_at: new Date().toISOString(),
@@ -873,7 +878,7 @@ orchestrator.store.markInFlight(recoverySession.session_id, {
873
878
  reviewer_peers: ["codex"],
874
879
  },
875
880
  });
876
- const recoveredInterrupted = orchestrator.store.recoverInterruptedSessions();
881
+ const recoveredInterrupted = await orchestrator.store.recoverInterruptedSessions();
877
882
  assert.equal(recoveredInterrupted.some((session) => session.session_id === recoverySession.session_id), true);
878
883
  assert.equal(orchestrator.store.read(recoverySession.session_id).control?.status, "recovered_after_restart");
879
884
  const abortController = new AbortController();
@@ -900,6 +905,7 @@ const preflightBlocked = await preflightOrchestrator.askPeers({
900
905
  assert.equal(preflightBlocked.converged, false);
901
906
  assert.equal(preflightBlocked.round.rejected.at(-1)?.failure_class, "budget_preflight");
902
907
  assert.equal(preflightBlocked.session.outcome_reason, "budget_preflight");
908
+ await orchestrator.store.flushPendingEvents();
903
909
  const eventful = orchestrator.store.readEvents(formatRecovered.session.session_id);
904
910
  assert.equal(eventful.some((event) => event.type === "round.completed"), true);
905
911
  assert.equal(eventful.some((event) => event.type === "peer.token.delta"), true);
@@ -927,6 +933,7 @@ const directStreamChars = directStreamEvents
927
933
  .reduce((total, event) => total + Number(event.data?.chars ?? 0), 0);
928
934
  assert.equal(directStreamChars, directStubResult.text.length);
929
935
  assert.deepEqual(eventful.map((event) => event.seq), eventful.map((_, index) => index + 1));
936
+ await orchestrator.store.flushPendingEvents();
930
937
  const metrics = orchestrator.store.metrics();
931
938
  assert.equal(metrics.fallback_events, 1);
932
939
  assert.equal((metrics.peer_failures.cancelled ?? 0) >= 1, true);
@@ -940,8 +947,8 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
940
947
  ...config,
941
948
  data_dir: smokeTmpDir("session-doctor"),
942
949
  });
943
- const doctorSession = doctorStore.init("doctor self-lead legacy fixture", "claude", []);
944
- doctorStore.markInFlight(doctorSession.session_id, {
950
+ const doctorSession = await doctorStore.init("doctor self-lead legacy fixture", "claude", []);
951
+ await doctorStore.markInFlight(doctorSession.session_id, {
945
952
  round: 1,
946
953
  peers: ["codex"],
947
954
  started_at: new Date().toISOString(),
@@ -955,25 +962,25 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
955
962
  lead_peer: "claude",
956
963
  },
957
964
  });
958
- doctorStore.appendEvent({
965
+ await doctorStore.appendEvent({
959
966
  type: "peer.token.delta",
960
967
  session_id: doctorSession.session_id,
961
968
  round: 1,
962
969
  peer: "codex",
963
970
  data: { chars: 12 },
964
971
  });
965
- doctorStore.appendEvent({
972
+ await doctorStore.appendEvent({
966
973
  type: "peer.token.completed",
967
974
  session_id: doctorSession.session_id,
968
975
  round: 1,
969
976
  peer: "codex",
970
977
  data: { chars: 12 },
971
978
  });
972
- const malformedSession = doctorStore.init("doctor malformed events fixture", "operator", []);
979
+ const malformedSession = await doctorStore.init("doctor malformed events fixture", "operator", []);
973
980
  fs.writeFileSync(doctorStore.eventsPath(malformedSession.session_id), "{bad-json\n", "utf8");
974
981
  // v2.22.0 (A.P2): self_lead_metadata is hidden by default. Pass
975
982
  // includeLegacy=true here to preserve the original behavior assertion.
976
- const doctor = doctorStore.sessionDoctor(5, true);
983
+ const doctor = await doctorStore.sessionDoctor(5, true);
977
984
  assert.equal(doctor.totals.sessions, 2);
978
985
  assert.equal(doctor.totals.open, 2);
979
986
  assert.equal(doctor.totals.self_lead_metadata, 1);
@@ -996,8 +1003,8 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
996
1003
  data_dir: smokeTmpDir("session-doctor-legacy"),
997
1004
  });
998
1005
  // Fixture: legacy self-lead session (caller==lead_peer)
999
- const legacySession = filterStore.init("legacy self-lead fixture", "claude", []);
1000
- filterStore.markInFlight(legacySession.session_id, {
1006
+ const legacySession = await filterStore.init("legacy self-lead fixture", "claude", []);
1007
+ await filterStore.markInFlight(legacySession.session_id, {
1001
1008
  round: 1,
1002
1009
  peers: ["codex"],
1003
1010
  started_at: new Date().toISOString(),
@@ -1012,13 +1019,13 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
1012
1019
  },
1013
1020
  });
1014
1021
  // Default call: array hidden, totals visible, recommendation mentions include_legacy.
1015
- const defaultReport = filterStore.sessionDoctor(20);
1022
+ const defaultReport = await filterStore.sessionDoctor(20);
1016
1023
  assert.equal(defaultReport.totals.self_lead_metadata, 1, "totals.self_lead_metadata count must remain visible when array is suppressed");
1017
1024
  assert.equal(defaultReport.findings.self_lead_metadata.length, 0, "findings.self_lead_metadata must be empty by default (legacy noise suppression)");
1018
1025
  const hasIncludeLegacyHint = defaultReport.recommendations.some((rec) => rec.includes("include_legacy=true"));
1019
1026
  assert.equal(hasIncludeLegacyHint, true, "recommendation must mention include_legacy=true when array is suppressed and count > 0");
1020
1027
  // Explicit include_legacy=true: array populated.
1021
- const inclusiveReport = filterStore.sessionDoctor(20, true);
1028
+ const inclusiveReport = await filterStore.sessionDoctor(20, true);
1022
1029
  assert.equal(inclusiveReport.totals.self_lead_metadata, 1, "totals must match between default and inclusive calls");
1023
1030
  assert.equal(inclusiveReport.findings.self_lead_metadata.length, 1, "findings.self_lead_metadata must be populated when include_legacy=true");
1024
1031
  assert.equal(inclusiveReport.findings.self_lead_metadata[0]?.lead_peer, "claude");
@@ -1034,7 +1041,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
1034
1041
  ...config,
1035
1042
  data_dir: smokeTmpDir("session-doctor-drilldown"),
1036
1043
  });
1037
- const driveSession = drillStore.init("evidence drill-down fixture", "operator", []);
1044
+ const driveSession = await drillStore.init("evidence drill-down fixture", "operator", []);
1038
1045
  // Fabricate evidence_checklist directly via meta path: 3 open items
1039
1046
  // (codex x1, gemini x2), one of them chronic (round_count=4).
1040
1047
  const metaPath = drillStore.metaPath(driveSession.session_id);
@@ -1076,7 +1083,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
1076
1083
  },
1077
1084
  ];
1078
1085
  fs.writeFileSync(metaPath, JSON.stringify(fabricatedMeta, null, 2));
1079
- const drillReport = drillStore.sessionDoctor(20);
1086
+ const drillReport = await drillStore.sessionDoctor(20);
1080
1087
  const entry = drillReport.findings.open_evidence_sessions.find((e) => e.session_id === driveSession.session_id);
1081
1088
  assert.ok(entry, "open_evidence_sessions must include the fabricated session");
1082
1089
  assert.equal(entry?.open_evidence_items, 3);
@@ -1097,7 +1104,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
1097
1104
  data_dir: smokeTmpDir("budget-warning"),
1098
1105
  budget: { ...config.budget, max_session_cost_usd: 20 },
1099
1106
  });
1100
- const budgetSession = budgetStore.init("budget warning fixture", "operator", []);
1107
+ const budgetSession = await budgetStore.init("budget warning fixture", "operator", []);
1101
1108
  // Verify init snapshotted the ceiling.
1102
1109
  const initial = budgetStore.read(budgetSession.session_id);
1103
1110
  assert.equal(initial.cost_ceiling_usd, 20, "cost_ceiling_usd must snapshot config at init");
@@ -1126,7 +1133,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
1126
1133
  const threshold = ceilingForCheck * 0.75;
1127
1134
  assert.equal(cumulative1 >= threshold, true, "fixture must cross 75% threshold");
1128
1135
  assert.equal(seededMeta.budget_warning_emitted, false, "warning must not have fired yet");
1129
- budgetStore.markBudgetWarningEmitted(budgetSession.session_id);
1136
+ await budgetStore.markBudgetWarningEmitted(budgetSession.session_id);
1130
1137
  const afterFirst = budgetStore.read(budgetSession.session_id);
1131
1138
  assert.equal(afterFirst.budget_warning_emitted, true, "markBudgetWarningEmitted must persist the one-shot guard");
1132
1139
  // Round 2: cumulative = 18 (still over threshold). Emit guard must
@@ -1149,7 +1156,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
1149
1156
  data_dir: smokeTmpDir("budget-warning-no-ceiling"),
1150
1157
  budget: { ...config.budget, max_session_cost_usd: undefined },
1151
1158
  });
1152
- const noCeilingSession = noCeilingStore.init("no ceiling fixture", "operator", []);
1159
+ const noCeilingSession = await noCeilingStore.init("no ceiling fixture", "operator", []);
1153
1160
  const noCeilingMeta = noCeilingStore.read(noCeilingSession.session_id);
1154
1161
  assert.equal(noCeilingMeta.cost_ceiling_usd, null, "cost_ceiling_usd must be null when config.max_session_cost_usd is unset");
1155
1162
  console.log("[smoke] budget_warning_emit_test: PASS");
@@ -1213,10 +1220,10 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
1213
1220
  const { SessionStore } = await import("../src/core/session-store.js");
1214
1221
  const fsModule = await import("node:fs");
1215
1222
  const seqStoreA = new SessionStore(config);
1216
- const seqMeta = seqStoreA.init("seq-durability-test", "operator", []);
1223
+ const seqMeta = await seqStoreA.init("seq-durability-test", "operator", []);
1217
1224
  const seqId = seqMeta.session_id;
1218
1225
  // Emit a normal event.
1219
- seqStoreA.appendEvent({
1226
+ await seqStoreA.appendEvent({
1220
1227
  type: "session.heartbeat",
1221
1228
  session_id: seqId,
1222
1229
  message: "first",
@@ -1231,7 +1238,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
1231
1238
  interceptorFired = true;
1232
1239
  throw new Error("simulated EIO");
1233
1240
  });
1234
- seqStoreA.appendEvent({
1241
+ await seqStoreA.appendEvent({
1235
1242
  type: "session.heartbeat",
1236
1243
  session_id: seqId,
1237
1244
  message: "should-fail",
@@ -1239,7 +1246,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
1239
1246
  // Restore fs and try again — the intended seq (2) must still be
1240
1247
  // available, not skipped to 3.
1241
1248
  fsModule.default.appendFileSync = realAppend;
1242
- seqStoreA.appendEvent({
1249
+ await seqStoreA.appendEvent({
1243
1250
  type: "session.heartbeat",
1244
1251
  session_id: seqId,
1245
1252
  message: "after-recovery",
@@ -1250,7 +1257,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
1250
1257
  // Restart simulation: fresh SessionStore reads from disk and the next
1251
1258
  // seq should be 3 (current line count + 1).
1252
1259
  const seqStoreB = new SessionStore(config);
1253
- seqStoreB.appendEvent({
1260
+ await seqStoreB.appendEvent({
1254
1261
  type: "session.heartbeat",
1255
1262
  session_id: seqId,
1256
1263
  message: "after-restart",
@@ -1269,9 +1276,9 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
1269
1276
  {
1270
1277
  const { SessionStore } = await import("../src/core/session-store.js");
1271
1278
  const flightStore = new SessionStore(config);
1272
- const flightMeta = flightStore.init("mark-in-flight-guard-test", "operator", []);
1279
+ const flightMeta = await flightStore.init("mark-in-flight-guard-test", "operator", []);
1273
1280
  const flightId = flightMeta.session_id;
1274
- flightStore.markInFlight(flightId, {
1281
+ await flightStore.markInFlight(flightId, {
1275
1282
  round: 1,
1276
1283
  peers: [...PEERS],
1277
1284
  started_at: new Date().toISOString(),
@@ -1284,7 +1291,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
1284
1291
  });
1285
1292
  let secondMarkRejected = false;
1286
1293
  try {
1287
- flightStore.markInFlight(flightId, {
1294
+ await flightStore.markInFlight(flightId, {
1288
1295
  round: 2,
1289
1296
  peers: [...PEERS],
1290
1297
  started_at: new Date().toISOString(),
@@ -1358,13 +1365,13 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
1358
1365
  {
1359
1366
  const { SessionStore } = await import("../src/core/session-store.js");
1360
1367
  const staleStore = new SessionStore(config);
1361
- const staleMeta = staleStore.init("stale-session-abort-test", "operator", []);
1368
+ const staleMeta = await staleStore.init("stale-session-abort-test", "operator", []);
1362
1369
  const staleId = staleMeta.session_id;
1363
1370
  const staleMetaPath = staleStore.metaPath(staleId);
1364
1371
  const staleRaw = JSON.parse(fs.readFileSync(staleMetaPath, "utf8"));
1365
1372
  staleRaw.updated_at = new Date(Date.now() - 25 * 60 * 60 * 1000).toISOString();
1366
1373
  fs.writeFileSync(staleMetaPath, JSON.stringify(staleRaw, null, 2), "utf8");
1367
- const sweep = staleStore.abortStaleSessions();
1374
+ const sweep = await staleStore.abortStaleSessions();
1368
1375
  assert.ok(sweep.aborted >= 1, `abortStaleSessions must abort ≥1 stale session, got ${sweep.aborted}`);
1369
1376
  const after = staleStore.read(staleId);
1370
1377
  assert.equal(after.outcome, "aborted");
@@ -1376,9 +1383,9 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
1376
1383
  {
1377
1384
  const { SessionStore } = await import("../src/core/session-store.js");
1378
1385
  const inflightStore = new SessionStore(config);
1379
- const inflightMeta = inflightStore.init("stale-session-skip-test", "operator", []);
1386
+ const inflightMeta = await inflightStore.init("stale-session-skip-test", "operator", []);
1380
1387
  const inflightId = inflightMeta.session_id;
1381
- inflightStore.markInFlight(inflightId, {
1388
+ await inflightStore.markInFlight(inflightId, {
1382
1389
  round: 1,
1383
1390
  peers: [...PEERS],
1384
1391
  started_at: new Date().toISOString(),
@@ -1393,7 +1400,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
1393
1400
  const inflightRaw = JSON.parse(fs.readFileSync(inflightMetaPath, "utf8"));
1394
1401
  inflightRaw.updated_at = new Date(Date.now() - 25 * 60 * 60 * 1000).toISOString();
1395
1402
  fs.writeFileSync(inflightMetaPath, JSON.stringify(inflightRaw, null, 2), "utf8");
1396
- const sweep = inflightStore.abortStaleSessions();
1403
+ const sweep = await inflightStore.abortStaleSessions();
1397
1404
  const after = inflightStore.read(inflightId);
1398
1405
  assert.equal(after.outcome, undefined, `in-flight session must NOT be aborted by stale sweep (got outcome=${after.outcome}, sweep=${JSON.stringify(sweep)})`);
1399
1406
  console.log("[smoke] stale_session_skipped_when_running_test: PASS");
@@ -1831,7 +1838,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
1831
1838
  const meta = tpOrch.store.read(sessionId);
1832
1839
  meta.evidence_checklist = fixtureItems;
1833
1840
  fs.writeFileSync(path.join(tpConfig.data_dir, "sessions", sessionId, "meta.json"), JSON.stringify(meta, null, 2));
1834
- const ad = tpOrch.store.runEvidenceChecklistAddressDetection(sessionId, FIXTURE_ROUND);
1841
+ const ad = await tpOrch.store.runEvidenceChecklistAddressDetection(sessionId, FIXTURE_ROUND);
1835
1842
  // (1) The open item with last_round===currentRound MUST NOT appear under
1836
1843
  // peer_resurfaced_terminal. This is the regression the buggy
1837
1844
  // truthy-OR predicate would have triggered.
@@ -1934,7 +1941,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
1934
1941
  });
1935
1942
  const item = opRound1.session.evidence_checklist?.[0];
1936
1943
  assert.ok(item, "R1 must produce a checklist item");
1937
- const result = opOrch.store.setEvidenceChecklistItemStatus(opRound1.session.session_id, item.id, "satisfied", { note: "smoke verified manually", by: "operator" });
1944
+ const result = await opOrch.store.setEvidenceChecklistItemStatus(opRound1.session.session_id, item.id, "satisfied", { note: "smoke verified manually", by: "operator" });
1938
1945
  assert.equal(result.item.status, "satisfied", "mutator must set status to satisfied");
1939
1946
  assert.equal(result.history_entry.from, "open");
1940
1947
  assert.equal(result.history_entry.to, "satisfied");
@@ -1956,7 +1963,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
1956
1963
  // type-system level: the mutator's signature excludes "addressed". We
1957
1964
  // assert that calling setEvidenceChecklistItemStatus with "deferred"
1958
1965
  // works as a different terminal transition.
1959
- const result2 = opOrch.store.setEvidenceChecklistItemStatus(opRound1.session.session_id, item.id, "deferred", { note: "retract satisfied, defer instead", by: "operator" });
1966
+ const result2 = await opOrch.store.setEvidenceChecklistItemStatus(opRound1.session.session_id, item.id, "deferred", { note: "retract satisfied, defer instead", by: "operator" });
1960
1967
  assert.equal(result2.item.status, "deferred");
1961
1968
  assert.equal(result2.history_entry.from, "satisfied");
1962
1969
  assert.equal(result2.history_entry.to, "deferred");
@@ -1994,6 +2001,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
1994
2001
  caller: "operator",
1995
2002
  peers: ["codex"],
1996
2003
  });
2004
+ await phOrch.store.flushPendingEvents();
1997
2005
  const metrics = phOrch.store.metrics();
1998
2006
  const perPeer = metrics.per_peer_health;
1999
2007
  assert.ok(perPeer, "metrics must include per_peer_health");
@@ -2835,7 +2843,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
2835
2843
  // would leave the durable log empty.
2836
2844
  const holder = {};
2837
2845
  const rollupOrch = new CrossReviewOrchestrator(cfg, (event) => {
2838
- holder.orch?.store.appendEvent(event);
2846
+ void holder.orch?.store.appendEvent(event);
2839
2847
  });
2840
2848
  holder.orch = rollupOrch;
2841
2849
  const r1 = await rollupOrch.askPeers({
@@ -2851,6 +2859,9 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
2851
2859
  caller: "operator",
2852
2860
  peers: ["claude"],
2853
2861
  });
2862
+ // v4.1.0: emit pipeline uses `void store.appendEvent(...)` (fire-
2863
+ // and-forget). Flush pending writes before reading the events file.
2864
+ await rollupOrch.store.flushPendingEvents();
2854
2865
  const rollup = rollupOrch.store.aggregateShadowJudgments();
2855
2866
  assert.ok(rollup.decisions_total >= 1, `aggregate must record at least 1 shadow decision (got ${rollup.decisions_total})`);
2856
2867
  assert.ok(rollup.would_promote_total >= 1, `aggregate must record at least 1 would_promote (got ${rollup.would_promote_total})`);
@@ -2860,7 +2871,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
2860
2871
  assert.ok(claudeStats.would_promote >= 1);
2861
2872
  assert.ok((claudeStats.by_confidence.verified ?? 0) >= 1);
2862
2873
  assert.ok(claudeStats.first_seen_at && claudeStats.last_seen_at);
2863
- const metrics = rollupOrch.store.metrics();
2874
+ const metrics = rollupOrch.store.metrics(); // already flushed above
2864
2875
  assert.ok(metrics.shadow_judgment, "metrics().shadow_judgment must be present");
2865
2876
  assert.equal(metrics.shadow_judgment.decisions_total, rollup.decisions_total);
2866
2877
  console.log("[smoke] metrics_shadow_judgment_rollup_test: PASS");
@@ -2898,7 +2909,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
2898
2909
  const holder = {};
2899
2910
  const orch = new CrossReviewOrchestrator(cfg, (e) => {
2900
2911
  events.push({ type: e.type, data: e.data });
2901
- holder.orch?.store.appendEvent(e);
2912
+ void holder.orch?.store.appendEvent(e);
2902
2913
  });
2903
2914
  holder.orch = orch;
2904
2915
  // Drive runUntilUnanimous with FORCE_DRIFT (lead generation triggers
@@ -2942,7 +2953,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
2942
2953
  const holder = {};
2943
2954
  const orch = new CrossReviewOrchestrator(cfg, (e) => {
2944
2955
  events.push({ type: e.type, data: e.data });
2945
- holder.orch?.store.appendEvent(e);
2956
+ void holder.orch?.store.appendEvent(e);
2946
2957
  });
2947
2958
  holder.orch = orch;
2948
2959
  const result = await orch.runUntilUnanimous({
@@ -2980,7 +2991,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
2980
2991
  const holder = {};
2981
2992
  const orch = new CrossReviewOrchestrator(cfg, (e) => {
2982
2993
  events.push({ type: e.type, data: e.data });
2983
- holder.orch?.store.appendEvent(e);
2994
+ void holder.orch?.store.appendEvent(e);
2984
2995
  });
2985
2996
  holder.orch = orch;
2986
2997
  const result = await orch.runUntilUnanimous({
@@ -3017,7 +3028,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
3017
3028
  const holder = {};
3018
3029
  const orch = new CrossReviewOrchestrator(cfg, (e) => {
3019
3030
  events.push({ type: e.type, data: e.data });
3020
- holder.orch?.store.appendEvent(e);
3031
+ void holder.orch?.store.appendEvent(e);
3021
3032
  });
3022
3033
  holder.orch = orch;
3023
3034
  await orch.runUntilUnanimous({
@@ -3052,7 +3063,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
3052
3063
  };
3053
3064
  const holder = {};
3054
3065
  const aeOrch = new CrossReviewOrchestrator(cfg, (e) => {
3055
- holder.orch?.store.appendEvent(e);
3066
+ void holder.orch?.store.appendEvent(e);
3056
3067
  });
3057
3068
  holder.orch = aeOrch;
3058
3069
  // Init session, attach 2 evidence files, run askPeers, read R1 prompt.
@@ -3071,12 +3082,12 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
3071
3082
  const sessionId = initial.session.session_id;
3072
3083
  // The first askPeers above completed R1 already without attachments
3073
3084
  // — that is the "before" baseline. Now attach files and run R2.
3074
- aeOrch.store.attachEvidence(sessionId, {
3085
+ await aeOrch.store.attachEvidence(sessionId, {
3075
3086
  label: "gates-output",
3076
3087
  content: "EXIT 0 typecheck\nEXIT 0 lint\nEXIT 0 build\nEXIT 0 smoke 41/41 PASS\n",
3077
3088
  extension: "log",
3078
3089
  });
3079
- aeOrch.store.attachEvidence(sessionId, {
3090
+ await aeOrch.store.attachEvidence(sessionId, {
3080
3091
  label: "diff-stat",
3081
3092
  content: " path/to/file.ts | +12/-3\n 1 file changed, 12 insertions, 3 deletions\n",
3082
3093
  extension: "txt",
@@ -3132,7 +3143,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
3132
3143
  const sessionId = initial.session.session_id;
3133
3144
  const big = "X".repeat(30_000);
3134
3145
  for (let i = 0; i < 4; i++) {
3135
- capOrch.store.attachEvidence(sessionId, {
3146
+ await capOrch.store.attachEvidence(sessionId, {
3136
3147
  label: `att-${i}`,
3137
3148
  content: big,
3138
3149
  extension: "txt",
@@ -3316,7 +3327,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
3316
3327
  };
3317
3328
  const holder = {};
3318
3329
  const prOrch = new CrossReviewOrchestrator(cfg, (event) => {
3319
- holder.orch?.store.appendEvent(event);
3330
+ void holder.orch?.store.appendEvent(event);
3320
3331
  });
3321
3332
  holder.orch = prOrch;
3322
3333
  // R1: produce a NEEDS_EVIDENCE ask. R2: ask resurfaces (so far ground
@@ -3351,6 +3362,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
3351
3362
  caller: "operator",
3352
3363
  peers: ["claude"],
3353
3364
  });
3365
+ await prOrch.store.flushPendingEvents();
3354
3366
  const report = prOrch.store.computeJudgmentPrecisionReport();
3355
3367
  assert.ok(report.decisions_total >= 1, `at least 1 decision recorded`);
3356
3368
  const claudeStats = report.by_judge_peer.claude;
@@ -3400,7 +3412,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
3400
3412
  const holder = {};
3401
3413
  const acOrch = new CrossReviewOrchestrator(cfg, (e) => {
3402
3414
  events.push(e.type);
3403
- holder.orch?.store.appendEvent(e);
3415
+ void holder.orch?.store.appendEvent(e);
3404
3416
  });
3405
3417
  holder.orch = acOrch;
3406
3418
  // R1: produce a NEEDS_EVIDENCE ask via FORCE_NEEDS_EVIDENCE.
@@ -3470,9 +3482,9 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
3470
3482
  peers: ["claude"],
3471
3483
  });
3472
3484
  const originalId = initial.session.session_id;
3473
- cvOrch.store.finalize(originalId, "max-rounds", "test_finalize");
3485
+ await cvOrch.store.finalize(originalId, "max-rounds", "test_finalize");
3474
3486
  // Contest it.
3475
- const contestation = cvOrch.store.contestVerdict({
3487
+ const contestation = await cvOrch.store.contestVerdict({
3476
3488
  session_id: originalId,
3477
3489
  reason: "Caller disagrees with the verdict; new evidence has surfaced.",
3478
3490
  new_task: "Contest test re-deliberation",
@@ -3491,7 +3503,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
3491
3503
  // Double-contesting must throw.
3492
3504
  let threw = null;
3493
3505
  try {
3494
- cvOrch.store.contestVerdict({
3506
+ await cvOrch.store.contestVerdict({
3495
3507
  session_id: originalId,
3496
3508
  reason: "Trying to contest twice",
3497
3509
  new_task: "Should not happen",
@@ -3513,7 +3525,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
3513
3525
  // but typically the session still has no outcome until finalize.)
3514
3526
  threw = null;
3515
3527
  try {
3516
- cvOrch.store.contestVerdict({
3528
+ await cvOrch.store.contestVerdict({
3517
3529
  session_id: inFlight.session.session_id,
3518
3530
  reason: "in-flight should reject",
3519
3531
  new_task: "should not happen",
@@ -3919,8 +3931,8 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
3919
3931
  };
3920
3932
  // Scenario A: finalize("converged") on a session whose latest round
3921
3933
  // did NOT converge MUST be rejected with a structured error code.
3922
- const sess = invariantStore.init("invariant-fixture", "operator", []);
3923
- invariantStore.appendRound(sess.session_id, {
3934
+ const sess = await invariantStore.init("invariant-fixture", "operator", []);
3935
+ await invariantStore.appendRound(sess.session_id, {
3924
3936
  caller_status: "READY",
3925
3937
  prompt_file: "round-1-prompt.md",
3926
3938
  peers: [],
@@ -3948,7 +3960,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
3948
3960
  });
3949
3961
  let rejected = null;
3950
3962
  try {
3951
- invariantStore.finalize(sess.session_id, "converged", "unanimous_ready");
3963
+ await invariantStore.finalize(sess.session_id, "converged", "unanimous_ready");
3952
3964
  }
3953
3965
  catch (err) {
3954
3966
  rejected = err;
@@ -3959,8 +3971,8 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
3959
3971
  assert.equal(invariantStore.read(sess.session_id).outcome, undefined, "finalize-reject MUST NOT mutate meta.outcome");
3960
3972
  // Scenario B: finalize("converged") on a session whose latest round
3961
3973
  // DID converge succeeds and leaves a consistent meta.
3962
- const sess2 = invariantStore.init("invariant-fixture-2", "operator", []);
3963
- invariantStore.appendRound(sess2.session_id, {
3974
+ const sess2 = await invariantStore.init("invariant-fixture-2", "operator", []);
3975
+ await invariantStore.appendRound(sess2.session_id, {
3964
3976
  caller_status: "READY",
3965
3977
  prompt_file: "round-1-prompt.md",
3966
3978
  peers: [],
@@ -3986,14 +3998,14 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
3986
3998
  },
3987
3999
  started_at: new Date().toISOString(),
3988
4000
  });
3989
- const finalized = invariantStore.finalize(sess2.session_id, "converged", "unanimous_ready");
4001
+ const finalized = await invariantStore.finalize(sess2.session_id, "converged", "unanimous_ready");
3990
4002
  assert.equal(finalized.outcome, "converged");
3991
4003
  assert.equal(finalized.convergence_health?.state, "converged");
3992
4004
  // Scenario C: appendRound on a finalized session MUST throw with the
3993
4005
  // structured code.
3994
4006
  let appendRejected = null;
3995
4007
  try {
3996
- invariantStore.appendRound(sess2.session_id, {
4008
+ await invariantStore.appendRound(sess2.session_id, {
3997
4009
  caller_status: "READY",
3998
4010
  prompt_file: "round-2-prompt.md",
3999
4011
  peers: [],
@@ -4932,9 +4944,9 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
4932
4944
  entries: [],
4933
4945
  };
4934
4946
  fs.mkdirSync(path.join(config.data_dir, "sessions", manifestSession), { recursive: true });
4935
- writeCacheManifest(config.data_dir, manifestSession, manifestData);
4947
+ await writeCacheManifest(config.data_dir, manifestSession, manifestData);
4936
4948
  for (let i = 0; i < 5; i += 1) {
4937
- appendCacheManifestEntry(config.data_dir, manifestSession, {
4949
+ await appendCacheManifestEntry(config.data_dir, manifestSession, {
4938
4950
  ts: new Date().toISOString(),
4939
4951
  round: i + 1,
4940
4952
  peer: "codex",
@@ -5283,7 +5295,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
5283
5295
  /this\.store\.finalize\([\s\S]{0,100}"max-rounds"[\s\S]{0,100}"circular_max_rotations_exceeded"/.test(orchSrc), "v2.25.0 / circular_mode: orchestrator finalizes with outcome=max-rounds + reason=circular_max_rotations_exceeded");
5284
5296
  // (9) Meta carries circular_state with the expected shape.
5285
5297
  assert.ok(/circular_state\?:\s*\{[\s\S]{0,300}rotation_order:\s*PeerId\[\][\s\S]{0,300}consecutive_no_change_count:\s*number[\s\S]{0,300}last_revision_round:\s*number\s*\|\s*null/.test(typesSrc), "v2.25.0 / circular_mode: SessionMeta.circular_state declares {rotation_order, consecutive_no_change_count, last_revision_round}");
5286
- assert.ok(/setCircularState\(/.test(storeSrc) && /meta\.circular_state\s*=\s*state/.test(storeSrc), "v2.25.0 / circular_mode: SessionStore.setCircularState() persists circular_state under session lock");
5298
+ assert.ok(/setCircularState\(/.test(storeSrc) && /meta\.circular_state\s*=\s*state/.test(storeSrc), "v2.25.0 / circular_mode: await SessionStore.setCircularState() persists circular_state under session lock");
5287
5299
  // (10) MCP tool schemas accept "circular".
5288
5300
  const circularEnumOccurrences = (mcpSrc.match(/z\.enum\(\["ship",\s*"review",\s*"circular"\]\)/g) ?? []).length;
5289
5301
  assert.ok(circularEnumOccurrences >= 2, `v2.25.0 / circular_mode: MCP schemas in mcp/server.ts must include z.enum(["ship","review","circular"]) for both run_until_unanimous + session_start_unanimous (found ${circularEnumOccurrences} occurrences)`);
@@ -5862,12 +5874,12 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
5862
5874
  fs.mkdirSync(path.join(repairCfg.data_dir, "sessions", corruptId), { recursive: true });
5863
5875
  fs.writeFileSync(path.join(repairCfg.data_dir, "sessions", corruptId, "meta.json"), JSON.stringify(corruptMeta, null, 2));
5864
5876
  // repair=false (default) — read-only, the contradiction is NOT touched.
5865
- const readOnly = repairStore.sessionDoctor(20, false, false);
5877
+ const readOnly = await repairStore.sessionDoctor(20, false, false);
5866
5878
  assert.equal(readOnly.repaired, undefined, "v3.6.0 / C: repair=false must NOT include a `repaired` array (stays read-only)");
5867
5879
  const afterReadOnly = repairStore.read(corruptId);
5868
5880
  assert.equal(afterReadOnly.convergence_health?.state, "blocked", "v3.6.0 / C: repair=false must leave the corrupt health state untouched");
5869
5881
  // repair=true — the contradiction is recomputed from the latest round.
5870
- const repaired = repairStore.sessionDoctor(20, false, true);
5882
+ const repaired = await repairStore.sessionDoctor(20, false, true);
5871
5883
  assert.ok(Array.isArray(repaired.repaired) && repaired.repaired.length === 1, `v3.6.0 / C: repair=true must report exactly 1 repaired session, got ${repaired.repaired?.length}`);
5872
5884
  assert.equal(repaired.repaired?.[0]?.session_id, corruptId);
5873
5885
  assert.equal(repaired.repaired?.[0]?.from_health_state, "blocked");
@@ -5876,7 +5888,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
5876
5888
  assert.equal(afterRepair.convergence_health?.state, "converged", "v3.6.0 / C: repair=true must recompute health to converged");
5877
5889
  assert.ok(/v3\.6\.0 doctor repair/.test(afterRepair.convergence_health?.detail ?? ""), "v3.6.0 / C: repaired health detail must record the repair provenance");
5878
5890
  // Idempotent — a second repair pass finds nothing to fix.
5879
- const secondPass = repairStore.sessionDoctor(20, false, true);
5891
+ const secondPass = await repairStore.sessionDoctor(20, false, true);
5880
5892
  assert.equal(secondPass.repaired?.length, 0, "v3.6.0 / C: repair must be idempotent — second pass repairs nothing");
5881
5893
  console.log("[smoke] session_doctor_repair_test: PASS");
5882
5894
  }
@@ -6214,6 +6226,90 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
6214
6226
  !verifyScript.includes("readFile(") &&
6215
6227
  verifyScript.includes("npm_package_name") &&
6216
6228
  verifyScript.includes("npm_package_version"), "v4.0.8 / F3: verify-registry-dist.mjs must not read package.json from disk; PACKAGE_NAME/PACKAGE_VERSION come from env (or npm-script-injected npm_package_name/version). Removing the file-data → fetch flow kills the recurring js/file-access-to-http CodeQL false positive at the source.");
6229
+ // v4.1.0 / F4: redactPrivateKeyBlocks must handle UNTERMINATED -----BEGIN
6230
+ // PRIVATE KEY----- blocks by redacting from BEGIN to end-of-string. Pre-
6231
+ // v4.1.0 leaked partial keys to events.ndjson when logs were truncated
6232
+ // mid-key. Empirical regression test:
6233
+ {
6234
+ const { redact } = await import("../src/security/redact.js");
6235
+ const truncatedKey = `error: -----BEGIN PRIVATE KEY-----\nMIIBVQIBADANBgkqhkiG9w0BAQEF...[TRUNCATED`;
6236
+ const redacted = redact(truncatedKey);
6237
+ assert.ok(redacted.includes("[REDACTED]"), "v4.1.0 / F4: redact() must emit [REDACTED] for unterminated -----BEGIN PRIVATE KEY----- blocks.");
6238
+ assert.ok(!redacted.includes("MIIBVQIBADANBgkqhkiG9w0BAQEF"), "v4.1.0 / F4: redact() must NOT leak the partial key body when the BEGIN marker has no matching END.");
6239
+ assert.equal(redact("just a normal log line"), "just a normal log line", "v4.1.0 / F4: redact() must be passthrough when no PRIVATE KEY markers are present.");
6240
+ console.log("[smoke] redact_unterminated_private_key_test: PASS");
6241
+ }
6242
+ // v4.1.0 / F5: writeJson + cache-manifest writeJsonAtomic must be
6243
+ // async AND no source file under src/ may contain the
6244
+ // `while (Date.now() - start < wait)` CPU-burning busy-wait pattern.
6245
+ // Pre-v4.1.0 this pattern blocked the event loop for up to 310 ms
6246
+ // under Windows AV stress. Codex R1 NEEDS_EVIDENCE catch on session
6247
+ // 059b0093 asked for repo-wide grep (the original pin only covered
6248
+ // session-store.ts and missed cache-manifest.ts:47).
6249
+ {
6250
+ const storeSrc = fs.readFileSync(path.join(process.cwd(), "src", "core", "session-store.ts"), "utf8");
6251
+ assert.ok(/async\s+function\s+writeJson\b/.test(storeSrc), "v4.1.0 / F5: writeJson must be declared `async function writeJson` in src/core/session-store.ts.");
6252
+ assert.ok(/new\s+Promise[\s\S]{0,80}?setTimeout/.test(storeSrc), "v4.1.0 / F5: writeJson must use a Promise-based async delay (`new Promise(r => setTimeout(r, wait))`) for retry backoff so the event loop remains responsive.");
6253
+ const cacheManifestSrc = fs.readFileSync(path.join(process.cwd(), "src", "core", "cache-manifest.ts"), "utf8");
6254
+ assert.ok(/async\s+function\s+writeJsonAtomic\b/.test(cacheManifestSrc), "v4.1.0 / F5: writeJsonAtomic in cache-manifest.ts must be `async function writeJsonAtomic`.");
6255
+ // Repo-wide invariant: scan every .ts under src/ for executable
6256
+ // busy-wait patterns. Strip block comments so the pin only checks
6257
+ // executable code (comments may reference the removed pattern).
6258
+ function walkTs(dir, out) {
6259
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
6260
+ const full = path.join(dir, entry.name);
6261
+ if (entry.isDirectory()) {
6262
+ walkTs(full, out);
6263
+ }
6264
+ else if (entry.isFile() && entry.name.endsWith(".ts")) {
6265
+ out.push(full);
6266
+ }
6267
+ }
6268
+ return out;
6269
+ }
6270
+ const tsFiles = walkTs(path.join(process.cwd(), "src"), []);
6271
+ for (const file of tsFiles) {
6272
+ const raw = fs.readFileSync(file, "utf8");
6273
+ // Strip /* ... */ and // ... so the busy-wait grep doesn't trip
6274
+ // on documentation. The lint runs on EXECUTABLE source only.
6275
+ const stripped = raw.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|\n)\s*\/\/[^\n]*/g, "$1");
6276
+ assert.ok(!/while\s*\(\s*Date\.now\(\)\s*-\s*\w+\s*<\s*\w+\s*\)\s*\{[^}]*\}/.test(stripped), `v4.1.0 / F5: ${path.relative(process.cwd(), file)} contains a synchronous busy-wait (\`while (Date.now() - start < wait) {}\`). Replace with \`await new Promise(r => setTimeout(r, wait))\`.`);
6277
+ assert.ok(!/Atomics\.wait\s*\(/.test(stripped), `v4.1.0 / F5: ${path.relative(process.cwd(), file)} uses Atomics.wait — also a synchronous block. Replace with a Promise+setTimeout.`);
6278
+ }
6279
+ console.log("[smoke] writeJson_async_no_busy_wait_test: PASS");
6280
+ }
6281
+ // v4.1.0 / F6: withSessionLock in src/core/session-store.ts must use
6282
+ // `proper-lockfile` (mkdir-atomic locking) instead of the pre-v4.1.0
6283
+ // `fs.openSync(lockPath, "wx")` followed by separate writeFileSync. The
6284
+ // pre-v4.1.0 pattern had a multi-process TOCTOU race: between open()
6285
+ // (creates empty inode) and writeFileSync (populates content) another
6286
+ // process could observe the empty lock, JSON-parse-fail, and rmSync the
6287
+ // lock — letting both processes enter the critical section. proper-
6288
+ // lockfile uses fs.mkdir which is atomic across NTFS and POSIX so the
6289
+ // lock comes into existence as a directory in one syscall with no
6290
+ // empty-window race. Additionally — Codex 059b0093 R1..R4 catches —
6291
+ // v4.1.0 must NEVER auto-remove a pre-v4.1.0 legacy regular `.lock`
6292
+ // file: under live cross-version v4.0/v4.1 operation, every
6293
+ // auto-clean variant raced a concurrent v4.0.x process's own
6294
+ // stale-removal-and-recreate path. v4.1.0 fails closed with a
6295
+ // clear remediation error; operator manually cleans up at upgrade.
6296
+ {
6297
+ const storeSrc = fs.readFileSync(path.join(process.cwd(), "src", "core", "session-store.ts"), "utf8");
6298
+ assert.ok(/from\s+["']proper-lockfile["']/.test(storeSrc), "v4.1.0 / F6: session-store.ts must import from 'proper-lockfile'.");
6299
+ assert.ok(/lockfile\.lock\(/.test(storeSrc), "v4.1.0 / F6: withSessionLock must call lockfile.lock() to acquire the session lock.");
6300
+ assert.ok(!/fs\.openSync\([^,]+,\s*["']wx["']\)/.test(storeSrc), "v4.1.0 / F6: session-store.ts must not contain the pre-v4.1.0 `fs.openSync(..., 'wx')` lock-acquire pattern that had the TOCTOU race.");
6301
+ assert.ok(/async\s+withSessionLock|withSessionLock[^(]*\([^)]*\)\s*:\s*Promise/.test(storeSrc), "v4.1.0 / F6: withSessionLock must be declared async (returning Promise<T>).");
6302
+ // Fail-closed migration policy: surface a remediation error
6303
+ // instead of removing legacy regular `.lock` files. The string
6304
+ // "detected a pre-v4.1.0 lock file" is the contract every caller
6305
+ // sees; the F6 smoke pin asserts it remains pinned.
6306
+ assert.ok(/detected a pre-v4\.1\.0 lock file/.test(storeSrc), "v4.1.0 / F6: session-store.ts must throw the fail-closed `detected a pre-v4.1.0 lock file` error when a legacy regular `.lock` file is observed (NO auto-removal under live cross-version operation).");
6307
+ // Belt-and-suspenders: no rmSync(lockfilePath, ...) anywhere in
6308
+ // withSessionLock — fail-closed implies the legacy file is
6309
+ // NEVER deleted by v4.1.0.
6310
+ assert.ok(!/fs\.rmSync\(\s*lockfilePath\b/.test(storeSrc), "v4.1.0 / F6: session-store.ts must NOT contain `fs.rmSync(lockfilePath, ...)` — v4.1.0 fails closed on legacy locks and never auto-removes them.");
6311
+ console.log("[smoke] session_lock_proper_lockfile_test: PASS");
6312
+ }
6217
6313
  for (const required of ["dist", "shasum", "integrity", "tarball"]) {
6218
6314
  assert.ok(verifyScript.includes(required), `v4.0.5 / AUDIT-6: verify-registry-dist.mjs must validate npm registry dist.${required}.`);
6219
6315
  }