@indigoai-us/hq-cloud 6.18.32 → 6.18.34

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 (95) hide show
  1. package/dist/bin/sync-runner-delete-snapshot.bench.test.js +13 -3
  2. package/dist/bin/sync-runner-delete-snapshot.bench.test.js.map +1 -1
  3. package/dist/bin/sync-runner-watch-loop.d.ts +9 -1
  4. package/dist/bin/sync-runner-watch-loop.d.ts.map +1 -1
  5. package/dist/bin/sync-runner-watch-loop.js +139 -35
  6. package/dist/bin/sync-runner-watch-loop.js.map +1 -1
  7. package/dist/bin/sync-runner.d.ts.map +1 -1
  8. package/dist/bin/sync-runner.js +8 -4
  9. package/dist/bin/sync-runner.js.map +1 -1
  10. package/dist/bin/sync-runner.test.js +226 -36
  11. package/dist/bin/sync-runner.test.js.map +1 -1
  12. package/dist/cli/reindex-async.d.ts +9 -0
  13. package/dist/cli/reindex-async.d.ts.map +1 -0
  14. package/dist/cli/reindex-async.js +76 -0
  15. package/dist/cli/reindex-async.js.map +1 -0
  16. package/dist/cli/reindex.d.ts +4 -0
  17. package/dist/cli/reindex.d.ts.map +1 -1
  18. package/dist/cli/reindex.js +3 -3
  19. package/dist/cli/reindex.js.map +1 -1
  20. package/dist/cli/rescue-core.d.ts +2 -0
  21. package/dist/cli/rescue-core.d.ts.map +1 -1
  22. package/dist/cli/rescue-core.js +19 -11
  23. package/dist/cli/rescue-core.js.map +1 -1
  24. package/dist/cli/rescue-rsync-preflight.test.js +1 -1
  25. package/dist/cli/rescue-rsync-preflight.test.js.map +1 -1
  26. package/dist/cli/rescue-source-switch-floor.test.js +39 -2
  27. package/dist/cli/rescue-source-switch-floor.test.js.map +1 -1
  28. package/dist/cognito-auth.d.ts +5 -1
  29. package/dist/cognito-auth.d.ts.map +1 -1
  30. package/dist/cognito-auth.js +19 -8
  31. package/dist/cognito-auth.js.map +1 -1
  32. package/dist/http-keepalive.d.ts +7 -0
  33. package/dist/http-keepalive.d.ts.map +1 -0
  34. package/dist/http-keepalive.js +66 -0
  35. package/dist/http-keepalive.js.map +1 -0
  36. package/dist/journal.d.ts +1 -0
  37. package/dist/journal.d.ts.map +1 -1
  38. package/dist/journal.js +1 -1
  39. package/dist/journal.js.map +1 -1
  40. package/dist/machine-auth.test.js +87 -0
  41. package/dist/machine-auth.test.js.map +1 -1
  42. package/dist/object-io.d.ts +2 -1
  43. package/dist/object-io.d.ts.map +1 -1
  44. package/dist/object-io.js +37 -32
  45. package/dist/object-io.js.map +1 -1
  46. package/dist/object-io.test.js +8 -1
  47. package/dist/object-io.test.js.map +1 -1
  48. package/dist/s3.d.ts.map +1 -1
  49. package/dist/s3.js +2 -1
  50. package/dist/s3.js.map +1 -1
  51. package/dist/session-log-capture.d.ts +4 -0
  52. package/dist/session-log-capture.d.ts.map +1 -1
  53. package/dist/session-log-capture.js +56 -19
  54. package/dist/session-log-capture.js.map +1 -1
  55. package/dist/session-log-capture.test.js +79 -0
  56. package/dist/session-log-capture.test.js.map +1 -1
  57. package/dist/sync/multipart-uploader.d.ts +2 -0
  58. package/dist/sync/multipart-uploader.d.ts.map +1 -1
  59. package/dist/sync/multipart-uploader.js +31 -1
  60. package/dist/sync/multipart-uploader.js.map +1 -1
  61. package/dist/sync/multipart-uploader.test.js +17 -1
  62. package/dist/sync/multipart-uploader.test.js.map +1 -1
  63. package/dist/sync/realtime-network-integration.test.d.ts +2 -0
  64. package/dist/sync/realtime-network-integration.test.d.ts.map +1 -0
  65. package/dist/sync/realtime-network-integration.test.js +83 -0
  66. package/dist/sync/realtime-network-integration.test.js.map +1 -0
  67. package/dist/sync/realtime-telemetry.d.ts +20 -3
  68. package/dist/sync/realtime-telemetry.d.ts.map +1 -1
  69. package/dist/sync/realtime-telemetry.js +85 -7
  70. package/dist/sync/realtime-telemetry.js.map +1 -1
  71. package/dist/sync/realtime-telemetry.test.js +68 -6
  72. package/dist/sync/realtime-telemetry.test.js.map +1 -1
  73. package/dist/sync/sync-work.d.ts +1 -0
  74. package/dist/sync/sync-work.d.ts.map +1 -1
  75. package/dist/sync/sync-work.js.map +1 -1
  76. package/dist/vault-client.d.ts.map +1 -1
  77. package/dist/vault-client.js +8 -2
  78. package/dist/vault-client.js.map +1 -1
  79. package/dist/watcher-replan-sweep.test.js +21 -3
  80. package/dist/watcher-replan-sweep.test.js.map +1 -1
  81. package/dist/watcher.d.ts +2 -0
  82. package/dist/watcher.d.ts.map +1 -1
  83. package/dist/watcher.js +166 -50
  84. package/dist/watcher.js.map +1 -1
  85. package/dist/watcher.test.js +59 -5
  86. package/dist/watcher.test.js.map +1 -1
  87. package/dist/watcher.warmup-cancel.test.d.ts +2 -0
  88. package/dist/watcher.warmup-cancel.test.d.ts.map +1 -0
  89. package/dist/watcher.warmup-cancel.test.js +59 -0
  90. package/dist/watcher.warmup-cancel.test.js.map +1 -0
  91. package/dist/watcher.warmup-responsive.test.d.ts +2 -0
  92. package/dist/watcher.warmup-responsive.test.d.ts.map +1 -0
  93. package/dist/watcher.warmup-responsive.test.js +62 -0
  94. package/dist/watcher.warmup-responsive.test.js.map +1 -0
  95. package/package.json +1 -1
@@ -20,10 +20,10 @@ import { AUTH_WAIT_INITIAL_RECHECK_MS, AUTH_WAIT_RESUME_MESSAGE, AUTH_WAIT_START
20
20
  import { PresignObjectIO, S3SdkObjectIO } from "../object-io.js";
21
21
  import { BandwidthGovernor, setBandwidthGovernorForTesting, } from "../bandwidth.js";
22
22
  import { POST_SYNC_FLAG_KEYS, } from "./sync-runner-post-sync-flags.js";
23
- import { FakeClock, TreeWatcher, } from "../watcher.js";
24
- import { adaptivePollMs, advanceMonotonicDeadline, DEFAULT_EVENT_BATCH_LIMIT, DEFAULT_FAST_LANE_MAX_BYTES, DEFAULT_FAST_LANE_MAX_FILE_BYTES, DEFAULT_FAST_LANE_MAX_FILES, DEFAULT_FULL_RECONCILE_MS, EVENT_BATCH_LIMIT_ENV, FAST_LANE_MAX_BYTES_ENV, FAST_LANE_MAX_FILES_ENV, FULL_RECONCILE_MS_ENV, isFastLocalBatch, isFastReceiverBatch, partitionLocalBatch, journalEntriesForWatcherDelete, resolveEventBatchLimit, resolveFullReconcileMs, resolveWatchPollMs, CHORE_STARVATION_CAP_MS, CooperativePassScheduler, REALTIME_ADMISSION_DEADLINE_MS, REALTIME_DRAIN_BURST_MS, REALTIME_WORKER_OCCUPANCY_CAP, RealtimeAdmissionTimeoutError, setRealtimeAdmissionDeadlineMsForTesting, setRealtimeDrainBurstMsForTesting, normalizeCoordinatorPaths, shouldQuarantineWatcherDeleteIntents, shouldUseAreaStorage, WATCHER_REDISCOVERY_RETRY_MS, WATCHER_REDISCOVERY_RETRY_MAX_MS, EVENT_SYNC_STARTUP_SHUTDOWN_WAIT_MS, } from "./sync-runner-watch-loop.js";
23
+ import { FakeClock, TreeWatcher, createWatchPathFilter, } from "../watcher.js";
24
+ import { adaptivePollMs, advanceMonotonicDeadline, DEFAULT_EVENT_BATCH_LIMIT, DEFAULT_FAST_LANE_MAX_BYTES, DEFAULT_FAST_LANE_MAX_FILE_BYTES, DEFAULT_FAST_LANE_MAX_FILES, DEFAULT_FULL_RECONCILE_MS, EVENT_BATCH_LIMIT_ENV, FAST_LANE_MAX_BYTES_ENV, FAST_LANE_MAX_FILES_ENV, FULL_RECONCILE_MS_ENV, isFastLocalBatch, isLargeLocalUpload, localUploadSizeClass, isChoreLocalPush, isSessionLogsPushOnlyPath, isFastReceiverBatch, partitionLocalBatch, journalEntriesForWatcherDelete, resolveEventBatchLimit, resolveFullReconcileMs, resolveWatchPollMs, CHORE_STARVATION_CAP_MS, CooperativePassScheduler, REALTIME_ADMISSION_DEADLINE_MS, REALTIME_DRAIN_BURST_MS, REALTIME_WORKER_OCCUPANCY_CAP, REALTIME_LARGE_UPLOAD_THRESHOLD_BYTES, RealtimeAdmissionTimeoutError, setRealtimeAdmissionDeadlineMsForTesting, setRealtimeDrainBurstMsForTesting, normalizeCoordinatorPaths, shouldQuarantineWatcherDeleteIntents, shouldUseAreaStorage, WATCHER_REDISCOVERY_RETRY_MS, WATCHER_REDISCOVERY_RETRY_MAX_MS, EVENT_SYNC_STARTUP_SHUTDOWN_WAIT_MS, } from "./sync-runner-watch-loop.js";
25
25
  import { FullPassCapController, resetFullPassCapLogForTest, } from "../sync/full-pass-caps.js";
26
- import { AreaLedgerMigration, JournalStore, openJournalStateStoreForMigration, PERSONAL_VAULT_JOURNAL_SLUG, readJournal, readJournalForPass, readJournalScoped, readJournalScopedAsync, writeJournal, } from "../journal.js";
26
+ import { AreaLedgerMigration, JournalStore, getJournalPath, openJournalStateStoreForMigration, PERSONAL_VAULT_JOURNAL_SLUG, readJournal, readJournalForPass, readJournalScoped, readJournalScopedAsync, writeJournal, } from "../journal.js";
27
27
  import { AreaLedger } from "../sync/area-ledger.js";
28
28
  import { AreaResolver } from "../sync/area-resolver.js";
29
29
  import { AREA_LEGACY_MIRROR_LINGER_MS } from "../sync/area-ledger-migration.js";
@@ -3145,11 +3145,12 @@ describe("all-complete aggregate", () => {
3145
3145
  true,
3146
3146
  ]);
3147
3147
  expect(reindex).toHaveBeenCalledTimes(1);
3148
- // Session logs are recopied at most every 10 minutes from the runner.
3148
+ // Session logs are captured only after ten minutes without source changes.
3149
3149
  expect(reindex).toHaveBeenCalledWith({
3150
3150
  repoRoot: "/tmp/fake-hq",
3151
3151
  skipLock: true,
3152
- sessionLogMinIntervalMs: 600_000,
3152
+ sessionLogQuietForMs: 600_000,
3153
+ sessionLogMaxStalenessMs: 3_600_000,
3153
3154
  });
3154
3155
  });
3155
3156
  it("gates qmd to indexable knowledge and project changes and propagates hq-relative paths", async () => {
@@ -6360,6 +6361,8 @@ describe("runRunnerWithLoop — event-push wiring", () => {
6360
6361
  writeJournal("personal", legacy);
6361
6362
  const hqRoot = fs.mkdtempSync(path.join(os.tmpdir(), "hq-warmup-rows-"));
6362
6363
  fs.mkdirSync(path.join(hqRoot, "companies", "indigo", "repos", "x"), { recursive: true });
6364
+ fs.mkdirSync(path.join(hqRoot, "companies", "bad-json"), { recursive: true });
6365
+ fs.writeFileSync(getJournalPath("bad-json"), "{corrupt legacy journal");
6363
6366
  fs.writeFileSync(path.join(hqRoot, "companies", "indigo", "repos", "x", "a.md"), "keep local repository checkout");
6364
6367
  const watcher = makeBatchWatcherStub();
6365
6368
  let markReady = () => { };
@@ -6735,7 +6738,7 @@ describe("runRunnerWithLoop — event-push wiring", () => {
6735
6738
  });
6736
6739
  await expect(superseded).resolves.toBeUndefined();
6737
6740
  expect(runPass.mock.calls.filter(([argv]) => argv.includes("superseded.md"))).toHaveLength(0);
6738
- expect(stderr.mock.calls.map(([chunk]) => String(chunk)).join("")).toContain('"event":"sync.realtime.timing","schemaVersion":3,"source":"receiver","direction":"pull","outcome":"refused"');
6741
+ expect(stderr.mock.calls.map(([chunk]) => String(chunk)).join("")).toContain('"event":"sync.realtime.timing","schemaVersion":5,"source":"receiver","direction":"pull","sizeClass":"unknown","outcome":"refused"');
6739
6742
  releaseHeld();
6740
6743
  await heldReceiverWork;
6741
6744
  await replacement;
@@ -9073,6 +9076,7 @@ describe("runRunnerWithLoop — event-push wiring", () => {
9073
9076
  fs.rmSync(hqRoot, { recursive: true, force: true });
9074
9077
  });
9075
9078
  it("U07: drains the first watcher event after activation without guarded overlap", async () => {
9079
+ const hqRoot = fs.mkdtempSync(path.join(os.tmpdir(), "hq-watch-first-event-"));
9076
9080
  const watcher = makeWatcherStub();
9077
9081
  let triggerShutdown = () => { };
9078
9082
  let releaseFirstPass = () => { };
@@ -9097,7 +9101,7 @@ describe("runRunnerWithLoop — event-push wiring", () => {
9097
9101
  "--poll-remote-ms",
9098
9102
  "600000",
9099
9103
  "--hq-root",
9100
- "/tmp/hq",
9104
+ hqRoot,
9101
9105
  ], {
9102
9106
  runPass,
9103
9107
  createWatcher: () => watcher,
@@ -9114,13 +9118,17 @@ describe("runRunnerWithLoop — event-push wiring", () => {
9114
9118
  expect(watcher.started).toBe(true);
9115
9119
  // Activation happens synchronously after the maintenance-bearing pass,
9116
9120
  // before the next wait is registered. Its first event must still drain.
9121
+ const eventPath = path.join(hqRoot, "companies/indigo/pre-registration.md");
9122
+ fs.mkdirSync(path.dirname(eventPath), { recursive: true });
9123
+ fs.writeFileSync(eventPath, "small watcher edit");
9117
9124
  watcher.emit("companies/indigo/pre-registration.md");
9118
- await flushLoopMicrotasks();
9125
+ await flushLoopMicrotasks(32);
9119
9126
  expect(spans).toHaveLength(3);
9120
9127
  expect(spans[1].enteredAt >= spans[0].exitedAt).toBe(true);
9121
9128
  expect(spans[2].enteredAt >= spans[1].exitedAt).toBe(true);
9122
9129
  triggerShutdown();
9123
9130
  await loop;
9131
+ fs.rmSync(hqRoot, { recursive: true, force: true });
9124
9132
  });
9125
9133
  it("F05: guard-held watcher changes stay accumulated, then drain immediately after the guarded pass", async () => {
9126
9134
  const watcher = makeWatcherStub();
@@ -9343,7 +9351,7 @@ describe("runRunnerWithLoop — event-push wiring", () => {
9343
9351
  return () => { };
9344
9352
  },
9345
9353
  });
9346
- await flushLoopMicrotasks();
9354
+ await waitForPollSleep(sleep);
9347
9355
  sleep.tick();
9348
9356
  await flushLoopMicrotasks();
9349
9357
  // The transient pass did NOT end the loop — it ran again, and neither
@@ -9385,6 +9393,7 @@ describe("runRunnerWithLoop — event-push wiring", () => {
9385
9393
  },
9386
9394
  });
9387
9395
  await flushLoopMicrotasks();
9396
+ await waitForPollSleep(sleep);
9388
9397
  sleep.tick();
9389
9398
  await flushLoopMicrotasks();
9390
9399
  expect(routedArgv).toEqual([
@@ -9924,6 +9933,152 @@ describe("runRunnerWithLoop — U58 realtime scheduler guard", () => {
9924
9933
  });
9925
9934
  });
9926
9935
  describe("CooperativePassScheduler", () => {
9936
+ it("identifies session-log backups for the chore-only route", () => {
9937
+ const transcript = "workspace/.session-logs/codex/2026/10/01/live.jsonl";
9938
+ expect(isSessionLogsPushOnlyPath(transcript)).toBe(true);
9939
+ expect(isSessionLogsPushOnlyPath("workspace/notes.md")).toBe(false);
9940
+ expect(isChoreLocalPush([transcript], false)).toBe(true);
9941
+ expect(isChoreLocalPush(["workspace/notes.md"], false)).toBe(false);
9942
+ expect(isChoreLocalPush(["workspace/notes.md"], true)).toBe(true);
9943
+ });
9944
+ it("reserves a realtime occupant for each transfer direction", async () => {
9945
+ const scheduler = new CooperativePassScheduler(() => 0);
9946
+ const started = [];
9947
+ let releasePush1 = () => { };
9948
+ const push1Gate = new Promise((resolve) => { releasePush1 = resolve; });
9949
+ let releasePush2 = () => { };
9950
+ const push2Gate = new Promise((resolve) => { releasePush2 = resolve; });
9951
+ let releaseCheckpoint = () => { };
9952
+ const checkpointReady = new Promise((resolve) => { releaseCheckpoint = resolve; });
9953
+ const chore = scheduler.submit(async ({ checkpoint }) => {
9954
+ await checkpointReady;
9955
+ await checkpoint();
9956
+ return 0;
9957
+ }, "chore");
9958
+ await Promise.resolve();
9959
+ const push1 = scheduler.submit(async () => {
9960
+ started.push("push-1");
9961
+ await push1Gate;
9962
+ return 0;
9963
+ }, "realtime", "slow", false, undefined, false, false, "push");
9964
+ releaseCheckpoint();
9965
+ await new Promise((resolve) => setTimeout(resolve, 2));
9966
+ const push2 = scheduler.submit(async () => {
9967
+ started.push("push-2");
9968
+ await push2Gate;
9969
+ return 0;
9970
+ }, "realtime", "slow", false, undefined, false, true, "push");
9971
+ await new Promise((resolve) => setTimeout(resolve, 2));
9972
+ const pull = scheduler.submit(async () => {
9973
+ started.push("pull");
9974
+ return 0;
9975
+ }, "realtime", "slow", false, undefined, false, true, "pull");
9976
+ try {
9977
+ await new Promise((resolve) => setTimeout(resolve, 2));
9978
+ expect(started).toContain("pull");
9979
+ }
9980
+ finally {
9981
+ releasePush1();
9982
+ releasePush2();
9983
+ await Promise.all([chore, push1, push2, pull]);
9984
+ }
9985
+ });
9986
+ it("classifies uploads at the multipart part-size boundary", () => {
9987
+ const directory = fs.mkdtempSync(path.join(os.tmpdir(), "hq-large-upload-class-"));
9988
+ try {
9989
+ const small = path.join(directory, "small.txt");
9990
+ const large = path.join(directory, "large.bin");
9991
+ const missing = path.join(directory, "not-created-yet.txt");
9992
+ fs.writeFileSync(small, "small edit");
9993
+ const fd = fs.openSync(large, "w");
9994
+ fs.ftruncateSync(fd, REALTIME_LARGE_UPLOAD_THRESHOLD_BYTES);
9995
+ fs.closeSync(fd);
9996
+ expect(REALTIME_LARGE_UPLOAD_THRESHOLD_BYTES).toBe(8 * 1024 * 1024);
9997
+ expect(isLargeLocalUpload([small])).toBe(false);
9998
+ expect(isLargeLocalUpload([large])).toBe(true);
9999
+ expect(localUploadSizeClass([small])).toBe("small");
10000
+ expect(localUploadSizeClass([large])).toBe("large");
10001
+ expect(localUploadSizeClass([missing])).toBe("unknown");
10002
+ expect(isLargeLocalUpload([missing])).toBe(true);
10003
+ }
10004
+ finally {
10005
+ fs.rmSync(directory, { recursive: true, force: true });
10006
+ }
10007
+ });
10008
+ it("keeps small pulls under 5s during a 60s fake S3 PUT despite excluded path floods", async () => {
10009
+ vi.useFakeTimers();
10010
+ const watchRoot = fs.mkdtempSync(path.join(os.tmpdir(), "hq-realtime-excluded-flood-"));
10011
+ try {
10012
+ const scheduler = new CooperativePassScheduler(() => Date.now());
10013
+ const shouldWatch = createWatchPathFilter(watchRoot, false);
10014
+ const fakeS3 = new Map();
10015
+ const localFiles = new Map();
10016
+ const mirrorJournal = new Map();
10017
+ const remoteChanges = Array.from({ length: 20 }, (_, index) => [`remote-${index}`, `value-${index}`]);
10018
+ for (const [key, value] of remoteChanges)
10019
+ fakeS3.set(key, value);
10020
+ let markPutStarted = () => { };
10021
+ const putStarted = new Promise((resolve) => { markPutStarted = resolve; });
10022
+ let largePutComplete = false;
10023
+ const upload = scheduler.submit(async ({ checkpoint }) => {
10024
+ const transfer = await checkpoint.runChoreNetworkTransfer(async () => {
10025
+ markPutStarted();
10026
+ await new Promise((resolve) => { setTimeout(resolve, 60_000); });
10027
+ fakeS3.set("large-upload", "large payload");
10028
+ return "large-upload";
10029
+ });
10030
+ largePutComplete = true;
10031
+ mirrorJournal.set(transfer.value, fakeS3.get(transfer.value) ?? "");
10032
+ transfer.complete();
10033
+ return 0;
10034
+ }, "chore");
10035
+ await putStarted;
10036
+ for (let index = 0; index < 1_000; index += 1) {
10037
+ const excluded = path.join(watchRoot, "workspace", "worktrees", `lane-${index}`, "output.ts");
10038
+ if (shouldWatch(excluded)) {
10039
+ void scheduler.submit(async () => 0, "realtime", "slow", false, undefined, false, true, "push", "small");
10040
+ }
10041
+ }
10042
+ expect(scheduler.activeRealtimeWorkerCount).toBe(0);
10043
+ expect(scheduler.pendingLaneCount("slow")).toBe(0);
10044
+ let allPullsSettled = false;
10045
+ const pullWaits = remoteChanges.map(([key]) => {
10046
+ const arrivedAt = Date.now();
10047
+ return scheduler.submit(async () => {
10048
+ const value = fakeS3.get(key);
10049
+ if (value === undefined)
10050
+ throw new Error(`fake S3 object missing: ${key}`);
10051
+ localFiles.set(key, value);
10052
+ mirrorJournal.set(key, value);
10053
+ return Date.now() - arrivedAt;
10054
+ }, "realtime", "slow", false, undefined, false, true, "pull", "small");
10055
+ });
10056
+ const allPullWaits = Promise.all(pullWaits).then((waits) => {
10057
+ allPullsSettled = true;
10058
+ return waits;
10059
+ });
10060
+ await vi.advanceTimersByTimeAsync(5_000);
10061
+ const pullsMetWorkerBound = allPullsSettled;
10062
+ if (!pullsMetWorkerBound)
10063
+ await vi.advanceTimersByTimeAsync(55_000);
10064
+ const waits = await allPullWaits;
10065
+ expect(pullsMetWorkerBound).toBe(true);
10066
+ expect(waits.every((waitMs) => waitMs < 5_000)).toBe(true);
10067
+ expect(localFiles.size).toBe(remoteChanges.length);
10068
+ expect(largePutComplete).toBe(false);
10069
+ expect(mirrorJournal.has("large-upload")).toBe(false);
10070
+ if (!largePutComplete)
10071
+ await vi.advanceTimersByTimeAsync(55_000);
10072
+ await upload;
10073
+ expect(largePutComplete).toBe(true);
10074
+ expect(fakeS3.get("large-upload")).toBe("large payload");
10075
+ expect(mirrorJournal.get("large-upload")).toBe("large payload");
10076
+ }
10077
+ finally {
10078
+ vi.useRealTimers();
10079
+ fs.rmSync(watchRoot, { recursive: true, force: true });
10080
+ }
10081
+ });
9927
10082
  it("admits realtime during payload I/O and fences the next chore transfer until the result is committed", async () => {
9928
10083
  const scheduler = new CooperativePassScheduler(() => 0);
9929
10084
  const order = [];
@@ -10581,6 +10736,7 @@ describe("CooperativePassScheduler", () => {
10581
10736
  });
10582
10737
  describe("first-tick realtime yield", () => {
10583
10738
  it("commits a watcher push at the first chore checkpoint without a wall-clock wait", async () => {
10739
+ const hqRoot = fs.mkdtempSync(path.join(os.tmpdir(), "hq-first-tick-realtime-"));
10584
10740
  const previousCoordinator = process.env.HQ_SYNC_PARALLEL_COORDINATOR;
10585
10741
  const previousInterval = process.env.HQ_SYNC_FULL_RECONCILE_MS;
10586
10742
  process.env.HQ_SYNC_PARALLEL_COORDINATOR = "1";
@@ -10635,7 +10791,7 @@ describe("first-tick realtime yield", () => {
10635
10791
  "--direction",
10636
10792
  "both",
10637
10793
  "--hq-root",
10638
- "/tmp/hq",
10794
+ hqRoot,
10639
10795
  "--poll-remote-ms",
10640
10796
  "60000",
10641
10797
  ], {
@@ -10651,9 +10807,12 @@ describe("first-tick realtime yield", () => {
10651
10807
  });
10652
10808
  try {
10653
10809
  await flushLoopMicrotasks(40);
10810
+ const notePath = path.join(hqRoot, "companies/indigo/note.md");
10811
+ fs.mkdirSync(path.dirname(notePath), { recursive: true });
10812
+ fs.writeFileSync(notePath, "small watcher edit");
10654
10813
  watcher.emit("companies/indigo/note.md", {
10655
10814
  paths: new Map([
10656
- ["/tmp/hq/companies/indigo/note.md", "companies/indigo/note.md"],
10815
+ [notePath, "companies/indigo/note.md"],
10657
10816
  ]),
10658
10817
  });
10659
10818
  await new Promise((resolve) => setImmediate(resolve));
@@ -10687,6 +10846,7 @@ describe("first-tick realtime yield", () => {
10687
10846
  delete process.env.HQ_SYNC_FULL_RECONCILE_MS;
10688
10847
  else
10689
10848
  process.env.HQ_SYNC_FULL_RECONCILE_MS = previousInterval;
10849
+ fs.rmSync(hqRoot, { recursive: true, force: true });
10690
10850
  }
10691
10851
  });
10692
10852
  });
@@ -11844,6 +12004,10 @@ describe("runRunnerWithLoop — accumulate-and-drain scoped push", () => {
11844
12004
  await loop;
11845
12005
  });
11846
12006
  it("push-only scoped ticks run the scoped push and no pull leg", async () => {
12007
+ const hqRoot = fs.mkdtempSync(path.join(os.tmpdir(), "hq-push-only-tick-"));
12008
+ const file = path.join(hqRoot, "companies/indigo/knowledge/a.md");
12009
+ fs.mkdirSync(path.dirname(file), { recursive: true });
12010
+ fs.writeFileSync(file, "small watcher edit");
11847
12011
  const runPass = vi.fn().mockResolvedValue(completedPassOutcome());
11848
12012
  const { loop, watcher, tick, shutdown } = startDrainLoopWithArgv(runPass, [
11849
12013
  "--companies",
@@ -11852,15 +12016,15 @@ describe("runRunnerWithLoop — accumulate-and-drain scoped push", () => {
11852
12016
  "--direction",
11853
12017
  "push",
11854
12018
  "--hq-root",
11855
- "/tmp/hq",
12019
+ hqRoot,
11856
12020
  "--poll-remote-ms",
11857
12021
  "60000",
11858
12022
  ]);
11859
- await flushLoopMicrotasks();
12023
+ await flushLoopMicrotasks(30);
11860
12024
  runPass.mockClear();
11861
12025
  watcher.emit("companies/indigo/knowledge/a.md");
11862
12026
  tick();
11863
- await flushLoopMicrotasks();
12027
+ await flushLoopMicrotasks(30);
11864
12028
  expect(runPass.mock.calls.map((call) => call[0])).toEqual([
11865
12029
  [
11866
12030
  "--company",
@@ -11870,12 +12034,13 @@ describe("runRunnerWithLoop — accumulate-and-drain scoped push", () => {
11870
12034
  "--scope-path",
11871
12035
  "knowledge/a.md",
11872
12036
  "--hq-root",
11873
- "/tmp/hq",
12037
+ hqRoot,
11874
12038
  ],
11875
12039
  ]);
11876
12040
  shutdown();
11877
12041
  tick();
11878
12042
  await loop;
12043
+ fs.rmSync(hqRoot, { recursive: true, force: true });
11879
12044
  });
11880
12045
  it("overflow and an elapsed hour run a full unscoped reconcile", async () => {
11881
12046
  const clockState = { now: 0 };
@@ -12141,11 +12306,14 @@ describe("runRunnerWithLoop — accumulate-and-drain scoped push", () => {
12141
12306
  }
12142
12307
  });
12143
12308
  it("a 5,000-path batch under the shipped default stays on the targeted path", async () => {
12309
+ const hqRoot = fs.mkdtempSync(path.join(os.tmpdir(), "hq-5000-path-batch-"));
12310
+ const argv = [...watchArgv];
12311
+ argv[argv.indexOf("/tmp/hq")] = hqRoot;
12144
12312
  const watcher = makeBatchWatcherStub();
12145
12313
  const sleep = makeSteppableSleep();
12146
12314
  let triggerShutdown = () => { };
12147
12315
  const runPass = vi.fn().mockResolvedValue(completedPassOutcome());
12148
- const loop = runRunnerWithLoop(watchArgv, {
12316
+ const loop = runRunnerWithLoop(argv, {
12149
12317
  runPass,
12150
12318
  clock: new FakeClock(),
12151
12319
  createWatcher: () => watcher,
@@ -12158,13 +12326,16 @@ describe("runRunnerWithLoop — accumulate-and-drain scoped push", () => {
12158
12326
  await flushLoopMicrotasks();
12159
12327
  runPass.mockClear();
12160
12328
  watcher.emit("companies/indigo/a.md", {
12161
- paths: new Map(Array.from({ length: 5_000 }, (_, index) => [
12162
- `/tmp/hq/companies/indigo/${index}.md`,
12163
- `companies/indigo/${index}.md`,
12164
- ])),
12329
+ paths: new Map(Array.from({ length: 5_000 }, (_, index) => {
12330
+ const relativePath = `companies/indigo/${index}.md`;
12331
+ const absolutePath = path.join(hqRoot, relativePath);
12332
+ fs.mkdirSync(path.dirname(absolutePath), { recursive: true });
12333
+ fs.writeFileSync(absolutePath, "x");
12334
+ return [absolutePath, relativePath];
12335
+ })),
12165
12336
  });
12166
12337
  sleep.tick();
12167
- await flushLoopMicrotasks();
12338
+ await waitForCondition("5,000-path batch to split into two scoped passes", () => runPass.mock.calls.length === 2);
12168
12339
  const calls = runPass.mock.calls.map((call) => call[0]);
12169
12340
  expect(calls).not.toContainEqual(fullArgv);
12170
12341
  expect(calls).toHaveLength(2);
@@ -12178,6 +12349,7 @@ describe("runRunnerWithLoop — accumulate-and-drain scoped push", () => {
12178
12349
  expect(calls[1]?.filter((arg) => arg === "--scope-path")).toHaveLength(5_000);
12179
12350
  triggerShutdown();
12180
12351
  await loop;
12352
+ fs.rmSync(hqRoot, { recursive: true, force: true });
12181
12353
  });
12182
12354
  it("a batch above the shipped 10,000-path default stays on chunked targeted passes before the interval", async () => {
12183
12355
  const saved = process.env.HQ_SYNC_FULL_RECONCILE_MS;
@@ -13835,12 +14007,13 @@ describe("runRunnerWithLoop — Phase 3 event-sync wiring (US-017/018/019)", ()
13835
14007
  ]]),
13836
14008
  });
13837
14009
  tick();
13838
- await microtasks();
14010
+ await microtasks(30);
13839
14011
  expect(publishBatch).not.toHaveBeenCalled();
13840
14012
  shutdown();
13841
14013
  await loop;
13842
14014
  });
13843
14015
  it("parallel coordinator publishes only durable watcher paths after a subset is superseded", async () => {
14016
+ const hqRoot = fs.mkdtempSync(path.join(os.tmpdir(), "hq-subset-superseded-"));
13844
14017
  const previousCoordinator = process.env.HQ_SYNC_PARALLEL_COORDINATOR;
13845
14018
  process.env.HQ_SYNC_PARALLEL_COORDINATOR = "1";
13846
14019
  const publishBatch = vi.fn();
@@ -13859,36 +14032,42 @@ describe("runRunnerWithLoop — Phase 3 event-sync wiring (US-017/018/019)", ()
13859
14032
  });
13860
14033
  const { loop, watcher, clock, waitForPoll, shutdown } = runLoop({
13861
14034
  claims: EMAIL_CLAIMS,
14035
+ hqRoot,
13862
14036
  startEventSync,
13863
14037
  runPass,
13864
14038
  });
13865
14039
  const heldPath = "companies/indigo/held.md";
13866
14040
  const supersededPath = "companies/indigo/superseded.md";
13867
14041
  const durablePath = "companies/indigo/durable.md";
14042
+ for (const relativePath of [heldPath, supersededPath, durablePath]) {
14043
+ const absolutePath = path.join(hqRoot, relativePath);
14044
+ fs.mkdirSync(path.dirname(absolutePath), { recursive: true });
14045
+ fs.writeFileSync(absolutePath, "small watcher edit");
14046
+ }
13868
14047
  try {
13869
14048
  await waitForPoll();
13870
14049
  await waitForObservable("watcher subscription to become active", () => watcher.started && watcher.listenerCount() > 0);
13871
14050
  runPass.mockClear();
13872
14051
  watcher.emit(heldPath, {
13873
- paths: new Map([[`/tmp/hq/${heldPath}`, heldPath]]),
13874
- changes: new Map([[`/tmp/hq/${heldPath}`, { kind: "change" }]]),
14052
+ paths: new Map([[path.join(hqRoot, heldPath), heldPath]]),
14053
+ changes: new Map([[path.join(hqRoot, heldPath), { kind: "change" }]]),
13875
14054
  });
13876
14055
  clock.advance(0);
13877
14056
  await waitForObservable("held watcher push to start", () => runPass.mock.calls.some(([argv]) => argv.includes("held.md")));
13878
14057
  watcher.emit(supersededPath, {
13879
14058
  paths: new Map([
13880
- [`/tmp/hq/${supersededPath}`, supersededPath],
13881
- [`/tmp/hq/${durablePath}`, durablePath],
14059
+ [path.join(hqRoot, supersededPath), supersededPath],
14060
+ [path.join(hqRoot, durablePath), durablePath],
13882
14061
  ]),
13883
14062
  changes: new Map([
13884
- [`/tmp/hq/${supersededPath}`, { kind: "change" }],
13885
- [`/tmp/hq/${durablePath}`, { kind: "change" }],
14063
+ [path.join(hqRoot, supersededPath), { kind: "change" }],
14064
+ [path.join(hqRoot, durablePath), { kind: "change" }],
13886
14065
  ]),
13887
14066
  });
13888
14067
  clock.advance(0);
13889
14068
  watcher.emit(supersededPath, {
13890
- paths: new Map([[`/tmp/hq/${supersededPath}`, supersededPath]]),
13891
- changes: new Map([[`/tmp/hq/${supersededPath}`, { kind: "change" }]]),
14069
+ paths: new Map([[path.join(hqRoot, supersededPath), supersededPath]]),
14070
+ changes: new Map([[path.join(hqRoot, supersededPath), { kind: "change" }]]),
13892
14071
  });
13893
14072
  clock.advance(0);
13894
14073
  releaseHeld();
@@ -13907,9 +14086,11 @@ describe("runRunnerWithLoop — Phase 3 event-sync wiring (US-017/018/019)", ()
13907
14086
  delete process.env.HQ_SYNC_PARALLEL_COORDINATOR;
13908
14087
  else
13909
14088
  process.env.HQ_SYNC_PARALLEL_COORDINATOR = previousCoordinator;
14089
+ fs.rmSync(hqRoot, { recursive: true, force: true });
13910
14090
  }
13911
14091
  });
13912
14092
  it("parallel coordinator publishes nothing for a wholly superseded watcher batch", async () => {
14093
+ const hqRoot = fs.mkdtempSync(path.join(os.tmpdir(), "hq-wholly-superseded-"));
13913
14094
  const previousCoordinator = process.env.HQ_SYNC_PARALLEL_COORDINATOR;
13914
14095
  process.env.HQ_SYNC_PARALLEL_COORDINATOR = "1";
13915
14096
  const publishBatch = vi.fn();
@@ -13928,26 +14109,32 @@ describe("runRunnerWithLoop — Phase 3 event-sync wiring (US-017/018/019)", ()
13928
14109
  });
13929
14110
  const { loop, watcher, clock, shutdown } = runLoop({
13930
14111
  claims: EMAIL_CLAIMS,
14112
+ hqRoot,
13931
14113
  startEventSync,
13932
14114
  runPass,
13933
14115
  });
13934
14116
  const heldPath = "companies/indigo/held.md";
13935
14117
  const supersededPath = "companies/indigo/wholly-superseded.md";
14118
+ const heldAbsolutePath = path.join(hqRoot, heldPath);
14119
+ fs.mkdirSync(path.dirname(heldAbsolutePath), { recursive: true });
14120
+ fs.writeFileSync(heldAbsolutePath, "small watcher edit");
13936
14121
  try {
13937
14122
  await microtasks(30);
13938
14123
  runPass.mockClear();
13939
14124
  watcher.emit(heldPath, {
13940
- paths: new Map([[`/tmp/hq/${heldPath}`, heldPath]]),
14125
+ paths: new Map([[path.join(hqRoot, heldPath), heldPath]]),
13941
14126
  });
13942
14127
  clock.advance(0);
13943
14128
  await microtasks(30);
13944
14129
  watcher.emit(supersededPath, {
13945
- paths: new Map([[`/tmp/hq/${supersededPath}`, supersededPath]]),
14130
+ paths: new Map([[path.join(hqRoot, supersededPath), supersededPath]]),
14131
+ changes: new Map([[path.join(hqRoot, supersededPath), { kind: "unlink" }]]),
13946
14132
  });
13947
14133
  clock.advance(0);
13948
14134
  await microtasks(30);
13949
14135
  watcher.emit(supersededPath, {
13950
- paths: new Map([[`/tmp/hq/${supersededPath}`, supersededPath]]),
14136
+ paths: new Map([[path.join(hqRoot, supersededPath), supersededPath]]),
14137
+ changes: new Map([[path.join(hqRoot, supersededPath), { kind: "unlink" }]]),
13951
14138
  });
13952
14139
  clock.advance(0);
13953
14140
  await microtasks(30);
@@ -13967,7 +14154,9 @@ describe("runRunnerWithLoop — Phase 3 event-sync wiring (US-017/018/019)", ()
13967
14154
  // overlapping batch is reconciled. It has no accepted delete result,
13968
14155
  // so it must not publish an event.
13969
14156
  expect(supersededPushes).toHaveLength(1);
13970
- expect(publishBatch).not.toHaveBeenCalled();
14157
+ const publishedPaths = publishBatch.mock.calls.flatMap(([batch]) => [...batch.paths.values()]);
14158
+ expect(publishedPaths).toContain(heldPath);
14159
+ expect(publishedPaths).not.toContain(supersededPath);
13971
14160
  let loopExited = false;
13972
14161
  void loop.then(() => { loopExited = true; });
13973
14162
  await microtasks();
@@ -13982,6 +14171,7 @@ describe("runRunnerWithLoop — Phase 3 event-sync wiring (US-017/018/019)", ()
13982
14171
  delete process.env.HQ_SYNC_PARALLEL_COORDINATOR;
13983
14172
  else
13984
14173
  process.env.HQ_SYNC_PARALLEL_COORDINATOR = previousCoordinator;
14174
+ fs.rmSync(hqRoot, { recursive: true, force: true });
13985
14175
  }
13986
14176
  });
13987
14177
  it("excludes refused descendants while publishing accepted unlinkDir deletes", async () => {
@@ -14029,7 +14219,7 @@ describe("runRunnerWithLoop — Phase 3 event-sync wiring (US-017/018/019)", ()
14029
14219
  changes: new Map([[absolutePath, { kind: "unlinkDir" }]]),
14030
14220
  });
14031
14221
  tick();
14032
- await microtasks();
14222
+ await microtasks(30);
14033
14223
  expect(publishBatch).toHaveBeenCalledTimes(1);
14034
14224
  expect([...publishBatch.mock.calls[0][0].paths.values()]).toEqual([
14035
14225
  "companies/indigo/archive/accepted.md",
@@ -14078,7 +14268,7 @@ describe("runRunnerWithLoop — Phase 3 event-sync wiring (US-017/018/019)", ()
14078
14268
  changes: new Map([[absoluteDirectory, { kind: "unlinkDir" }]]),
14079
14269
  });
14080
14270
  tick();
14081
- await microtasks();
14271
+ await microtasks(30);
14082
14272
  expect(publishBatch).toHaveBeenCalledTimes(1);
14083
14273
  expect([...publishBatch.mock.calls[0][0].paths.values()]).toEqual([
14084
14274
  "companies/indigo/archive/first.md",