Skip to content

Concepts

Two delivery channels

Memory is only useful if it arrives when it is needed. legendary pushes on two signals, and neither requires the agent to decide to ask.

File-touch (surface, PreToolUse) — the agent opens or edits a file, and memories anchored to that file are injected.

Error-signature (guard, PostToolUse) — every episode stores the verbatim error strings that produced it. When one of those strings appears in a Bash result, the episode is pushed back.

The second channel exists because of how models use retrieved memory: an agent acts on past experience when the current situation resembles the recorded one. A recurring error message is the highest-fidelity resemblance signal a coding agent ever produces — no query formulation, no embedding, no guesswork.

Why episodes require triggers

An episode without a verbatim error string cannot be matched against a future failure, so it can only be found by search — the weakest channel. legendary rejects such writes with an actionable message rather than saving something that will never resurface.

Good: triggers: ["sqlite3.OperationalError: database is locked"] Useless: triggers: ["database problem"]

Anchors and staleness

An anchor is {file, symbol?, lines?, commit, content_hash}. At write time the symbol is resolved with tree-sitter and the region is hashed. At recall time it is re-resolved and re-hashed.

Verdict Meaning
fresh the region hashes the same — rendered (verified against current code)
stale the region changed — rendered with an instruction to verify
orphaned the file or symbol is gone

Whitespace-only edits do not invalidate a memory, and a symbol that merely moves stays fresh, because anchors re-resolve by symbol before hashing.

Staleness is what makes pushed procedure safe to act on. Delivering a confident "do it this way" for code that has since changed is worse than delivering nothing — it is the error-propagation failure mode that memory systems without verification cannot avoid.

Two memory types

Type For
decision why the code is the way it is
episode an approach that failed, and why (requires triggers)

Conventions belong in CLAUDE.md and references in docs — both already have a home with better delivery than any memory tool can offer. v0.2 removed those types; memories written under them in v0.1 load as decision.

Corrections cannot destroy knowledge

supersedes requires the replacing memory to cover every anchor of the memory it replaces. This was added after observing a real agent deprecate a broad, two-file memory with a narrower one, leaving a file with no active memory at all. If you cannot cover the anchors, use deprecate(reason=...) instead.

Storage

Markdown in .legendary/memories/, committed. index.db is a derived SQLite FTS5 index — gitignored, versioned, and rebuilt automatically when missing, corrupt, or written by an older schema.