@indigoai-us/hq-cloud 6.15.38 → 6.15.40
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.
- package/dist/bin/sync-runner-company.d.ts.map +1 -1
- package/dist/bin/sync-runner-company.js +8 -0
- package/dist/bin/sync-runner-company.js.map +1 -1
- package/dist/bin/sync-runner.d.ts +5 -0
- package/dist/bin/sync-runner.d.ts.map +1 -1
- package/dist/bin/sync-runner.js.map +1 -1
- package/dist/bin/sync-runner.test.js +24 -0
- package/dist/bin/sync-runner.test.js.map +1 -1
- package/dist/cli/share.d.ts +3 -3
- package/dist/cli/share.d.ts.map +1 -1
- package/dist/cli/share.js +45 -2
- package/dist/cli/share.js.map +1 -1
- package/dist/cli/share.test.js +143 -6
- package/dist/cli/share.test.js.map +1 -1
- package/dist/cli/sync.d.ts +10 -0
- package/dist/cli/sync.d.ts.map +1 -1
- package/dist/cli/sync.js +5 -1
- package/dist/cli/sync.js.map +1 -1
- package/dist/cli/sync.test.js +15 -0
- package/dist/cli/sync.test.js.map +1 -1
- package/dist/ignore.d.ts +28 -1
- package/dist/ignore.d.ts.map +1 -1
- package/dist/ignore.js +91 -28
- package/dist/ignore.js.map +1 -1
- package/dist/ignore.test.js +84 -1
- package/dist/ignore.test.js.map +1 -1
- package/dist/personal-vault.d.ts +13 -4
- package/dist/personal-vault.d.ts.map +1 -1
- package/dist/personal-vault.js +26 -4
- package/dist/personal-vault.js.map +1 -1
- package/dist/personal-vault.test.js +22 -0
- package/dist/personal-vault.test.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/share.test.js
CHANGED
|
@@ -1303,9 +1303,9 @@ describe("share", () => {
|
|
|
1303
1303
|
});
|
|
1304
1304
|
expect(result).toMatchObject({ filesUploaded: 0, filesSkipped: 1 });
|
|
1305
1305
|
expect(events).toContainEqual({
|
|
1306
|
-
type: "push-refused-server-owned",
|
|
1307
|
-
|
|
1308
|
-
|
|
1306
|
+
type: "push-refused-server-owned-summary",
|
|
1307
|
+
count: 1,
|
|
1308
|
+
samplePaths: ["sources/meetings/m-2.md"],
|
|
1309
1309
|
});
|
|
1310
1310
|
expect(events.some((event) => event.type === "reconciled")).toBe(false);
|
|
1311
1311
|
expect(uploadFile).not.toHaveBeenCalled();
|
|
@@ -1376,12 +1376,42 @@ describe("share", () => {
|
|
|
1376
1376
|
expect(result.filesSkipped).toBeGreaterThanOrEqual(1);
|
|
1377
1377
|
expect(result.conflictPaths).toEqual([]);
|
|
1378
1378
|
expect(events).toContainEqual({
|
|
1379
|
-
type: "push-refused-server-owned",
|
|
1380
|
-
|
|
1381
|
-
|
|
1379
|
+
type: "push-refused-server-owned-summary",
|
|
1380
|
+
count: 1,
|
|
1381
|
+
samplePaths: ["ontology/.last-run"],
|
|
1382
1382
|
});
|
|
1383
1383
|
expect(events.some((event) => event.type === "reconciled")).toBe(false);
|
|
1384
1384
|
});
|
|
1385
|
+
it("watch push collapses 32k server-owned refusals to one exact-count event while board.json still uploads", async () => {
|
|
1386
|
+
const companyRoot = path.join(tmpDir, "companies", "acme");
|
|
1387
|
+
const sourceRoot = path.join(companyRoot, "sources", "meetings");
|
|
1388
|
+
const serverOwnedPathCount = 32_540;
|
|
1389
|
+
fs.mkdirSync(sourceRoot, { recursive: true });
|
|
1390
|
+
for (let index = 0; index < serverOwnedPathCount; index++) {
|
|
1391
|
+
fs.writeFileSync(path.join(sourceRoot, `meeting-${index}.md`), "source");
|
|
1392
|
+
}
|
|
1393
|
+
fs.writeFileSync(path.join(companyRoot, "board.json"), "{\"cards\":[]}");
|
|
1394
|
+
const events = [];
|
|
1395
|
+
const result = await share({
|
|
1396
|
+
paths: [companyRoot],
|
|
1397
|
+
company: "acme",
|
|
1398
|
+
vaultConfig: mockConfig,
|
|
1399
|
+
hqRoot: tmpDir,
|
|
1400
|
+
serverOwnedPathPolicy: "skip",
|
|
1401
|
+
onEvent: (event) => events.push(event),
|
|
1402
|
+
});
|
|
1403
|
+
const summaries = events.filter((event) => event.type === "push-refused-server-owned-summary");
|
|
1404
|
+
expect(summaries).toHaveLength(1);
|
|
1405
|
+
expect(summaries[0]).toMatchObject({ count: serverOwnedPathCount });
|
|
1406
|
+
expect(summaries[0].samplePaths).toHaveLength(10);
|
|
1407
|
+
expect(summaries[0].samplePaths.every((sample) => sample.startsWith("sources/meetings/"))).toBe(true);
|
|
1408
|
+
expect(events.filter((event) => event.type === "push-refused-server-owned")).toHaveLength(0);
|
|
1409
|
+
expect(result).toMatchObject({
|
|
1410
|
+
filesUploaded: 1,
|
|
1411
|
+
filesSkipped: serverOwnedPathCount,
|
|
1412
|
+
});
|
|
1413
|
+
expect(vi.mocked(uploadFile).mock.calls.map((call) => call[2])).toEqual(["board.json"]);
|
|
1414
|
+
});
|
|
1385
1415
|
it("first-time-upload-with-cloud-collision: emits conflict + writes mirror under --on-conflict keep (Bug #7)", async () => {
|
|
1386
1416
|
// Bug #7 (data-loss class) from the 5.33.0 deep test: when a file has
|
|
1387
1417
|
// NO prior journal entry (fresh upload from this machine) but the
|
|
@@ -2950,6 +2980,7 @@ describe("share", () => {
|
|
|
2950
2980
|
e.type === "scope-materialization-gap" ||
|
|
2951
2981
|
e.type === "ignore-excluded" ||
|
|
2952
2982
|
e.type === "not-shipped" ||
|
|
2983
|
+
e.type === "push-refused-server-owned-summary" ||
|
|
2953
2984
|
e.type === "delete-refused-bulk-asymmetry")
|
|
2954
2985
|
return;
|
|
2955
2986
|
events.push({
|
|
@@ -4930,6 +4961,94 @@ describe("share", () => {
|
|
|
4930
4961
|
delete process.env.HQ_STATE_DIR;
|
|
4931
4962
|
}
|
|
4932
4963
|
});
|
|
4964
|
+
it("journal prune is local-only while an in-scope missing entry stays on the normal delete path", async () => {
|
|
4965
|
+
const journalSlug = "personal-prune-safety";
|
|
4966
|
+
const journalPath = path.join(stateDir, `sync-journal.${journalSlug}.json`);
|
|
4967
|
+
seedJournalAt(journalPath, {
|
|
4968
|
+
version: "2",
|
|
4969
|
+
lastSync: new Date().toISOString(),
|
|
4970
|
+
pulls: [],
|
|
4971
|
+
files: {
|
|
4972
|
+
// Absent and currently ignored: this is positively out of scope and
|
|
4973
|
+
// may be forgotten without expressing a remote deletion intent.
|
|
4974
|
+
".claude/worktrees/gone/stranded.ts": {
|
|
4975
|
+
hash: "stranded", size: 1, syncedAt: new Date().toISOString(), direction: "up",
|
|
4976
|
+
},
|
|
4977
|
+
// Absent but still in scope: this remains a genuine local-delete
|
|
4978
|
+
// candidate and must be handled by the ordinary delete planner.
|
|
4979
|
+
"personal/pending-delete.md": {
|
|
4980
|
+
hash: "pending",
|
|
4981
|
+
size: 1,
|
|
4982
|
+
syncedAt: new Date().toISOString(),
|
|
4983
|
+
direction: "up",
|
|
4984
|
+
remoteEtag: "pending-etag",
|
|
4985
|
+
kind: "file",
|
|
4986
|
+
localDeleteIntent: deleteIntent("pending-etag", "pending"),
|
|
4987
|
+
},
|
|
4988
|
+
},
|
|
4989
|
+
});
|
|
4990
|
+
const personalCtx = makeEntityContext({
|
|
4991
|
+
uid: "prs_01HASSAANTESTUSER",
|
|
4992
|
+
slug: journalSlug,
|
|
4993
|
+
bucketName: "hq-vault-prs-01HASSAANTESTUSER",
|
|
4994
|
+
});
|
|
4995
|
+
const result = await share({
|
|
4996
|
+
paths: [],
|
|
4997
|
+
deleteScopeRoots: ["personal/pending-delete.md"],
|
|
4998
|
+
entityContext: personalCtx,
|
|
4999
|
+
hqRoot: tmpDir,
|
|
5000
|
+
personalMode: true,
|
|
5001
|
+
journalSlug,
|
|
5002
|
+
skipUnchanged: true,
|
|
5003
|
+
propagateDeletes: true,
|
|
5004
|
+
propagateDeletePolicy: "owned-only",
|
|
5005
|
+
});
|
|
5006
|
+
// The in-scope deletion remains eligible and produces the existing normal
|
|
5007
|
+
// remote delete behavior; the prune itself never contributes a mutation.
|
|
5008
|
+
expect(result.filesDeleted).toBe(1);
|
|
5009
|
+
expect(deleteRemoteFile).toHaveBeenCalledTimes(1);
|
|
5010
|
+
expect(deleteRemoteFile).toHaveBeenCalledWith(expect.anything(), "personal/pending-delete.md");
|
|
5011
|
+
expect(uploadFile).not.toHaveBeenCalled();
|
|
5012
|
+
expect(uploadSymlink).not.toHaveBeenCalled();
|
|
5013
|
+
expect(journalAt(journalPath).files[".claude/worktrees/gone/stranded.ts"]).toBeUndefined();
|
|
5014
|
+
expect(journalAt(journalPath).files["personal/pending-delete.md"]).toBeUndefined();
|
|
5015
|
+
});
|
|
5016
|
+
it("journal prune of an ignored missing row issues zero push-side mutations", async () => {
|
|
5017
|
+
const journalSlug = "personal-prune-local-only";
|
|
5018
|
+
const journalPath = path.join(stateDir, `sync-journal.${journalSlug}.json`);
|
|
5019
|
+
seedJournalAt(journalPath, {
|
|
5020
|
+
version: "2",
|
|
5021
|
+
lastSync: new Date().toISOString(),
|
|
5022
|
+
pulls: [],
|
|
5023
|
+
files: {
|
|
5024
|
+
".claude/worktrees/gone/stranded.ts": {
|
|
5025
|
+
hash: "stranded", size: 1, syncedAt: new Date().toISOString(), direction: "up",
|
|
5026
|
+
},
|
|
5027
|
+
},
|
|
5028
|
+
});
|
|
5029
|
+
const personalCtx = makeEntityContext({
|
|
5030
|
+
uid: "prs_01HASSAANTESTUSER",
|
|
5031
|
+
slug: journalSlug,
|
|
5032
|
+
bucketName: "hq-vault-prs-01HASSAANTESTUSER",
|
|
5033
|
+
});
|
|
5034
|
+
const result = await share({
|
|
5035
|
+
paths: [],
|
|
5036
|
+
entityContext: personalCtx,
|
|
5037
|
+
hqRoot: tmpDir,
|
|
5038
|
+
personalMode: true,
|
|
5039
|
+
journalSlug,
|
|
5040
|
+
skipUnchanged: true,
|
|
5041
|
+
propagateDeletes: true,
|
|
5042
|
+
propagateDeletePolicy: "owned-only",
|
|
5043
|
+
});
|
|
5044
|
+
expect(result.filesUploaded).toBe(0);
|
|
5045
|
+
expect(result.filesDeleted).toBe(0);
|
|
5046
|
+
expect(result.filesTombstoned).toBe(0);
|
|
5047
|
+
expect(uploadFile).not.toHaveBeenCalled();
|
|
5048
|
+
expect(uploadSymlink).not.toHaveBeenCalled();
|
|
5049
|
+
expect(deleteRemoteFile).not.toHaveBeenCalled();
|
|
5050
|
+
expect(journalAt(journalPath).files[".claude/worktrees/gone/stranded.ts"]).toBeUndefined();
|
|
5051
|
+
});
|
|
4933
5052
|
// ── personalMode ───────────────────────────────────────────────────────────
|
|
4934
5053
|
//
|
|
4935
5054
|
// The personal vault (slug "personal" in the runner's fanout plan) shares
|
|
@@ -7657,5 +7776,23 @@ describe("defaultConsoleLogger — delete refusal wording per reason", () => {
|
|
|
7657
7776
|
expect(out).toContain("aaa111");
|
|
7658
7777
|
expect(out).toContain("bbb222");
|
|
7659
7778
|
});
|
|
7779
|
+
it("logs personal journal prune reason classes", () => {
|
|
7780
|
+
const lines = [];
|
|
7781
|
+
const spy = vi.spyOn(console, "log").mockImplementation((msg) => {
|
|
7782
|
+
lines.push(String(msg));
|
|
7783
|
+
});
|
|
7784
|
+
try {
|
|
7785
|
+
shareTesting.defaultConsoleLogger({
|
|
7786
|
+
type: "personal-vault-journal-pruned",
|
|
7787
|
+
count: 3,
|
|
7788
|
+
samplePaths: ["workspace/scratch/a.md"],
|
|
7789
|
+
byReason: { "ignore-filtered": 2, "excluded-top-level": 1 },
|
|
7790
|
+
});
|
|
7791
|
+
}
|
|
7792
|
+
finally {
|
|
7793
|
+
spy.mockRestore();
|
|
7794
|
+
}
|
|
7795
|
+
expect(lines.join("\n")).toContain("ignore-filtered=2, excluded-top-level=1");
|
|
7796
|
+
});
|
|
7660
7797
|
});
|
|
7661
7798
|
//# sourceMappingURL=share.test.js.map
|