fix(rust): drop supersede pointers outside the authority seed set - #378
Conversation
`authoritySeedRows` seeds every memory for a project with no status
filter, forwarding `superseded_by_memory_id` verbatim. When that target
is absent from the seed set, the module records a pending memory
reference it can never resolve: the resolution sweep in
`seed_memory_snapshots` only clears pendings whose target later appears
in `mc_memories`, and a hard-deleted row never will. The pending then
survives to `authority_finish_prepare`, which rejects the memories-domain
handoff outright:
store: storage backend: authority prepare complete rejected:
2 unresolved pending memory references
Every transform pass degrades to `served_from=raw` with `materialized=0`,
so m[0]/m[1] are never built — the operator sees an empty sidebar and a
reconnecting notification, and rust mode can never be entered.
These pointers are produced by ordinary merge/curate activity: the losing
row records `superseded_by_memory_id`, the winning row is later archived
and eventually deleted, and the pointer outlives it. Nothing in the TS
path dereferences it, so it stays inert and invisible until the module's
store resolves it.
Fixed host-side rather than by relaxing the Rust gate, because the host
can distinguish two cases the store cannot:
- target absent from the seed set -> dead link, drop it here
- target present in the seed set but missing module-side -> genuine
ingest failure, pending fires and the gate correctly rejects
Relaxing the gate would collapse both into "ignore" and lose the
protection it exists to provide.
Measured on one install: 7 of 40 projects affected, 2-25 rows each. The
change is surgical — on the affected project it drops exactly the 2 dead
links and preserves all 61 resolvable pointers.
Ready-to-post review comment for PR #378Approved. I verified that authority seeding includes archived rows, while the module keeps a project-scoped pending reference when a supplied target never arrives and rejects prepare completion. This patch removes only targets outside the complete project seed, so it preserves the important failure signal for a target the host did send but the module did not ingest. The two new host-seed tests pass, including the same-seed preservation and cross-project cases; I also ran the matching Rust-store pending-reference gate test and the plugin typecheck. No blocker found. One wording-only suggestion: the implementation comment says an out-of-set target was hard-deleted, but the cross-project case can still exist outside this project. “Outside this project's seed” would describe both cases precisely. |
Fixes #377.
The bug
authoritySeedRows(rust-mode-transform.ts:907) seeds every memory for a project — no status filter — and forwardssuperseded_by_memory_idverbatim:When that target is absent from the seed set,
seed_memory_snapshotsrecords a pending memory reference (crates/mc-store/src/lib.rs:14948-14958). The resolution sweep at:15068-15080only clears pendings whose target later appears inmc_memories, so a hard-deleted target never clears. The pending survives toauthority_finish_prepare(:14035-14047), which rejects the handoff:Every pass degrades with
materialized = 0, so m[0]/m[1] are never built — empty sidebar, "reconnecting" notification, and rust mode is unreachable. It is permanent: nothing repairs supersede pointers and the targets are gone.The pointers come from ordinary merge/curate activity — the losing row records
superseded_by_memory_id, the winner is later archived then deleted, the pointer outlives it. Nothing in the TS path dereferences it, so it stays inert and invisible until the module's store resolves it.Why host-side and not a gate relaxation
My first instinct (and the original text of #377) was to relax the Rust gate — treat unresolvable as resolved-to-null in
seed_memory_snapshots. After finding the resolution sweep, I think that's wrong: it would also swallow a genuine ingest failure, where the host sent the target and the module failed to store it. The store cannot tell those apart.The host can, because it knows the seed set it just sent:
So the gate keeps its protective value and only the provably-dead links are dropped.
The change
Note the seed set is project-scoped, so a target belonging to another project is equally unresolvable — covered by the same check and by a test.
Verification
Red-checked (reverted the fix, confirmed both new tests fail for the right reason, restored):
Gates:
Simulated against a real 3,101-memory store, on the project that hit this:
61-of-63 is the number that matters — the change is surgical, not a blanket null of the column.
Scope in the wild
Measured on one long-lived install, per project (blocking rows / seeded rows):
A fresh install has none, which is likely why this hasn't surfaced — it needs merge/curate history plus an eventual hard delete. Detection query is in #377 if you want to check other installs.
Tests
Two regression tests in
rust-mode-transform.test.ts, both red-checked:authoritySeedRowsis added to the existing__rustModeTransformTestexport surface; no other production surface changed. In-memory DB, nomock.module(avoiding the process-global bleed of #279).Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.Summary by cubic
Fixes rust mode being permanently unreachable when a memory's
superseded_by_memory_idpoints to a row that was hard-deleted after being archived. The seed set no longer includes that target, so the Rust store records an unresolvable pending reference and rejects the handoff. Host now drops such dead pointers before seeding, while keeping resolvable ones intact.Written for commit a0f548c. Summary will update on new commits.
Greptile Summary
The PR prevents permanently unresolved Rust authority references by clearing supersession pointers whose targets are absent from the complete project-scoped seed set.
Confidence Score: 5/5
The PR appears safe to merge, with no actionable correctness or security issues identified.
The project-scoped seed is complete before transport batching, valid in-seed supersession references are preserved, and host and module checksums operate on the same transformed snapshots.
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart LR A[Load all project memories] --> B[Build seeded ID set] B --> C{Supersession target included?} C -->|Yes| D[Preserve pointer] C -->|No| E[Set pointer to null] D --> F[Seed Rust authority] E --> F F --> G[Complete authority handoff]Reviews (1): Last reviewed commit: "fix(rust): drop supersede pointers outsi..." | Re-trigger Greptile
Context used: