@indigoai-us/hq-cloud 6.15.65 → 6.15.66

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.
@@ -14,7 +14,7 @@ import { runRunner, runRunnerWithLoop, resolveDeletePolicy, resolveSkipPersonal,
14
14
  import { PresignObjectIO, S3SdkObjectIO } from "../object-io.js";
15
15
  import { setBandwidthGovernorForTesting } from "../bandwidth.js";
16
16
  import { FakeClock, TreeWatcher, } from "../watcher.js";
17
- import { adaptivePollMs, advanceMonotonicDeadline, DEFAULT_EVENT_BATCH_LIMIT, DEFAULT_FAST_LANE_MAX_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, journalEntriesForWatcherDelete, resolveEventBatchLimit, resolveFullReconcileMs, shouldQuarantineWatcherDeleteIntents, WATCHER_REDISCOVERY_RETRY_MS, WATCHER_REDISCOVERY_RETRY_MAX_MS, } from "./sync-runner-watch-loop.js";
17
+ 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, shouldQuarantineWatcherDeleteIntents, WATCHER_REDISCOVERY_RETRY_MS, WATCHER_REDISCOVERY_RETRY_MAX_MS, } from "./sync-runner-watch-loop.js";
18
18
  import { PERSONAL_VAULT_JOURNAL_SLUG, readJournal, writeJournal, } from "../journal.js";
19
19
  import { HQ_CLOUD_VERSION } from "../version.js";
20
20
  import { lockPathFor, OPERATION_LOCKED_EXIT } from "../operation-lock.js";
@@ -4697,6 +4697,62 @@ describe("runRunnerWithLoop — event-push wiring", () => {
4697
4697
  await loop;
4698
4698
  fs.rmSync(hqRoot, { recursive: true, force: true });
4699
4699
  });
4700
+ it("fast lane: peels a tiny watcher path from mixed multi-megabyte residue", async () => {
4701
+ const hqRoot = fs.mkdtempSync(path.join(os.tmpdir(), "hq-fast-lane-mixed-watch-"));
4702
+ const tinyRel = "companies/indigo/realtime.md";
4703
+ const tiny = path.join(hqRoot, tinyRel);
4704
+ const bulk = [0, 1, 2].map((index) => path.join(hqRoot, `companies/indigo/session-${index}.log`));
4705
+ fs.mkdirSync(path.dirname(tiny), { recursive: true });
4706
+ fs.writeFileSync(tiny, "tiny realtime edit");
4707
+ for (const file of bulk)
4708
+ fs.writeFileSync(file, Buffer.alloc(8 * 1024 * 1024));
4709
+ const watcher = makeBatchWatcherStub();
4710
+ let triggerShutdown = () => { };
4711
+ let releaseBulk = () => { };
4712
+ const bulkGate = new Promise((resolve) => { releaseBulk = resolve; });
4713
+ const calls = [];
4714
+ let fullPasses = 0;
4715
+ const runPass = vi.fn().mockImplementation(async (passArgv) => {
4716
+ calls.push(passArgv);
4717
+ if (!passArgv.includes("--scope-path")) {
4718
+ fullPasses += 1;
4719
+ if (fullPasses === 2)
4720
+ await bulkGate;
4721
+ }
4722
+ return completedPassOutcome();
4723
+ });
4724
+ const loop = runRunnerWithLoop(["--companies", "--watch", "--event-push", "--direction", "push", "--hq-root", hqRoot], {
4725
+ runPass,
4726
+ clock: new FakeClock(),
4727
+ createWatcher: () => watcher,
4728
+ sleep: () => new Promise(() => { }),
4729
+ onShutdownSignal: (handler) => {
4730
+ triggerShutdown = handler;
4731
+ return () => { };
4732
+ },
4733
+ });
4734
+ await flushLoopMicrotasks();
4735
+ watcher.emit();
4736
+ await flushLoopMicrotasks();
4737
+ watcher.emit(tinyRel, {
4738
+ paths: new Map([
4739
+ [tiny, tinyRel],
4740
+ ...bulk.map((file, index) => [file, `companies/indigo/session-${index}.log`]),
4741
+ ]),
4742
+ });
4743
+ await flushLoopMicrotasks(30);
4744
+ // origin/main failure: expected a scoped tiny path while the bulk pass is
4745
+ // held, but #408 kept all four paths in the slow lane.
4746
+ expect(calls.filter((argv) => argv.includes("--scope-path"))).toHaveLength(1);
4747
+ expect(calls.flat().join(" ")).toContain("realtime.md");
4748
+ releaseBulk();
4749
+ await flushLoopMicrotasks(30);
4750
+ expect(calls.filter((argv) => argv.includes("--scope-path"))).toHaveLength(2);
4751
+ expect(calls.flat().join(" ")).toContain("session-0.log");
4752
+ triggerShutdown();
4753
+ await loop;
4754
+ fs.rmSync(hqRoot, { recursive: true, force: true });
4755
+ });
4700
4756
  it("fast lane: a small receiver pull dispatches during a slow bulk pass", async () => {
4701
4757
  const watcher = makeWatcherStub();
4702
4758
  let triggerShutdown = () => { };
@@ -4705,6 +4761,14 @@ describe("runRunnerWithLoop — event-push wiring", () => {
4705
4761
  const bulkGate = new Promise((resolve) => { releaseBulk = resolve; });
4706
4762
  const order = [];
4707
4763
  let fullPasses = 0;
4764
+ const journal = readJournal("acme");
4765
+ journal.files["realtime.md"] = {
4766
+ hash: "sha256:x",
4767
+ size: 16,
4768
+ syncedAt: "2026-08-26T00:00:00.000Z",
4769
+ direction: "down",
4770
+ };
4771
+ writeJournal("acme", journal);
4708
4772
  const runPass = vi.fn().mockImplementation(async (passArgv) => {
4709
4773
  if (passArgv.includes("--scope-path")) {
4710
4774
  order.push("fast-pull");
@@ -4954,12 +5018,28 @@ describe("runRunnerWithLoop — event-push wiring", () => {
4954
5018
  eventTimestamp: "2026-08-26T00:00:00.000Z",
4955
5019
  })),
4956
5020
  ];
5021
+ for (const [slug, prefix, count] of [["acme", "a", 17], ["beta", "b", 16]]) {
5022
+ const journal = readJournal(slug);
5023
+ for (let index = 0; index < count; index++) {
5024
+ journal.files[`${prefix}-${index}.md`] = {
5025
+ hash: "sha256:x",
5026
+ size: 16,
5027
+ syncedAt: "2026-08-26T00:00:00.000Z",
5028
+ direction: "down",
5029
+ };
5030
+ }
5031
+ writeJournal(slug, journal);
5032
+ }
4957
5033
  const receiverDrain = receiverSync({
4958
5034
  events,
4959
5035
  signal: new AbortController().signal,
4960
5036
  });
4961
5037
  await flushLoopMicrotasks(20);
4962
- expect(runPass).not.toHaveBeenCalled();
5038
+ // The 33-event mixed delivery peels its first 32 bounded paths into the
5039
+ // fast lane (17 acme + 15 beta); the final beta event remains slow.
5040
+ expect(runPass).toHaveBeenCalledTimes(2);
5041
+ expect(runPass.mock.calls.flat().join(" ")).toContain("a-16.md");
5042
+ expect(runPass.mock.calls.flat().join(" ")).toContain("b-14.md");
4963
5043
  releaseBulk();
4964
5044
  await receiverDrain;
4965
5045
  triggerShutdown();
@@ -5171,6 +5251,30 @@ describe("runRunnerWithLoop — event-push wiring", () => {
5171
5251
  fs.rmSync(hqRoot, { recursive: true, force: true });
5172
5252
  }
5173
5253
  });
5254
+ it("fast lane: keeps a latest large revision in the slow residue and caps 33 small paths", () => {
5255
+ const hqRoot = fs.mkdtempSync(path.join(os.tmpdir(), "hq-fast-lane-partition-"));
5256
+ const rel = "companies/indigo/revised.md";
5257
+ const absolute = path.join(hqRoot, rel);
5258
+ fs.mkdirSync(path.dirname(absolute), { recursive: true });
5259
+ // The map is the coalescing/affinity boundary: its one value is the latest
5260
+ // revision, so this path cannot appear in both sub-batches.
5261
+ fs.writeFileSync(absolute, Buffer.alloc(DEFAULT_FAST_LANE_MAX_FILE_BYTES + 1));
5262
+ const revised = partitionLocalBatch({ paths: new Map([[absolute, rel]]) });
5263
+ expect(revised.fast).toBeNull();
5264
+ expect(revised.slow?.paths.get(absolute)).toBe(rel);
5265
+ const entries = [];
5266
+ for (let index = 0; index < DEFAULT_FAST_LANE_MAX_FILES + 1; index++) {
5267
+ const file = path.join(hqRoot, `companies/indigo/small-${index}.md`);
5268
+ fs.writeFileSync(file, "x");
5269
+ entries.push([file, `companies/indigo/small-${index}.md`]);
5270
+ }
5271
+ const capped = partitionLocalBatch({ paths: new Map(entries) });
5272
+ expect(capped.fast?.paths.size).toBe(DEFAULT_FAST_LANE_MAX_FILES);
5273
+ // The 33rd path deliberately remains in the slow residue, rather than
5274
+ // creating a second concurrent realtime dispatch.
5275
+ expect(capped.slow?.paths.size).toBe(1);
5276
+ fs.rmSync(hqRoot, { recursive: true, force: true });
5277
+ });
5174
5278
  it("U07: drains the first watcher event after activation without guarded overlap", async () => {
5175
5279
  const watcher = makeWatcherStub();
5176
5280
  let triggerShutdown = () => { };