Skip to content

fix(mirror): upsert by stable slug + bump 0.6.0rc16 (P0)#122

Merged
cipher813 merged 1 commit into
mainfrom
fix/mirror-upsert-by-slug-rc16
May 15, 2026
Merged

fix(mirror): upsert by stable slug + bump 0.6.0rc16 (P0)#122
cipher813 merged 1 commit into
mainfrom
fix/mirror-upsert-by-slug-rc16

Conversation

@cipher813
Copy link
Copy Markdown
Owner

P0 — auto-mirror re-inserts on every edit (surfaced 2026-05-15)

A Claude Code auto-memory file's normal lifecycle is draft → refine → finalize-on-merge — often several edits within one session. The mnemon-mirror save path wrote a brand-new mnemon document on each edit instead of upserting by the file's slug, so a single intentional memory edited 3× became 3 near-identical docs (concrete: reference-morning-signal-iam-decoupled mirrored 3× on 2026-05-15 — ids 2253/2256/2259). The at-save vector-overlap dedup can't catch it: successive edits diverge enough to clear the threshold while still being the same memory by identity.

Fix — source_key (stable caller-owned identity)

  • store.save() upsert-by-key — optional source_key. At most one live (non-invalidated) doc per (collection, source_client, source_key): unchanged re-save is idempotent; changed re-save invalidates the prior live row(s) and inserts a fresh one with an auditable invalidated_by supersession chain. Generic content-hash dedup now scoped to invalidated_at IS NULL so an A→B→A revert surfaces a fresh visible memory rather than resurrecting a dead row's access_count.
  • Migration — additive documents.source_key column + lookup index, applied in place on existing vaults. Pre-existing rows stay NULL = old insert-only behaviour (no-op for everything predating this).
  • memory_save tool (shared by server_remote) threads source_key.
  • mirror.py keys source_key to the file's frontmatter name (slug).
  • path uuid salt — the upsert path can re-insert identical content within the same millisecond (in-session A→B→A revert), which collided with UNIQUE(collection, path) under the old time-ms + hash-prefix scheme.

Tests

+5 store regression tests (changed-reedit supersession, idempotent unchanged re-save, distinct-slug isolation, A→B→A revert, no-source_key insert-only preservation) + mirror payload assertion. Full suite 750 passed.

Release

Version bumped 0.6.0rc150.6.0rc16 for soak. CHANGELOG updated.

Operator follow-up (not in this PR)

One-shot memory_forget sweep of pre-fix same-slug pile-ups in the live default vault (source_client='mnemon-mirror' grouped by title, keep max(created_at)).

🤖 Generated with Claude Code

…bump rc16

P0: the mnemon-mirror save path wrote a brand-new document on every
local-file edit, so one intentional memory edited several times in a
session became N near-identical docs (the at-save vector dedup can't
catch it — divergent edits clear the overlap threshold while being the
same memory by identity).

- store.save() gains optional source_key (caller-owned stable identity).
  At most one live doc per (collection, source_client, source_key):
  unchanged re-save idempotent; changed re-save invalidates prior +
  inserts with an auditable invalidated_by supersession chain.
- Generic content-hash dedup scoped to invalidated_at IS NULL so an
  A->B->A revert surfaces a fresh memory, not a resurrected dead row.
- memory_save tool threads source_key; mirror.py keys it to the file's
  frontmatter name (slug).
- Additive documents.source_key column + index migrate in place;
  pre-existing rows stay NULL = old insert-only behaviour.
- path uuid-salted to hold UNIQUE(collection, path) under same-ms
  re-insert.
- +5 store regression tests + mirror payload assertion; suite 750 green.

Version bumped 0.6.0rc15 -> 0.6.0rc16 for soak.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@cipher813 cipher813 merged commit 54c025d into main May 15, 2026
9 checks passed
@cipher813 cipher813 deleted the fix/mirror-upsert-by-slug-rc16 branch May 15, 2026 13:50
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