Skip to content

fix(distill): only prune catalog entries whose origin project is visible here - #8

Open
JeePeeTee wants to merge 1 commit into
MarimerLLC:mainfrom
JeePeeTee:fix/distill-prune-scope
Open

fix(distill): only prune catalog entries whose origin project is visible here#8
JeePeeTee wants to merge 1 commit into
MarimerLLC:mainfrom
JeePeeTee:fix/distill-prune-scope

Conversation

@JeePeeTee

Copy link
Copy Markdown

Problem

Reconcile guards against pruning blind by checking that ProjectsDir exists — but ~/.claude/projects exists on every machine running Claude Code, so that check says nothing about any individual project.

Each entry then falls through to os.ReadFile on <ProjectsDir>/<originProject>/memory/<originFile>. On a workstation that has never opened that project the path is legitimately absent, ErrNotExist is read as "the user deleted it", and the entry file is removed.

Because the catalog lives inside the sync work-tree, the daemon's git add -A then propagates that deletion everywhere. Running claude-memsync distill --prune on a second workstation silently destroys entries distilled on the first.

Solution

Skip entries whose origin project directory is not present, and count them. claude-memsync distill reports the count, so a conservative prune reads as a deliberate decision rather than the command appearing to do nothing:

kept 3 entries whose origin project isn't on this machine (can't judge from here)

The cost is that an entry whose project directory the user really did delete now stays until removed by hand. That asymmetry is deliberate, and noted in the code comment: a missed prune is an annoyance, a wrong prune is data loss across every machine.

Verification

go build ./... && go vet ./... && gofmt -l . && go test ./... all clean.

The existing TestReconcilePrunesStaleEntries passes unchanged — a source deleted under a project that is present is still pruned, so this is selective rather than a blanket disable. A new test covers prune and skip happening in the same run; reverting the fix makes it fail with Pruned = 2, want 1 and the entry file gone.

Docs updated to explain the scoping, including the troubleshooting entry that previously told you to run --prune without saying it must be the machine owning the source project.

…ble here

Reconcile guards against pruning blind by checking that ProjectsDir exists, but
~/.claude/projects exists on every machine running Claude Code, so that check
says nothing about any individual project. Each entry then falls through to
os.ReadFile on <ProjectsDir>/<originProject>/memory/<originFile>; on a
workstation that has never opened that project the path is legitimately absent,
ErrNotExist is read as "the user deleted it", and the entry file is removed.

Because the catalog lives inside the sync work-tree, the daemon's `git add -A`
then propagates that deletion to every other machine -- so running
`distill --prune` on a second workstation silently destroys entries distilled
on the first.

Skip entries whose origin project directory is not present, and count them.
`claude-memsync distill` reports the count, so a conservative prune reads as a
deliberate decision rather than as the command doing nothing.

The cost is that an entry whose project directory the user really did delete
now stays until removed by hand. That asymmetry is deliberate: a missed prune
is an annoyance, a wrong prune is data loss across every machine.

The existing TestReconcilePrunesStaleEntries still passes unchanged -- a source
deleted under a project that *is* present is still pruned -- and a new test
covers prune and skip happening in the same run, so the fix is selective rather
than a blanket disable.
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