@indigoai-us/hq-cloud 6.13.5 → 6.14.1

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.
@@ -723,6 +723,8 @@ interface ParsedArgs {
723
723
  * mode (single-company runs never visit the personal target).
724
724
  */
725
725
  skipPersonal: boolean;
726
+ /** Repeatable company/personal-relative path scope for push-only passes. */
727
+ scopePaths: string[];
726
728
  /**
727
729
  * Bounded wait (seconds) for the per-root operation lock when another op is
728
730
  * already running. `0` → refuse immediately (pre-wait behavior); omitted →
@@ -742,6 +744,7 @@ function parseArgs(argv: string[]): ParsedArgs | { error: string } {
742
744
  let watch = false;
743
745
  let pollRemoteMs: number | undefined;
744
746
  let skipPersonal = false;
747
+ const scopePaths: string[] = [];
745
748
  let eventPush = false;
746
749
  let lockTimeoutSec: number | undefined;
747
750
 
@@ -817,6 +820,12 @@ function parseArgs(argv: string[]): ParsedArgs | { error: string } {
817
820
  // @getindigo.ai identities for the first release.
818
821
  eventPush = true;
819
822
  break;
823
+ case "--scope-path": {
824
+ const val = argv[++i];
825
+ if (!val) return { error: "--scope-path requires a value" };
826
+ scopePaths.push(val);
827
+ break;
828
+ }
820
829
  case "--lock-timeout": {
821
830
  const val = argv[++i];
822
831
  if (!val) return { error: "--lock-timeout requires a value (seconds)" };
@@ -867,6 +876,7 @@ function parseArgs(argv: string[]): ParsedArgs | { error: string } {
867
876
  watch,
868
877
  pollRemoteMs,
869
878
  skipPersonal,
879
+ scopePaths,
870
880
  eventPush,
871
881
  lockTimeoutSec,
872
882
  };
@@ -1219,6 +1229,7 @@ export async function runRunner(
1219
1229
  shareFn,
1220
1230
  resolveDeletePolicy,
1221
1231
  emit,
1232
+ scopePaths: parsed.scopePaths,
1222
1233
  });
1223
1234
  const { errors, allConflicts } = fanout;
1224
1235
  const rollup = rollupAllComplete(plan, fanout.stateByCompany);
@@ -350,6 +350,48 @@ describe("US-002: createWatchPathFilter — personal-vault exclusions", () => {
350
350
  // mode the ignore stack alone governs and lets it through.
351
351
  expect(nonPersonal(path.join(ROOT, "companies/indigo/board.json"))).toBe(true);
352
352
  });
353
+
354
+ // ── Never-synced heavy buckets pruned in BOTH modes ─────────────────────
355
+ // In --companies mode personalMode=false, so the excluded-top-level layer
356
+ // never ran and the watcher descended into the local git worktrees
357
+ // (workspace/worktrees/, ~200k transient files from agent coding runs) and
358
+ // the on-box repos checkout (repos/). Neither ever vault-syncs in any mode,
359
+ // so watching them only burns inotify watches (near the OS limit) + CPU on
360
+ // build churn it discards, and can trip the overflow→full-reconcile fallback.
361
+ it("non-personal mode EXCLUDES never-synced heavy buckets (workspace/worktrees, repos)", () => {
362
+ expect(
363
+ nonPersonal(path.join(ROOT, "workspace/worktrees/hq-console/src/a.ts")),
364
+ ).toBe(false);
365
+ // The bucket dir itself is pruned so chokidar never descends into it.
366
+ expect(nonPersonal(path.join(ROOT, "workspace/worktrees"), true)).toBe(false);
367
+ expect(
368
+ nonPersonal(path.join(ROOT, "repos/private/hq-cloud/src/x.ts")),
369
+ ).toBe(false);
370
+ expect(nonPersonal(path.join(ROOT, "repos"), true)).toBe(false);
371
+ });
372
+
373
+ it("non-personal mode STILL watches synced content (companies/, workspace/agency, continuity)", () => {
374
+ expect(
375
+ nonPersonal(path.join(ROOT, "companies/indigo/board.json")),
376
+ ).toBe(true);
377
+ expect(
378
+ nonPersonal(path.join(ROOT, "workspace/agency/indigo/nick/status.json")),
379
+ ).toBe(true);
380
+ expect(
381
+ nonPersonal(path.join(ROOT, "workspace/threads/handoff.json")),
382
+ ).toBe(true);
383
+ // chokidar must still descend into workspace/ to reach agency + continuity.
384
+ expect(nonPersonal(path.join(ROOT, "workspace"), true)).toBe(true);
385
+ // A worktrees SIBLING under workspace/ is unaffected (only worktrees pruned).
386
+ expect(
387
+ nonPersonal(path.join(ROOT, "workspace/agency"), true),
388
+ ).toBe(true);
389
+ });
390
+
391
+ it("personal mode still excludes worktrees + repos (regression, unchanged)", () => {
392
+ expect(personal(path.join(ROOT, "workspace/worktrees/x/a.ts"))).toBe(false);
393
+ expect(personal(path.join(ROOT, "repos/private/x/a.ts"))).toBe(false);
394
+ });
353
395
  });
354
396
 
355
397
  describe("US-002: TreeWatcher — debounce coalesce (FakeClock seam)", () => {
package/src/watcher.ts CHANGED
@@ -407,6 +407,24 @@ export function createWatchPathFilter(
407
407
  // createIgnoreFilter returns true = "sync this"; false = "ignored".
408
408
  if (!ignoreFilter(absolutePath, isDir)) return false;
409
409
 
410
+ // Never-synced heavy buckets — pruned in BOTH modes. The local git worktrees
411
+ // (`workspace/worktrees/`, where `/worktree` and agent coding runs check out
412
+ // repos — often 100k+ transient source/build files) and the on-box `repos/`
413
+ // checkout are never vault-synced in any mode. In personalMode the
414
+ // excluded-top-level layer below already drops them; in `--companies` mode
415
+ // that layer is skipped, so without this the watcher descends into every
416
+ // worktree source tree — tens of thousands of wasted inotify watches (the
417
+ // outpost sat at ~78% of the OS limit) plus CPU spent processing build churn
418
+ // it immediately discards, and enough backlog to trip the
419
+ // overflow → full-reconcile fallback. Returning false for the dir probe too
420
+ // prunes the whole subtree at descent instead of per-file. `companies/` and
421
+ // the synced `workspace/agency/` + continuity-pointer paths are untouched.
422
+ const topSegments = rel.split("/");
423
+ if (topSegments[0] === "repos") return false;
424
+ if (topSegments[0] === "workspace" && topSegments[1] === "worktrees") {
425
+ return false;
426
+ }
427
+
410
428
  if (personalMode) {
411
429
  // Continuity-pointer carve-out: the session pointer lives under the
412
430
  // otherwise-excluded `workspace/`, so it must re-include BEFORE the