@indigoai-us/hq-cloud 6.16.65 → 6.16.67

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 (126) hide show
  1. package/dist/bin/sync-runner-company.d.ts +14 -0
  2. package/dist/bin/sync-runner-company.d.ts.map +1 -1
  3. package/dist/bin/sync-runner-company.js +42 -8
  4. package/dist/bin/sync-runner-company.js.map +1 -1
  5. package/dist/bin/sync-runner-company.test.js +62 -7
  6. package/dist/bin/sync-runner-company.test.js.map +1 -1
  7. package/dist/bin/sync-runner-planning.d.ts +12 -3
  8. package/dist/bin/sync-runner-planning.d.ts.map +1 -1
  9. package/dist/bin/sync-runner-planning.js +65 -3
  10. package/dist/bin/sync-runner-planning.js.map +1 -1
  11. package/dist/bin/sync-runner-planning.test.js +172 -1
  12. package/dist/bin/sync-runner-planning.test.js.map +1 -1
  13. package/dist/bin/sync-runner-watch-loop.d.ts +51 -2
  14. package/dist/bin/sync-runner-watch-loop.d.ts.map +1 -1
  15. package/dist/bin/sync-runner-watch-loop.js +409 -165
  16. package/dist/bin/sync-runner-watch-loop.js.map +1 -1
  17. package/dist/bin/sync-runner-watch-routes.d.ts +19 -2
  18. package/dist/bin/sync-runner-watch-routes.d.ts.map +1 -1
  19. package/dist/bin/sync-runner-watch-routes.js +40 -5
  20. package/dist/bin/sync-runner-watch-routes.js.map +1 -1
  21. package/dist/bin/sync-runner-watch-routes.test.js +47 -0
  22. package/dist/bin/sync-runner-watch-routes.test.js.map +1 -1
  23. package/dist/bin/sync-runner.d.ts +59 -5
  24. package/dist/bin/sync-runner.d.ts.map +1 -1
  25. package/dist/bin/sync-runner.js +48 -4
  26. package/dist/bin/sync-runner.js.map +1 -1
  27. package/dist/bin/sync-runner.test.js +1553 -192
  28. package/dist/bin/sync-runner.test.js.map +1 -1
  29. package/dist/cli/onepass-pull-planner-regressions.test.d.ts +2 -0
  30. package/dist/cli/onepass-pull-planner-regressions.test.d.ts.map +1 -0
  31. package/dist/cli/onepass-pull-planner-regressions.test.js +300 -0
  32. package/dist/cli/onepass-pull-planner-regressions.test.js.map +1 -0
  33. package/dist/cli/onepass-pull-planner.test.d.ts +2 -0
  34. package/dist/cli/onepass-pull-planner.test.d.ts.map +1 -0
  35. package/dist/cli/onepass-pull-planner.test.js +266 -0
  36. package/dist/cli/onepass-pull-planner.test.js.map +1 -0
  37. package/dist/cli/push-scan-exclusions.test.js +4 -4
  38. package/dist/cli/push-scan-exclusions.test.js.map +1 -1
  39. package/dist/cli/share.d.ts +31 -1
  40. package/dist/cli/share.d.ts.map +1 -1
  41. package/dist/cli/share.js +143 -8
  42. package/dist/cli/share.js.map +1 -1
  43. package/dist/cli/share.test.js +350 -5
  44. package/dist/cli/share.test.js.map +1 -1
  45. package/dist/cli/streaming-pull-planner.bench.test.d.ts +15 -0
  46. package/dist/cli/streaming-pull-planner.bench.test.d.ts.map +1 -0
  47. package/dist/cli/streaming-pull-planner.bench.test.js +273 -0
  48. package/dist/cli/streaming-pull-planner.bench.test.js.map +1 -0
  49. package/dist/cli/sync-scope.test.js +8 -0
  50. package/dist/cli/sync-scope.test.js.map +1 -1
  51. package/dist/cli/sync.d.ts +12 -0
  52. package/dist/cli/sync.d.ts.map +1 -1
  53. package/dist/cli/sync.js +297 -125
  54. package/dist/cli/sync.js.map +1 -1
  55. package/dist/cli/sync.test.js +255 -2
  56. package/dist/cli/sync.test.js.map +1 -1
  57. package/dist/journal-row-store.d.ts +40 -5
  58. package/dist/journal-row-store.d.ts.map +1 -1
  59. package/dist/journal-row-store.js +240 -21
  60. package/dist/journal-row-store.js.map +1 -1
  61. package/dist/journal-row-store.test.js +54 -0
  62. package/dist/journal-row-store.test.js.map +1 -1
  63. package/dist/journal-snapshot-v4.d.ts.map +1 -1
  64. package/dist/journal-snapshot-v4.js +11 -6
  65. package/dist/journal-snapshot-v4.js.map +1 -1
  66. package/dist/journal-store.test.js +324 -7
  67. package/dist/journal-store.test.js.map +1 -1
  68. package/dist/journal.d.ts +51 -16
  69. package/dist/journal.d.ts.map +1 -1
  70. package/dist/journal.js +303 -79
  71. package/dist/journal.js.map +1 -1
  72. package/dist/lib/exit-codes.d.ts +15 -0
  73. package/dist/lib/exit-codes.d.ts.map +1 -1
  74. package/dist/lib/exit-codes.js +15 -0
  75. package/dist/lib/exit-codes.js.map +1 -1
  76. package/dist/personal-vault.d.ts.map +1 -1
  77. package/dist/personal-vault.js +10 -3
  78. package/dist/personal-vault.js.map +1 -1
  79. package/dist/personal-vault.test.js +35 -0
  80. package/dist/personal-vault.test.js.map +1 -1
  81. package/dist/remote-pull.d.ts.map +1 -1
  82. package/dist/remote-pull.js +9 -2
  83. package/dist/remote-pull.js.map +1 -1
  84. package/dist/remote-pull.test.js +59 -2
  85. package/dist/remote-pull.test.js.map +1 -1
  86. package/dist/s3.d.ts +1 -0
  87. package/dist/s3.d.ts.map +1 -1
  88. package/dist/s3.js +20 -2
  89. package/dist/s3.js.map +1 -1
  90. package/dist/s3.list-cost.test.d.ts +2 -0
  91. package/dist/s3.list-cost.test.d.ts.map +1 -0
  92. package/dist/s3.list-cost.test.js +51 -0
  93. package/dist/s3.list-cost.test.js.map +1 -0
  94. package/dist/sync/area-ledger.d.ts +27 -3
  95. package/dist/sync/area-ledger.d.ts.map +1 -1
  96. package/dist/sync/area-ledger.js +149 -39
  97. package/dist/sync/area-ledger.js.map +1 -1
  98. package/dist/sync/area-ledger.test.js +192 -5
  99. package/dist/sync/area-ledger.test.js.map +1 -1
  100. package/dist/sync/parallel-sync-coordinator.test.js +29 -0
  101. package/dist/sync/parallel-sync-coordinator.test.js.map +1 -1
  102. package/dist/sync/realtime-telemetry.d.ts +1 -1
  103. package/dist/sync/realtime-telemetry.d.ts.map +1 -1
  104. package/dist/sync/realtime-telemetry.js +14 -6
  105. package/dist/sync/realtime-telemetry.js.map +1 -1
  106. package/dist/sync/realtime-telemetry.test.js +136 -16
  107. package/dist/sync/realtime-telemetry.test.js.map +1 -1
  108. package/dist/sync/remote-file-spool.d.ts +16 -0
  109. package/dist/sync/remote-file-spool.d.ts.map +1 -0
  110. package/dist/sync/remote-file-spool.js +91 -0
  111. package/dist/sync/remote-file-spool.js.map +1 -0
  112. package/dist/sync/remote-file-spool.test.d.ts +2 -0
  113. package/dist/sync/remote-file-spool.test.d.ts.map +1 -0
  114. package/dist/sync/remote-file-spool.test.js +77 -0
  115. package/dist/sync/remote-file-spool.test.js.map +1 -0
  116. package/dist/sync/state-store.d.ts +6 -0
  117. package/dist/sync/state-store.d.ts.map +1 -1
  118. package/dist/sync/state-store.js +14 -2
  119. package/dist/sync/state-store.js.map +1 -1
  120. package/dist/watcher.d.ts +12 -0
  121. package/dist/watcher.d.ts.map +1 -1
  122. package/dist/watcher.js +131 -0
  123. package/dist/watcher.js.map +1 -1
  124. package/dist/watcher.test.js +63 -0
  125. package/dist/watcher.test.js.map +1 -1
  126. package/package.json +2 -1
@@ -64,7 +64,7 @@ vi.mock("readline", () => ({
64
64
  })),
65
65
  }));
66
66
  import * as readline from "readline";
67
- import { share, ServerOwnedPushPathsError, UnreachablePushPathsError, UnregisteredCompanySkillError, currentUploadBytes, parseRegisteredCompanySkillUid, _testing as shareTesting, } from "./share.js";
67
+ import { share, ServerOwnedPushPathsError, UnreachablePushPathsError, UnregisteredCompanySkillError, currentUploadBytes, FULL_PUSH_COOPERATIVE_CHECKPOINT_BATCH_SIZE, parseRegisteredCompanySkillUid, _testing as shareTesting, } from "./share.js";
68
68
  import { deleteRemoteFile, downloadFile, headRemoteFile, primeObjectTransport, primeUploads, uploadFile, uploadSymlink, WindowsSymlinkPrivilegeError, } from "../s3.js";
69
69
  import { VaultAuthError, VaultClientError, VaultConflictError, VaultPermissionDeniedError, } from "../vault-client.js";
70
70
  import { CognitoRefreshError } from "../cognito-auth.js";
@@ -72,6 +72,7 @@ import { RateLimitedError } from "../object-io.js";
72
72
  import { AreaLedgerMigration, closeCachedAreaJournalMigration, hashFile, openJournalStateStoreForMigration, readJournal, readJournalScoped, setAreaLedgerMigrationTestHooksForTest, writeJournal, } from "../journal.js";
73
73
  import { ReconcileCursorStore } from "../sync/reconcile-cursor.js";
74
74
  import { AreaResolver } from "../sync/area-resolver.js";
75
+ import { CooperativePassScheduler } from "../bin/sync-runner-watch-loop.js";
75
76
  /**
76
77
  * Read a journal the way production does.
77
78
  *
@@ -1658,6 +1659,7 @@ describe("share", () => {
1658
1659
  fs.rmSync(tmpDir, { recursive: true, force: true });
1659
1660
  fs.rmSync(stateDir, { recursive: true, force: true });
1660
1661
  delete process.env.HQ_STATE_DIR;
1662
+ shareTesting.disableCooperativeRowVisits();
1661
1663
  });
1662
1664
  it("shares a single file keyed relative to the company root", async () => {
1663
1665
  const companyRoot = path.join(tmpDir, "companies", "acme");
@@ -1675,6 +1677,285 @@ describe("share", () => {
1675
1677
  // Remote key must be company-relative, not hqRoot-relative
1676
1678
  expect(uploadFile).toHaveBeenCalledWith(expect.anything(), testFile, "test.md", undefined, expect.anything());
1677
1679
  });
1680
+ it("hands queued realtime work a settled full-push boundary without losing journal rows", async () => {
1681
+ const companyRoot = path.join(tmpDir, "companies", "acme");
1682
+ const fileCount = FULL_PUSH_COOPERATIVE_CHECKPOINT_BATCH_SIZE + 1;
1683
+ for (let index = 0; index < fileCount; index++) {
1684
+ const file = path.join(companyRoot, "docs", "full-push", `${index}.md`);
1685
+ fs.mkdirSync(path.dirname(file), { recursive: true });
1686
+ fs.writeFileSync(file, "full-push body");
1687
+ }
1688
+ const scheduler = new CooperativePassScheduler(() => 0);
1689
+ let uploadsCompleted = 0;
1690
+ let uploadsInFlight = 0;
1691
+ let maxUploadsInFlight = 0;
1692
+ let realtimeQueued = false;
1693
+ let realtimeDispatchedAt = -1;
1694
+ let checkpointCalls = 0;
1695
+ let realtimePass;
1696
+ const upload = vi.mocked(uploadFile);
1697
+ const originalUpload = upload.getMockImplementation();
1698
+ upload.mockImplementation(async () => {
1699
+ uploadsInFlight++;
1700
+ maxUploadsInFlight = Math.max(maxUploadsInFlight, uploadsInFlight);
1701
+ await Promise.resolve();
1702
+ uploadsCompleted++;
1703
+ if (uploadsCompleted === 1) {
1704
+ realtimeQueued = true;
1705
+ realtimePass = scheduler.submit(async () => {
1706
+ // `executeUploads` drains the full unit before the scheduler grants
1707
+ // realtime the root lock: no two durable writers overlap.
1708
+ expect(uploadsInFlight).toBe(0);
1709
+ realtimeDispatchedAt = uploadsCompleted;
1710
+ const realtimePath = path.join(companyRoot, "docs", "realtime.md");
1711
+ fs.writeFileSync(realtimePath, "realtime body");
1712
+ const journal = readJournal("acme");
1713
+ journal.files["docs/realtime.md"] = {
1714
+ hash: hashFile(realtimePath),
1715
+ size: fs.statSync(realtimePath).size,
1716
+ syncedAt: "2026-09-17T12:00:00.000Z",
1717
+ direction: "up",
1718
+ remoteEtag: "realtime",
1719
+ mtimeMs: fs.statSync(realtimePath).mtimeMs,
1720
+ ctimeMs: fs.statSync(realtimePath).ctimeMs,
1721
+ };
1722
+ writeJournal("acme", journal);
1723
+ }, "realtime");
1724
+ }
1725
+ uploadsInFlight--;
1726
+ return { etag: '"full-push-etag"' };
1727
+ });
1728
+ try {
1729
+ await scheduler.submit(async ({ checkpoint }) => {
1730
+ await share({
1731
+ paths: [companyRoot],
1732
+ company: "acme",
1733
+ vaultConfig: mockConfig,
1734
+ hqRoot: tmpDir,
1735
+ skipUnchanged: true,
1736
+ propagateDeletes: true,
1737
+ cooperativeCheckpoint: async () => {
1738
+ checkpointCalls++;
1739
+ return checkpoint();
1740
+ },
1741
+ });
1742
+ }, "chore");
1743
+ await realtimePass;
1744
+ }
1745
+ finally {
1746
+ if (originalUpload)
1747
+ upload.mockImplementation(originalUpload);
1748
+ else
1749
+ upload.mockReset();
1750
+ }
1751
+ expect(maxUploadsInFlight).toBeGreaterThan(1);
1752
+ expect(realtimeQueued).toBe(true);
1753
+ expect(checkpointCalls).toBeGreaterThan(0);
1754
+ expect(realtimeDispatchedAt).toBeGreaterThan(0);
1755
+ expect(realtimeDispatchedAt).toBeLessThanOrEqual(FULL_PUSH_COOPERATIVE_CHECKPOINT_BATCH_SIZE);
1756
+ // Reopen the durable journal, rather than inspecting the stale first-pass
1757
+ // object: every pre-handoff key and the realtime key must survive.
1758
+ const durable = readJournal("acme");
1759
+ expect(Object.keys(durable.files)).toHaveLength(fileCount + 1);
1760
+ expect(durable.files["docs/realtime.md"]).toBeDefined();
1761
+ for (let index = 0; index < fileCount; index++) {
1762
+ expect(durable.files[`docs/full-push/${index}.md`]).toBeDefined();
1763
+ }
1764
+ }, 120_000);
1765
+ it("hands an all-skipped full personal push to queued realtime work", async () => {
1766
+ const companyRoot = path.join(tmpDir, "companies", "acme");
1767
+ const fileCount = FULL_PUSH_COOPERATIVE_CHECKPOINT_BATCH_SIZE + 1;
1768
+ const files = {};
1769
+ for (let index = 0; index < fileCount; index++) {
1770
+ const relativePath = `docs/unchanged/${index}.md`;
1771
+ const file = path.join(companyRoot, relativePath);
1772
+ fs.mkdirSync(path.dirname(file), { recursive: true });
1773
+ fs.writeFileSync(file, "unchanged body");
1774
+ const stat = fs.statSync(file);
1775
+ files[relativePath] = {
1776
+ hash: hashFile(file),
1777
+ size: stat.size,
1778
+ mtimeMs: stat.mtimeMs,
1779
+ ctimeMs: stat.ctimeMs,
1780
+ syncedAt: "2026-09-17T12:00:00.000Z",
1781
+ direction: "up",
1782
+ remoteEtag: "unchanged",
1783
+ };
1784
+ }
1785
+ seedJournalAt(path.join(stateDir, "sync-journal.acme.json"), {
1786
+ version: "2",
1787
+ lastSync: "2026-09-17T12:00:00.000Z",
1788
+ files,
1789
+ pulls: [],
1790
+ });
1791
+ const scheduler = new CooperativePassScheduler(() => 0);
1792
+ let fullPassFinished = false;
1793
+ let eventLoopTicked = false;
1794
+ let realtimeQueued = false;
1795
+ let realtimeRanBeforeFullPassFinished = false;
1796
+ let checkpointCalls = 0;
1797
+ let realtimePass;
1798
+ let resolveRealtimeScheduled;
1799
+ const realtimeScheduled = new Promise((resolve) => {
1800
+ resolveRealtimeScheduled = resolve;
1801
+ });
1802
+ const queueRealtime = () => {
1803
+ if (realtimeQueued)
1804
+ return;
1805
+ realtimeQueued = true;
1806
+ setImmediate(() => {
1807
+ eventLoopTicked = true;
1808
+ realtimePass = scheduler.submit(async () => {
1809
+ realtimeRanBeforeFullPassFinished = !fullPassFinished;
1810
+ }, "realtime");
1811
+ resolveRealtimeScheduled?.();
1812
+ });
1813
+ };
1814
+ await scheduler.submit(async ({ checkpoint }) => {
1815
+ try {
1816
+ const result = await share({
1817
+ paths: [companyRoot],
1818
+ company: "acme",
1819
+ vaultConfig: mockConfig,
1820
+ hqRoot: tmpDir,
1821
+ skipUnchanged: true,
1822
+ onEvent: (event) => {
1823
+ if (event.type === "plan")
1824
+ queueRealtime();
1825
+ },
1826
+ cooperativeCheckpoint: async () => {
1827
+ checkpointCalls++;
1828
+ return checkpoint();
1829
+ },
1830
+ });
1831
+ expect(result.filesSkipped).toBe(fileCount);
1832
+ expect(result.filesUploaded).toBe(0);
1833
+ }
1834
+ finally {
1835
+ fullPassFinished = true;
1836
+ }
1837
+ }, "chore");
1838
+ await realtimeScheduled;
1839
+ await realtimePass;
1840
+ expect(eventLoopTicked).toBe(true);
1841
+ expect(realtimeQueued).toBe(true);
1842
+ expect(realtimeRanBeforeFullPassFinished).toBe(true);
1843
+ expect(checkpointCalls).toBeGreaterThan(0);
1844
+ expect(uploadFile).not.toHaveBeenCalled();
1845
+ expect(Object.keys(readJournal("acme").files)).toHaveLength(fileCount);
1846
+ }, 120_000);
1847
+ it("resumes a skip-only full push at the next unit after realtime hand-off", async () => {
1848
+ const companyRoot = path.join(tmpDir, "companies", "acme");
1849
+ const fileCount = FULL_PUSH_COOPERATIVE_CHECKPOINT_BATCH_SIZE * 3;
1850
+ const files = {};
1851
+ for (let index = 0; index < fileCount; index++) {
1852
+ const relativePath = `docs/resume/${index}.md`;
1853
+ const file = path.join(companyRoot, relativePath);
1854
+ fs.mkdirSync(path.dirname(file), { recursive: true });
1855
+ fs.writeFileSync(file, "resume body");
1856
+ const stat = fs.statSync(file);
1857
+ files[relativePath] = {
1858
+ hash: hashFile(file),
1859
+ size: stat.size,
1860
+ mtimeMs: stat.mtimeMs,
1861
+ ctimeMs: stat.ctimeMs,
1862
+ syncedAt: "2026-09-17T12:00:00.000Z",
1863
+ direction: "up",
1864
+ remoteEtag: "unchanged",
1865
+ };
1866
+ }
1867
+ seedJournalAt(path.join(stateDir, "sync-journal.acme.json"), {
1868
+ version: "2",
1869
+ lastSync: "2026-09-17T12:00:00.000Z",
1870
+ files,
1871
+ pulls: [],
1872
+ });
1873
+ const scheduler = new CooperativePassScheduler(() => 0);
1874
+ let fullPassFinished = false;
1875
+ let realtimeRanBeforeFullPassFinished = false;
1876
+ const planEvents = [];
1877
+ const realtimePhaseRuns = [];
1878
+ const realtimePasses = [];
1879
+ const queuedForPhase = new Set();
1880
+ let currentPhase = "collection";
1881
+ const queueRealtimeForCurrentPhase = () => {
1882
+ const phase = currentPhase;
1883
+ if (queuedForPhase.has(phase))
1884
+ return;
1885
+ queuedForPhase.add(phase);
1886
+ setImmediate(() => {
1887
+ realtimePasses.push(scheduler.submit(async () => {
1888
+ realtimePhaseRuns.push(phase);
1889
+ if (!fullPassFinished)
1890
+ realtimeRanBeforeFullPassFinished = true;
1891
+ }, "realtime"));
1892
+ });
1893
+ };
1894
+ shareTesting.enableCooperativeRowVisits();
1895
+ let visits = {
1896
+ collect: new Map(),
1897
+ plan: new Map(),
1898
+ };
1899
+ try {
1900
+ await scheduler.submit(async ({ checkpoint }) => {
1901
+ queueRealtimeForCurrentPhase();
1902
+ try {
1903
+ const result = await share({
1904
+ paths: [companyRoot],
1905
+ company: "acme",
1906
+ vaultConfig: mockConfig,
1907
+ hqRoot: tmpDir,
1908
+ skipUnchanged: true,
1909
+ propagateDeletes: true,
1910
+ onEvent: (event) => {
1911
+ if (event.type === "plan") {
1912
+ planEvents.push(event);
1913
+ currentPhase = "execution";
1914
+ queueRealtimeForCurrentPhase();
1915
+ }
1916
+ },
1917
+ cooperativeCheckpoint: async () => {
1918
+ if (currentPhase === "collection" &&
1919
+ shareTesting.cooperativeRowVisits().plan.size > 0) {
1920
+ currentPhase = "planning";
1921
+ }
1922
+ if (currentPhase === "planning")
1923
+ queueRealtimeForCurrentPhase();
1924
+ return checkpoint();
1925
+ },
1926
+ });
1927
+ expect(result.filesSkipped).toBe(fileCount);
1928
+ expect(result.filesUploaded).toBe(0);
1929
+ }
1930
+ finally {
1931
+ fullPassFinished = true;
1932
+ }
1933
+ }, "chore");
1934
+ await Promise.all(realtimePasses);
1935
+ visits = shareTesting.cooperativeRowVisits();
1936
+ }
1937
+ finally {
1938
+ shareTesting.disableCooperativeRowVisits();
1939
+ }
1940
+ expect(realtimeRanBeforeFullPassFinished).toBe(true);
1941
+ expect(realtimePhaseRuns).toEqual(["collection", "planning", "execution"]);
1942
+ expect(planEvents).toHaveLength(1);
1943
+ expect(planEvents[0]).toMatchObject({
1944
+ type: "plan",
1945
+ filesToUpload: 0,
1946
+ filesToSkip: fileCount,
1947
+ filesToDelete: 0,
1948
+ });
1949
+ expect(visits.collect.size).toBe(fileCount);
1950
+ expect(visits.plan.size).toBe(fileCount);
1951
+ for (let index = 0; index < fileCount; index++) {
1952
+ const relativePath = `docs/resume/${index}.md`;
1953
+ expect(visits.collect.get(relativePath)).toBe(1);
1954
+ expect(visits.plan.get(relativePath)).toBe(1);
1955
+ }
1956
+ expect(uploadFile).not.toHaveBeenCalled();
1957
+ expect(Object.keys(readJournal("acme").files)).toHaveLength(fileCount);
1958
+ }, 120_000);
1678
1959
  it("defers a rate-limited upload as a diagnostic error and leaves the journal row unchanged", async () => {
1679
1960
  // Production (2026-09-18): vault 100/hr throttle on a per-file PUT was
1680
1961
  // emitted as a hard type:"error", so the company landed in errors[] and
@@ -1794,6 +2075,61 @@ describe("share", () => {
1794
2075
  vi.mocked(uploadFile).mockResolvedValue({ etag: '"upload-etag"' });
1795
2076
  }
1796
2077
  });
2078
+ it("defers a file that vanishes between upload and journal as a diagnostic error", async () => {
2079
+ // Production (2026-09-19 02:02:57Z, agent Alpha, company equi):
2080
+ // workers/thread-police/data/run.lock was PUT, then deleted by the
2081
+ // worker before updateEntry's lstat. PrematureJournalEntryError is
2082
+ // correct (nothing journaled), but the catch used to emit a hard
2083
+ // type:"error" so hq-sync-runner exited 2. Same diagnostic deferral
2084
+ // as a rate-limited PUT; the next pass re-plans the key.
2085
+ const companyRoot = path.join(tmpDir, "companies", "acme");
2086
+ fs.mkdirSync(companyRoot, { recursive: true });
2087
+ const relativePath = "workers/thread-police/data/run.lock";
2088
+ const testFile = path.join(companyRoot, relativePath);
2089
+ fs.mkdirSync(path.dirname(testFile), { recursive: true });
2090
+ fs.writeFileSync(testFile, "pid");
2091
+ const journalPath = path.join(stateDir, "sync-journal.acme.json");
2092
+ seedJournalAt(journalPath, {
2093
+ version: "1",
2094
+ lastSync: new Date(Date.now() - 60_000).toISOString(),
2095
+ files: {},
2096
+ });
2097
+ vi.mocked(uploadFile).mockReset();
2098
+ let putAttempted = false;
2099
+ vi.mocked(uploadFile).mockImplementationOnce(async (_ctx, localPath, key) => {
2100
+ expect(key).toBe(relativePath);
2101
+ putAttempted = true;
2102
+ fs.unlinkSync(localPath);
2103
+ return { etag: '"upload-etag"' };
2104
+ });
2105
+ try {
2106
+ const events = [];
2107
+ const result = await share({
2108
+ paths: [testFile],
2109
+ company: "acme",
2110
+ vaultConfig: mockConfig,
2111
+ hqRoot: tmpDir,
2112
+ onEvent: (e) => events.push(e),
2113
+ });
2114
+ expect(putAttempted).toBe(true);
2115
+ expect(result.filesUploaded).toBe(0);
2116
+ expect(result.filesSkipped).toBeGreaterThanOrEqual(1);
2117
+ expect(journalAt(journalPath).files[relativePath]).toBeUndefined();
2118
+ const errorEvents = events.filter((e) => e.type === "error");
2119
+ expect(errorEvents).toHaveLength(1);
2120
+ expect(errorEvents[0]).toMatchObject({
2121
+ type: "error",
2122
+ diagnostic: true,
2123
+ path: relativePath,
2124
+ message: expect.stringContaining("PrematureJournalEntryError"),
2125
+ });
2126
+ expect(events.some((e) => e.type === "error" && e.diagnostic !== true)).toBe(false);
2127
+ }
2128
+ finally {
2129
+ vi.mocked(uploadFile).mockReset();
2130
+ vi.mocked(uploadFile).mockResolvedValue({ etag: '"upload-etag"' });
2131
+ }
2132
+ });
1797
2133
  it("defers a breaker-tripped upload batch as one diagnostic company error", async () => {
1798
2134
  // Production (2026-09-18 16:03Z brickcraft, 16:05Z mhi-media): the
1799
2135
  // RateLimitBreaker tripped, remaining GET presigns all threw
@@ -2085,7 +2421,7 @@ describe("share", () => {
2085
2421
  });
2086
2422
  expect(result.filesUploaded).toBe(2);
2087
2423
  });
2088
- it("walks a very large directory without using its entries as call arguments", () => {
2424
+ it("walks a very large directory without using its entries as call arguments", async () => {
2089
2425
  const companyRoot = path.join(tmpDir, "companies", "acme");
2090
2426
  fs.mkdirSync(companyRoot, { recursive: true });
2091
2427
  const nestedDir = path.join(companyRoot, "nested");
@@ -2112,8 +2448,15 @@ describe("share", () => {
2112
2448
  .spyOn(fs, "readdirSync")
2113
2449
  .mockImplementation(((dir) => path.resolve(String(dir)) === nestedDir ? nestedEntries : rootEntries));
2114
2450
  let heartbeatCalls = 0;
2451
+ let eventLoopTicked = false;
2452
+ const eventLoopTick = new Promise((resolve) => {
2453
+ setImmediate(() => {
2454
+ eventLoopTicked = true;
2455
+ resolve();
2456
+ });
2457
+ });
2115
2458
  try {
2116
- const collected = shareTesting.collectFiles([companyRoot], tmpDir, companyRoot, () => true, { onHeartbeat: () => heartbeatCalls++ });
2459
+ const collected = await shareTesting.collectFiles([companyRoot], tmpDir, companyRoot, () => true, { onHeartbeat: () => heartbeatCalls++ });
2117
2460
  expect(collected).toHaveLength(entryCount);
2118
2461
  expect(collected[0]).toMatchObject({ relativePath: "before.md" });
2119
2462
  expect(collected[1]).toMatchObject({ relativePath: "nested/child.md" });
@@ -2122,6 +2465,8 @@ describe("share", () => {
2122
2465
  relativePath: `entry-${entryCount - 3}.md`,
2123
2466
  });
2124
2467
  expect(heartbeatCalls).toBeGreaterThan(entryCount);
2468
+ expect(eventLoopTicked).toBe(true);
2469
+ await eventLoopTick;
2125
2470
  }
2126
2471
  finally {
2127
2472
  readdirSpy.mockRestore();
@@ -7851,7 +8196,7 @@ describe("share", () => {
7851
8196
  expect(uploadFile).toHaveBeenCalledWith(expect.anything(), realPolicy, "policies/real.md", undefined, expect.anything());
7852
8197
  expect(uploadSymlink).toHaveBeenCalledWith(expect.anything(), "real.md", "policies/link.md", undefined, expect.anything());
7853
8198
  });
7854
- it("skips and reports a top-level link whose win32 readlink raises EINVAL", () => {
8199
+ it("skips and reports a top-level link whose win32 readlink raises EINVAL", async () => {
7855
8200
  const companyRoot = path.join(tmpDir, "companies", "acme");
7856
8201
  const targetDir = path.join(tmpDir, "outside", "target");
7857
8202
  fs.mkdirSync(targetDir, { recursive: true });
@@ -7870,7 +8215,7 @@ describe("share", () => {
7870
8215
  const warnings = vi.spyOn(console, "error").mockImplementation(() => { });
7871
8216
  const unreachable = [];
7872
8217
  try {
7873
- const collected = shareTesting.collectFiles([unreadableLink], tmpDir, companyRoot, () => true, {
8218
+ const collected = await shareTesting.collectFiles([unreadableLink], tmpDir, companyRoot, () => true, {
7874
8219
  onUnreachablePath: (namedPath, reason) => unreachable.push([namedPath, reason]),
7875
8220
  });
7876
8221
  expect(collected).toEqual([]);