@indigoai-us/hq-cloud 6.15.26 → 6.15.28

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.
@@ -530,6 +530,60 @@ describe("sync", () => {
530
530
  // …and the local one survives beside it.
531
531
  expect(fs.readFileSync(sidecarIn(companyDocs), "utf-8")).toBe("local version");
532
532
  });
533
+ it("rechecks the current local body when executing a planned conflict", async () => {
534
+ const companyDocs = path.join(tmpDir, "companies", "acme", "docs");
535
+ fs.mkdirSync(companyDocs, { recursive: true });
536
+ const localPath = path.join(companyDocs, "handoff.md");
537
+ fs.writeFileSync(localPath, "local body seen by the planner");
538
+ fs.writeFileSync(journalPath, JSON.stringify({
539
+ version: "1",
540
+ lastSync: new Date().toISOString(),
541
+ files: {
542
+ "docs/handoff.md": {
543
+ hash: "common-base-hash",
544
+ size: 16,
545
+ syncedAt: new Date(Date.now() - 3600000).toISOString(),
546
+ direction: "down",
547
+ remoteEtag: "common-base-etag",
548
+ },
549
+ },
550
+ }));
551
+ vi.mocked(s3Module.listRemoteFiles).mockResolvedValueOnce([
552
+ {
553
+ key: "docs/handoff.md",
554
+ size: "mock file content".length,
555
+ lastModified: new Date(),
556
+ etag: '"new-remote-etag"',
557
+ },
558
+ ]);
559
+ let changedAfterPlan = false;
560
+ const result = await sync({
561
+ company: "acme",
562
+ onConflict: "keep",
563
+ vaultConfig: mockConfig,
564
+ hqRoot: tmpDir,
565
+ onEvent: (event) => {
566
+ if (event.type === "plan" && !changedAfterPlan) {
567
+ changedAfterPlan = true;
568
+ // The local writer converged this path with the cloud after the
569
+ // whole-run preview classified it, but before its action executes.
570
+ fs.writeFileSync(localPath, "mock file content");
571
+ }
572
+ },
573
+ });
574
+ expect(changedAfterPlan).toBe(true);
575
+ expect(result.conflicts).toBe(0);
576
+ expect(result.conflictPaths).toEqual([]);
577
+ expect(fs.readdirSync(companyDocs).filter((name) => name.includes(".conflict-")))
578
+ .toEqual([]);
579
+ const entry = journalAt(journalPath).files["docs/handoff.md"];
580
+ expect(entry).toMatchObject({
581
+ hash: crypto.createHash("sha256").update("mock file content").digest("hex"),
582
+ remoteEtag: "new-remote-etag",
583
+ direction: "down",
584
+ });
585
+ expect(entry.localDiverges).toBeFalsy();
586
+ });
533
587
  it("conflict skip marks the entry localDiverges (journal-honesty: the held copy never matched the recorded remote etag)", async () => {
534
588
  const companyDocs = path.join(tmpDir, "companies", "acme", "docs");
535
589
  fs.mkdirSync(companyDocs, { recursive: true });
@@ -3351,6 +3405,42 @@ describe("sync", () => {
3351
3405
  expect(journal.files["docs/handoff.md"].remoteEtag).toBe("abc123");
3352
3406
  expect(journal.files["knowledge/readme.md"].remoteEtag).toBe("def456");
3353
3407
  });
3408
+ it("persists each downloaded hash before starting the next file", async () => {
3409
+ process.env.HQ_SYNC_TRANSFER_CONCURRENCY = "1";
3410
+ const remoteFiles = [
3411
+ { key: "ordered/first.md", size: 18, lastModified: new Date(), etag: '"first-etag"' },
3412
+ { key: "ordered/second.md", size: 18, lastModified: new Date(), etag: '"second-etag"' },
3413
+ ];
3414
+ const defaultDownload = vi.mocked(s3Module.downloadFile).getMockImplementation();
3415
+ if (!defaultDownload)
3416
+ throw new Error("missing default download mock");
3417
+ try {
3418
+ vi.mocked(s3Module.listRemoteFiles).mockResolvedValueOnce(remoteFiles);
3419
+ vi.mocked(s3Module.downloadFile).mockImplementation(async (ctx, key, localPath, options) => {
3420
+ if (key === "ordered/second.md") {
3421
+ const first = journalAt(journalPath).files["ordered/first.md"];
3422
+ expect(first).toMatchObject({
3423
+ hash: crypto.createHash("sha256").update("mock file content").digest("hex"),
3424
+ remoteEtag: "first-etag",
3425
+ direction: "down",
3426
+ });
3427
+ expect(first.localDiverges).toBeFalsy();
3428
+ }
3429
+ return defaultDownload(ctx, key, localPath, options);
3430
+ });
3431
+ await sync({
3432
+ company: "acme",
3433
+ vaultConfig: mockConfig,
3434
+ hqRoot: tmpDir,
3435
+ skipReindex: true,
3436
+ onEvent: () => undefined,
3437
+ });
3438
+ }
3439
+ finally {
3440
+ vi.mocked(s3Module.downloadFile).mockImplementation(defaultDownload);
3441
+ delete process.env.HQ_SYNC_TRANSFER_CONCURRENCY;
3442
+ }
3443
+ });
3354
3444
  it("checkpoints completed downloads so a failed bulk pass resumes only its tail", async () => {
3355
3445
  process.env.HQ_SYNC_TRANSFER_CONCURRENCY = "1";
3356
3446
  const remoteFiles = Array.from({ length: 9 }, (_, index) => ({