You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Authority drain rolls memory importance/source_type back to stale mirror snapshots — same null-then-restore class as #379, and it fires even when zero transforms succeed #391
Enabling rust mode and reverting caused 46 memories to have importance silently rolled back to older values, and 4 archived memories to return to active. No transform ever succeeded in that window — every pass failed in state_sync (that part is #390). The drift came purely from activation + drain.
This is the same shape as #379 (mirror projection writes NULL as a transient placeholder, something else is supposed to restore it) for a different field. #379 fixed supersede pointers by translating pending references on drain. importance/source_type instead restore from mirror_live_memory_rows.full_row_snapshot, and that snapshot can be stale.
Evidence
Fingerprints before the flip and after the revert, against a pre-flip .backup:
JOIN mirror_identity identity
ONidentity.domain='memories'ANDidentity.context_row_id=m.idJOIN mirror_live_memory_rows live
ONlive.module_project=identity.module_projectANDlive.module_row_id=identity.module_row_id
classified_at on those rows is still 2026-08-28 04:12 — nothing re-classified them.
Ruled out
Dreamer. Zero dream_runs rows in the window. Last classify-memories ran 4h earlier (classified=45 changed=33), before the pre-flip backup was taken.
Module store writing directly.mc_memories holds 245 rows with ids 981–1225 and an mtime predating the flip. The changed host ids (868, 875, …) map to it only through mirror_identity, which is exactly the repair join.
Successful transforms. There were none — applied=false, served_from=raw, module=0.0 ms on every pass.
WHERE (memory.source_type IS NULLORmemory.importance IS NULL)
ANDlive.full_row_snapshotIS NOT NULL
and restores both fields from the snapshot. The observed rows were non-null before and after (72 → 70), so the only consistent sequence is:
mirror projection writes importance = NULL as a transient placeholder (the function's own comment describes this: "sparse mapping records overwrote source_type and importance with null before the mirror retained full snapshots");
the repair restores from full_row_snapshot;
the snapshot predates recent host-side classification, so the restore is a rollback.
The repair is doing exactly what it says. The problem is that the snapshot is treated as authoritative for a field the host has since updated, and there is no recency comparison — no classified_at/updated_at check between snapshot and row.
status (4 rows, archived → active) is not touched by this function, so the mirror apply path appears to have a second instance of the same class. I did not isolate that one.
Why it matters more than the numbers suggest
It fires on a failed activation. Nothing in the logs suggests memory was touched; the visible story is "rust mode didn't work, reverted cleanly."
It is invisible to timestamp-keyed checks — classified_at is untouched, and the repair sets updated_at = Date.now(), so a "what changed recently" query shows a bump with no indication that the content of the change was a rollback.
Either make the null-placeholder unobservable (write the projection in a transaction that restores before commit, so a partially-drained store never persists NULL), or make the restore recency-aware (compare snapshot vintage against the row's classified_at/updated_at and prefer the newer). The first is stronger — it removes the window rather than arbitrating it.
More generally: this is the second field found in the null-then-restore pattern. A sweep for other columns the mirror projection nulls transiently would be worth more than fixing importance alone.
Environment
Upstream master with #376/#378/#379/#381 present; ck-mc rebuilt at matching generation. Store at schema v82, 3185 memories, 704 supersede pointers. Pre-flip .backup retained; drift is reversible from it.
#379 itself held perfectly — 704 pointers and an identical hash across the same drain that produced this. This is the neighbouring field, not a regression of that fix.
Summary
Enabling rust mode and reverting caused 46 memories to have
importancesilently rolled back to older values, and 4 archived memories to return toactive. No transform ever succeeded in that window — every pass failed instate_sync(that part is #390). The drift came purely from activation + drain.This is the same shape as #379 (mirror projection writes NULL as a transient placeholder, something else is supposed to restore it) for a different field. #379 fixed supersede pointers by translating pending references on drain.
importance/source_typeinstead restore frommirror_live_memory_rows.full_row_snapshot, and that snapshot can be stale.Evidence
Fingerprints before the flip and after the revert, against a pre-flip
.backup:Diff against the backup:
importancediffers on 46 rows,statuson 4,contenton 0,seen_counton 0.The new value equals the mirror snapshot in every case:
via
classified_aton those rows is still2026-08-28 04:12— nothing re-classified them.Ruled out
dream_runsrows in the window. Lastclassify-memoriesran 4h earlier (classified=45 changed=33), before the pre-flip backup was taken.mc_memoriesholds 245 rows with ids 981–1225 and an mtime predating the flip. The changed host ids (868, 875, …) map to it only throughmirror_identity, which is exactly the repair join.applied=false,served_from=raw,module=0.0 mson every pass.Mechanism
repairNullClobberedMemoryRows(context-authority.ts:1670) selects:and restores both fields from the snapshot. The observed rows were non-null before and after (72 → 70), so the only consistent sequence is:
importance = NULLas a transient placeholder (the function's own comment describes this: "sparse mapping records overwrote source_type and importance with null before the mirror retained full snapshots");full_row_snapshot;The repair is doing exactly what it says. The problem is that the snapshot is treated as authoritative for a field the host has since updated, and there is no recency comparison — no
classified_at/updated_atcheck between snapshot and row.status(4 rows,archived → active) is not touched by this function, so the mirror apply path appears to have a second instance of the same class. I did not isolate that one.Why it matters more than the numbers suggest
classified_atis untouched, and the repair setsupdated_at = Date.now(), so a "what changed recently" query shows a bump with no indication that the content of the change was a rollback.scope|shareable|importancecatches it. That is exactly why rust mode: superseded_by_memory_id silently lost when authority is dropped with pending mirror references outstanding #379 needed a paired count+hash fingerprint to detect, and the same instrument caught this.Suggested direction
Either make the null-placeholder unobservable (write the projection in a transaction that restores before commit, so a partially-drained store never persists NULL), or make the restore recency-aware (compare snapshot vintage against the row's
classified_at/updated_atand prefer the newer). The first is stronger — it removes the window rather than arbitrating it.More generally: this is the second field found in the null-then-restore pattern. A sweep for other columns the mirror projection nulls transiently would be worth more than fixing
importancealone.Environment
Upstream master with #376/#378/#379/#381 present;
ck-mcrebuilt at matching generation. Store at schema v82, 3185 memories, 704 supersede pointers. Pre-flip.backupretained; drift is reversible from it.#379 itself held perfectly — 704 pointers and an identical hash across the same drain that produced this. This is the neighbouring field, not a regression of that fix.