diff --git a/src/main/runtime/rpc/worktree-catalog-snapshot.test.ts b/src/main/runtime/rpc/worktree-catalog-snapshot.test.ts index 70178aa48db..4f52319e523 100644 --- a/src/main/runtime/rpc/worktree-catalog-snapshot.test.ts +++ b/src/main/runtime/rpc/worktree-catalog-snapshot.test.ts @@ -63,6 +63,18 @@ describe('resolveWorktreeCatalogSnapshot', () => { expect(resolveWorktreeCatalogSnapshot(result(1), null).snapshotId).toBe(warm) }) + it('isolates the memo from mutation of a previously resolved catalog', () => { + const mutable = result(1) + const first = resolveWorktreeCatalogSnapshot(mutable, null) + + mutable.totalCount = 2 + const changed = resolveWorktreeCatalogSnapshot(mutable, first.snapshotId) + + expect(changed).toMatchObject({ totalCount: 2 }) + expect(changed).not.toHaveProperty('unchanged') + expect(changed.snapshotId).not.toBe(first.snapshotId) + }) + it('produces ids within the request schema bound', () => { const { snapshotId } = resolveWorktreeCatalogSnapshot(result(1), null) expect(snapshotId.length).toBeGreaterThan(0) diff --git a/src/main/runtime/rpc/worktree-catalog-snapshot.ts b/src/main/runtime/rpc/worktree-catalog-snapshot.ts index 9514266a963..ca5fc944879 100644 --- a/src/main/runtime/rpc/worktree-catalog-snapshot.ts +++ b/src/main/runtime/rpc/worktree-catalog-snapshot.ts @@ -24,11 +24,13 @@ function worktreeCatalogSnapshotId(result: RuntimeWorktreePsResult): string { if (memoizedId && isDeepStrictEqual(memoizedId.result, result)) { return memoizedId.snapshotId } + const serialized = JSON.stringify(result) const snapshotId = createHash('sha256') - .update(JSON.stringify(result)) + .update(serialized) .digest('base64url') .slice(0, SNAPSHOT_ID_LENGTH) - memoizedId = { result, snapshotId } + // Why: caller-owned mutation must not let an old id label new catalog content. + memoizedId = { result: JSON.parse(serialized) as RuntimeWorktreePsResult, snapshotId } return snapshotId }