Skip to content

Hot-layer memory silently never loads: templates ship without entry markers, writer never re-adds a missing BEGIN #1426

Description

@rpriven

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:

  1. 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 -->.
  2. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions