Repository navigation
levain 0.5.10
The seed now gives Claude Code's own auto-memory a job (the operator-facing layer) instead of displacing it, and a Claude Code install mirrors every native memory write into the entity's anneal store.
Changed
seed/memory.mdnow hands the harness's built-in memory the operator-facing layer. It used to call that memory "a scratchpad for harness-operational notes only ... subordinate", and in Claude Code runs with auto-memory on, a Levain entity wrote no native memory files at all. The new wording keeps anneal-memory authoritative for the work history, decisions, the self-model and anything that must be portable or governed, and tells the entity to save the operator's preferences, shorthand, working rules and corrections in the harness's memory, alongside operational notes. A rule that shapes a work decision still gets its own anneal episode. The tie-break is unchanged: on the self-model, anneal-memory wins. An uneditedseed/memory.mdis refreshed bylevain update; an edited one is kept and the new version staged under.levain/pending/.- The trade, stated plainly: what the entity writes to native memory lives in Claude Code's store (
~/.claude/projects/<folder>/memory/), which is not sovereign, not portable and not under anneal-memory's immune system. The mirror below is what brings it back under anneal.
Added
- The auto-memory mirror (Claude Code adapter), ON by default. A new
activation/hooks/automemory_mirror.py, wired as aPostToolUsehook onWrite|Editand swept at every session start, copies each change to this install's Claude Code auto-memory notes into this install's anneal store, one way. A new note becomes an episode carrying its text; an edit records an episode that supersedes the current one for that note (anneal's own--supersedes, so recall hides the old one); a deletion records a retraction that supersedes it; a note recreated after a deletion supersedes the retraction. Notes edited by hand or through Bash (rm,sed -i) are picked up at the next sweep, not at once. It never writes continuity and never writes back to native memory; the wrap decides what graduates. The hook does its work in a detached process and always exits 0; on a write under Claude Code's projects folder it may first run onegit rev-parse(capped at 2 s).- What is superseded is read from the store, not from the mirror's own state file: a write supersedes every episode the store currently shows for that note. anneal accepts a second supersession of an episode that was already superseded, so an id remembered locally could otherwise leave an old note current.
- No backfill. The first sweep that sees a memory folder records the hash of every note already in it and writes no episode: on a new install, on an existing one upgraded with
levain update, and for anautoMemoryDirectoryset later. A folder that drops out of view (a differentCLAUDE_CONFIG_DIR, a removed setting) keeps its record, so it is not backfilled when it comes back. A note written after the session started is mirrored even when the session-start sweep runs after it. - Turn it off with
"automemory_mirror": falsein.levain/config.json(durable), orLEVAIN_AUTOMEMORY_MIRROR=off(per session; it wins over the config). - What a mirrored episode looks like: source
automemory-mirror, tagsautomemory,mirrorandamem-path-<16 hex>(one per note). A note whose frontmattertypeisuserorfeedbackis typedecision, taggedoperator-rule, with content startingOPERATOR RULE (auto-memory mirror): memory/<file>.md; any other note is typecontext, startingAUTO-MEMORY NOTE (auto-memory mirror): memory/<file>.md. An edit reads..., REVISED (auto-memory mirror)and a deletionRETRACTED OPERATOR RULE/RETRACTED AUTO-MEMORY NOTE.anneal-memory --db .levain/memory.db episodes --source automemory-mirrorlists the current ones (add--include-supersededfor the history). - Which folders: Claude Code's per-project folder for the install directory, for the git work tree it sits in and for the repository of a linked worktree (Claude Code keys auto-memory by repository, so an install inside a repository shares that repository's notes), or
$CLAUDE_CODE_PROJECT_DIR_NAME; plus the effectiveautoMemoryDirectorywhen it is set in the install's.claude/settings.local.jsonor.claude/settings.json(an absolute or~/path, as Claude Code requires). A user-scopeautoMemoryDirectoryis not mirrored: one folder shared by every project would pull other projects' notes into this entity. AnautoMemoryDirectorygiven byclaude --settingsor by managed (policy) settings cannot be seen by a hook and is not mirrored. Withscope: "global", sessions started outside the install write to other projects' folders, which are not mirrored.MEMORY.md(the index) is never mirrored. - If the state file is lost while the store already holds mirror episodes, the mirror stops (re-baselining then would leave an edited or deleted note current until its next edit) and writes
.levain/automemory_mirror.lost;levain doctorreports it. Recovery is a person's: restore.levain/automemory_mirror.jsonfrom a backup (an older copy is safe) and delete the marker, or turn the mirror off. Deleting only the marker stops it again. - If the very first sweep cannot read the anneal store, it waits (it cannot tell a new install from lost state) and records when; once the store is readable, notes written since then are mirrored, not baselined.
levain doctorreports the wait. - A memory folder that is missing or cannot be listed (an unmounted volume, a removed directory) is skipped, and nothing in it is retracted;
levain doctorreports it when it holds mirrored notes.
levain doctorreports the mirror: wired, off, start-sweep only (advisory: runlevain update), stopped, or a last sweep that could not read the store, left notes unmirrored or could not list a memory folder.
Known open issues
levain doctorreports the mirror wired when aPostToolUsecommand namesautomemory_mirror.py, without checking that its matcher coversWrite/Editor that it passeshook; only a hand-edited.claude/settings.jsoncan differ from the rendered one.- The first-sweep wait records one time for all folders. A folder that cannot be listed when the store recovers is baselined, not mirrored, when it returns; a folder configured during the wait has notes written after the wait began mirrored.
- A sweep that waits more than 60 seconds for another sweep's lock gives up, and the note it was started for is mirrored at the next session start or memory write, not at once.