Skip to content

fix(sync): stop rewriting keep.lock on every sync#227

Merged
cvince merged 1 commit into
mainfrom
fix/keep-lock-canonical-self-heal
Jun 6, 2026
Merged

fix(sync): stop rewriting keep.lock on every sync#227
cvince merged 1 commit into
mainfrom
fix/keep-lock-canonical-self-heal

Conversation

@cvince

@cvince cvince commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Problem

Running bare capy (sync) rewrites keep.lock on every run — even when .env is empty and nothing has changed. This produces a spurious git diff after every sync.

Root cause

During sync, the self-heal step compares the local keep.lock against the server's keep_file and overwrites local on any difference:

const localSerialized = currentKeep ? JSON.stringify(currentKeep) : '';
const serverSerialized = JSON.stringify(serverKeep);
if (localSerialized !== serverSerialized) { /* rewrite keep.lock */ }

But the two sides are never in the same shape:

  • Local is read off disk via readKeepFile() (raw as KeepFile), so it still carries the injected _comment field and is already key-sorted.
  • Server keep_file has neither _comment nor guaranteed ordering.

So JSON.stringify(local) !== JSON.stringify(server) is always true → writeKeepFile() fires on every sync, independent of .env contents.

Fix

  • Extract the canonical lockfile serialization into an exported serializeKeep() — the single source of truth for the on-disk form (_comment, fixed key order, sorted variables, stable per-entry field order). The self-heal comparison now normalizes both sides through it, so it only rewrites on a real change.
  • Give writeKeepFile() a no-op guard: if the on-disk file is already byte-identical to the canonical output, skip the write entirely. Defense-in-depth that also stops churn from other writeKeepFile callers.

Tests

tests/files/fileManager.test.ts:

  • serializeKeep canonical comparison (sync self-heal) — asserts an on-disk keep (with _comment) and a server keep (without) serialize identically (the fix), and documents that the old bare-JSON.stringify comparison saw them as different (the bug). Plus order/extra-field independence and a positive test that a real value_hash change is still detected.
  • writeKeepFile no-op guard — skips the write when on-disk content already matches; rewrites when it differs.

All affected suites pass individually (fileManager, capyCommand, branchKeepFile); tsc --noEmit clean.

…heal compare

Bare `capy` sync rewrote keep.lock on every run — even with an empty
.env. The self-heal step compares the local keep.lock against the
server's keep_file and overwrites on any difference. But the local copy
is read off disk (so it carries the injected `_comment` and is already
sorted) while the server's copy has neither, so the bare JSON.stringify
comparison ALWAYS reported a difference and rewrote the file.

- Extract the canonical lockfile serialization into `serializeKeep()`
  (the single source of truth for on-disk form) and have the self-heal
  comparison normalize both sides through it.
- Give `writeKeepFile()` a no-op guard so it skips the write when the
  on-disk file is already byte-identical to the canonical output.
- Add regression tests covering the canonical comparison and the no-op
  guard.
@cvince cvince merged commit 87d519f into main Jun 6, 2026
1 check passed
@cvince cvince mentioned this pull request Jun 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant