@indigoai-us/hq-cloud 6.14.35 → 6.14.37

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.
@@ -92,6 +92,8 @@ function defaultShareResult(overrides = {}) {
92
92
  filesExcludedByPolicy: 0,
93
93
  filesExcludedByScope: 0,
94
94
  filesExcludedByIgnore: 0,
95
+ unreachablePaths: [],
96
+ linkedSubtreesNotShipped: [],
95
97
  conflictPaths: [],
96
98
  aborted: false,
97
99
  ...overrides,
@@ -6189,6 +6191,9 @@ describe("watcher delete intents without pre-seeded authorization", () => {
6189
6191
  });
6190
6192
  let initialPullGated = options.gateInitialPull === true;
6191
6193
  const runPass = vi.fn(async (argv) => {
6194
+ // Lets a test hold a pass mid-flight and act while the loop is awaiting
6195
+ // it, which is where producer-side state that outlives a pass is decided.
6196
+ await options.onPass?.(argv);
6192
6197
  const direction = argv[argv.indexOf("--direction") + 1];
6193
6198
  if (direction === "push") {
6194
6199
  const journal = readJournal("indigo");
@@ -6259,7 +6264,8 @@ describe("watcher delete intents without pre-seeded authorization", () => {
6259
6264
  return 0;
6260
6265
  });
6261
6266
  const loop = runRunnerWithLoop([
6262
- "--companies", "--watch", "--event-push", "--direction", "both",
6267
+ "--companies", "--watch", "--event-push",
6268
+ "--direction", options.direction ?? "both",
6263
6269
  "--hq-root", hqRoot, "--poll-remote-ms", "60000",
6264
6270
  ], {
6265
6271
  runPass,
@@ -6660,6 +6666,86 @@ describe("watcher delete intents without pre-seeded authorization", () => {
6660
6666
  expect(h.remote.size).toBe(11);
6661
6667
  await stopHarness(h);
6662
6668
  });
6669
+ // A pass is awaited, so a bulk removal observed while one is in flight
6670
+ // registers its quarantine root only after that pass's batch was taken — its
6671
+ // own snapshots ride the NEXT batch. Expiring the whole set once the pass
6672
+ // returns would drop the root before `prepareWatcherChanges` ever sees that
6673
+ // batch, re-stamping the revoked descendant intents and handing the engine
6674
+ // the bulk delete the quarantine refused (remotely deleting the subtree under
6675
+ // HQ_SYNC_DELETE_POLICY=all).
6676
+ it("a bulk removal observed during an in-flight pass keeps its quarantine", async () => {
6677
+ const keys = Array.from({ length: 12 }, (_, i) => `knowledge/moved/f-${i}.md`);
6678
+ let releaseGatedPush = () => { };
6679
+ const gatedPush = new Promise((resolve) => {
6680
+ releaseGatedPush = resolve;
6681
+ });
6682
+ let gateArmed = false;
6683
+ let gateReached = false;
6684
+ // Push-only: a pull would restore the quarantined subtree (the documented
6685
+ // recovery for an intent-less disappearance) before the batch carrying the
6686
+ // episode is prepared, masking whether the refusal itself survived.
6687
+ const h = startDeleteHarness([...keys, "knowledge/other.md"], {
6688
+ direction: "push",
6689
+ onPass: async (_argv) => {
6690
+ if (!gateArmed)
6691
+ return;
6692
+ gateArmed = false;
6693
+ gateReached = true;
6694
+ await gatedPush;
6695
+ },
6696
+ });
6697
+ await settle();
6698
+ const intentCount = () => Object.values(readJournal("indigo").files).filter((entry) => entry.localDeleteIntent).length;
6699
+ // An unrelated edit drains as a scoped push, which then blocks on the gate
6700
+ // with its batch already taken from the pending buffer.
6701
+ gateArmed = true;
6702
+ const otherPath = path.join(h.companyRoot, "knowledge/other.md");
6703
+ fs.writeFileSync(otherPath, "edited");
6704
+ h.watcher.emit("companies/indigo/knowledge/other.md", {
6705
+ paths: new Map([[otherPath, "companies/indigo/knowledge/other.md"]]),
6706
+ });
6707
+ h.tick();
6708
+ await settle();
6709
+ expect(gateReached).toBe(true);
6710
+ // The whole subtree disappears while that pass is still in flight.
6711
+ const childChanges = new Map();
6712
+ for (const key of keys) {
6713
+ const absolutePath = path.join(h.companyRoot, key);
6714
+ fs.unlinkSync(absolutePath);
6715
+ childChanges.set(absolutePath, {
6716
+ kind: "unlink",
6717
+ deleteSnapshots: h.capture()(`companies/indigo/${key}`, "unlink"),
6718
+ });
6719
+ }
6720
+ const subtree = path.join(h.companyRoot, "knowledge/moved");
6721
+ fs.rmSync(subtree, { recursive: true, force: true });
6722
+ const dirSnapshots = h.capture()("companies/indigo/knowledge/moved", "unlinkDir");
6723
+ expect(dirSnapshots).toHaveLength(0);
6724
+ expect(intentCount()).toBe(0);
6725
+ h.watcher.emit("companies/indigo/knowledge/moved", {
6726
+ paths: new Map([
6727
+ ...keys.map((key) => [
6728
+ path.join(h.companyRoot, key),
6729
+ `companies/indigo/${key}`,
6730
+ ]),
6731
+ [subtree, "companies/indigo/knowledge/moved"],
6732
+ ]),
6733
+ changes: new Map([
6734
+ ...childChanges,
6735
+ [subtree, { kind: "unlinkDir", deleteSnapshots: dirSnapshots }],
6736
+ ]),
6737
+ });
6738
+ // Completing the overlapped pass must not expire the refusal recorded
6739
+ // during it: the next drain prepares the batch that carries the episode.
6740
+ releaseGatedPush();
6741
+ await settle();
6742
+ h.tick();
6743
+ await settle();
6744
+ expect(intentCount()).toBe(0);
6745
+ for (const key of keys)
6746
+ expect(h.remote.has(key)).toBe(true);
6747
+ await stopHarness(h);
6748
+ });
6663
6749
  it("HQ_SYNC_DELETE_BULK_OVERRIDE=1 lets a whole-shard unlinkDir propagate", async () => {
6664
6750
  process.env.HQ_SYNC_DELETE_BULK_OVERRIDE = "1";
6665
6751
  try {