diff --git a/src/serviceFeatures/startupLifecycle/incompleteDocuments.ts b/src/serviceFeatures/startupLifecycle/incompleteDocuments.ts index 0b4ee3de..42d4ee47 100644 --- a/src/serviceFeatures/startupLifecycle/incompleteDocuments.ts +++ b/src/serviceFeatures/startupLifecycle/incompleteDocuments.ts @@ -117,6 +117,10 @@ export async function checkIncompleteDocuments( continue; } sizeOnStorage = storageFileContent.byteLength; + // The file may have changed since stat, so recheck using the bytes read. + if (recordedSize === actualSize && sizeOnStorage === actualSize && !isConflicted) { + continue; + } const contentMatched = await isDocContentSame(doc.data, storageFileContent); errorFiles.push({ path, diff --git a/src/serviceFeatures/startupLifecycle/incompleteDocuments.unit.spec.ts b/src/serviceFeatures/startupLifecycle/incompleteDocuments.unit.spec.ts index 2f30f12b..c67eddbc 100644 --- a/src/serviceFeatures/startupLifecycle/incompleteDocuments.unit.spec.ts +++ b/src/serviceFeatures/startupLifecycle/incompleteDocuments.unit.spec.ts @@ -182,6 +182,39 @@ describe("checkIncompleteDocuments", () => { expect(fixture.dependencies.keyValueDB.set).not.toHaveBeenCalled(); }); + it("does not offer repair when all sizes agree after reading storage", async () => { + const healthy = documentFixture("settled.md", { storageContent: "abc" }); + const fixture = createDependencies(documentsFrom([healthy]), [healthy]); + vi.mocked(fixture.dependencies.storageAccess.statHidden).mockResolvedValueOnce({ ...healthy.stub.stat, size: 4 }); + + await expect(checkIncompleteDocuments(fixture.dependencies)).resolves.toBe(true); + + expect(fixture.readHiddenFileBinary).toHaveBeenCalledWith("settled.md"); + expect(fixture.askSelectStringDialogue).not.toHaveBeenCalled(); + expect(fixture.storeFileToDB).not.toHaveBeenCalled(); + expect(fixture.dependencies.keyValueDB.set).toHaveBeenCalledWith("checkIncompleteDocs", true); + }); + + it("uses the bytes read from storage when a size mismatch remains", async () => { + const mismatched = documentFixture("changing.md", { recordedSize: 3, storageContent: "abcde" }); + const fixture = createDependencies(documentsFrom([mismatched]), [mismatched]); + vi.mocked(fixture.dependencies.storageAccess.statHidden).mockResolvedValueOnce({ + ...mismatched.stub.stat, + size: 4, + }); + fixture.askSelectStringDialogue.mockImplementation(selectButton(0)); + + await expect(checkIncompleteDocuments(fixture.dependencies)).resolves.toBe(true); + + expect(fixture.readHiddenFileBinary).toHaveBeenCalledWith("changing.md"); + expect(fixture.askSelectStringDialogue).toHaveBeenCalledOnce(); + expect(fixture.askSelectStringDialogue.mock.calls[0]?.[0]).toEqual( + expect.stringContaining("changing.md (M: 3, A: 3, S: 5)") + ); + expect(fixture.storeFileToDB).not.toHaveBeenCalled(); + expect(fixture.dependencies.keyValueDB.set).not.toHaveBeenCalled(); + }); + it("still reads conflicted storage for the existing repair dialogue", async () => { const conflicted = documentFixture("conflicted.md", { storageContent: "abc", conflicts: ["2-other"] }); const fixture = createDependencies(documentsFrom([conflicted]), [conflicted]); diff --git a/updates.md b/updates.md index ca2e3497..7e31a95e 100644 --- a/updates.md +++ b/updates.md @@ -12,6 +12,10 @@ Earlier releases remain available in the 1.0 release history, the 1.0 preview hi ## Unreleased +### Synchronisation + +- We can run startup integrity checks with lower memory use for healthy Vault files. Files that change between the size check and content reading no longer trigger an unnecessary repair warning when their actual sizes agree and there are no conflicts. (#1283) + ## 1.0.39 20th October, 2026