Summary
The autonomic memory reviewer writes durable facts into the hot-layer files
(USER/PRINCIPAL/PRINCIPAL_MEMORY.md, USER/DIGITAL_ASSISTANT/DA_MEMORY.md),
but none of them are ever injected back into a session on a stock install.
The flagship "memory that compounds" loop captures correctly and recalls nothing.
Two bugs compound to cause it:
- The shipped templates contain no entry markers at all. Both
install/USER/PRINCIPAL/PRINCIPAL_MEMORY.md and
install/USER/DIGITAL_ASSISTANT/DA_MEMORY.md ship with only the prose comment
<!-- Entries are appended automatically… --> — no <!-- BEGIN ENTRIES -->
and no <!-- END ENTRIES -->.
MemoryWriter.ts self-heals a missing END marker but never a missing BEGIN.
serializeFile has if (!post.startsWith(END_MARKER)) post = END_MARKER + post
with no symmetric guard for BEGIN. So every write to a marker-less file appends
another END and never a BEGIN.
Why it's silent and total
LoadMemory.hook.ts (the reader that injects memory each prompt) slices content
between the first <!-- BEGIN ENTRIES --> and the first <!-- END ENTRIES -->.
With no BEGIN, startIdx === -1 → it returns { entries: [], count: 0 } and
renders (no entries yet) — regardless of how many facts the reviewer has written.
MemoryHealthCheck.ts parses with the same marker contract, so it reports empty
too; nothing alarms. The three other readers (PULSE/modules/memory.ts,
MemoryRestore.ts) share the contract and also see nothing.
Meanwhile MemoryWriter.parseFile, on a marker-less file, treats the whole body as
preEntriesBody and returns entries: [], so dedup / the 48-entry cap / eviction
never run either — the file just accretes stray END markers on every write.
Reproduction
Fresh install, then let the reviewer run a few times (or just inspect a stock file):
# The shipped template has neither marker:
grep -c 'BEGIN ENTRIES' ~/.claude/LIFEOS/USER/PRINCIPAL/PRINCIPAL_MEMORY.md # → 0
grep -c 'END ENTRIES' ~/.claude/LIFEOS/USER/PRINCIPAL/PRINCIPAL_MEMORY.md # → 0
# After the reviewer has written N facts, the reader still injects zero:
echo '{}' | bun ~/.claude/hooks/LoadMemory.hook.ts
# → ## PRINCIPAL MEMORY [0/48 entries] (no entries yet) ← despite N stored facts
On an install that has been running a while, the file shows the accretion signature:
BEGIN=0, END=<number of writes>, real PREFERENCE:/RULE:/NAME:/RELATION: lines
stranded outside any readable block.
Impact
- Universal: every stock v6 install ships the marker-less templates + the
non-healing writer, so the hot-layer memory feature is non-functional out of the box.
- Platform-independent: it's a template + a logic gap in shared TypeScript, not a
Linux/casing issue.
- Silent: no error, health check blind to it — the assistant simply never seems to
remember anything durable about the principal.
Fix
PR attached. Three parts: (1) ship both markers in the templates (fresh installs);
(2) make serializeFile heal a missing BEGIN symmetrically (can't recur) and make
parseFile recover orphaned entries from an already-broken file; (3) a one-time
MigrateMemoryMarkers migration so existing installs repair on update. A regression
test covers the previously-untested "write in one session, read in the next" path.
Summary
The autonomic memory reviewer writes durable facts into the hot-layer files
(
USER/PRINCIPAL/PRINCIPAL_MEMORY.md,USER/DIGITAL_ASSISTANT/DA_MEMORY.md),but none of them are ever injected back into a session on a stock install.
The flagship "memory that compounds" loop captures correctly and recalls nothing.
Two bugs compound to cause it:
install/USER/PRINCIPAL/PRINCIPAL_MEMORY.mdandinstall/USER/DIGITAL_ASSISTANT/DA_MEMORY.mdship with only the prose comment<!-- Entries are appended automatically… -->— no<!-- BEGIN ENTRIES -->and no
<!-- END ENTRIES -->.MemoryWriter.tsself-heals a missing END marker but never a missing BEGIN.serializeFilehasif (!post.startsWith(END_MARKER)) post = END_MARKER + postwith no symmetric guard for BEGIN. So every write to a marker-less file appends
another
ENDand never aBEGIN.Why it's silent and total
LoadMemory.hook.ts(the reader that injects memory each prompt) slices contentbetween the first
<!-- BEGIN ENTRIES -->and the first<!-- END ENTRIES -->.With no BEGIN,
startIdx === -1→ it returns{ entries: [], count: 0 }andrenders
(no entries yet)— regardless of how many facts the reviewer has written.MemoryHealthCheck.tsparses with the same marker contract, so it reports emptytoo; nothing alarms. The three other readers (
PULSE/modules/memory.ts,MemoryRestore.ts) share the contract and also see nothing.Meanwhile
MemoryWriter.parseFile, on a marker-less file, treats the whole body aspreEntriesBodyand returnsentries: [], so dedup / the 48-entry cap / evictionnever run either — the file just accretes stray
ENDmarkers on every write.Reproduction
Fresh install, then let the reviewer run a few times (or just inspect a stock file):
On an install that has been running a while, the file shows the accretion signature:
BEGIN=0,END=<number of writes>, realPREFERENCE:/RULE:/NAME:/RELATION:linesstranded outside any readable block.
Impact
non-healing writer, so the hot-layer memory feature is non-functional out of the box.
Linux/casing issue.
remember anything durable about the principal.
Fix
PR attached. Three parts: (1) ship both markers in the templates (fresh installs);
(2) make
serializeFileheal a missing BEGIN symmetrically (can't recur) and makeparseFilerecover orphaned entries from an already-broken file; (3) a one-timeMigrateMemoryMarkersmigration so existing installs repair on update. A regressiontest covers the previously-untested "write in one session, read in the next" path.