Skip to content
Download

ADR 0129 — Core memories are user-approved and start every session

  • Status: Amended
  • Date: 2026-11-04
  • Amended by:
    • ADR 0136 — a proposal an agent or Organize made can return to proposed, which undoes its approval or rejection
    • ADR 0143 — sessions receive generalised guidelines the user approved; agents’ and Organize’s statements are kept as records without approval; the budget is 3,000 estimated tokens
  • Related: ADR 0021, ADR 0037, ADR 0060, ADR 0067, ADR 0116, ADR 0130

The user asked for a “main core memories” collection: their preferences and the things they like an agent to do, kept apart from everything else.

Nothing like it exists, and what comes closest does not reach a session:

  • The preference type is set by regular expressions (“i prefer”, “don’t like”, imprint/classifier.py) or by the LLM tagger. It is one type among many, and retagging can change it (ADR 0067’s rejected alternatives).
  • Nothing from memory starts a session. The SessionStart hook adds a fixed reminder and the session’s workspace only (imprint/hook_entry.py, session_context). wake_up, whose digest weights preferences highly, runs only on a session’s first search with detail="full". The default compact search returns before it (imprint/server.py, ADR 0021).
  • Claude Code’s own memory files are imported as ordinary notes. Each file in ~/.claude/projects/*/memory becomes a decision note. An edited file adds a new note beside the old one (imprint/cli_conversations.py, learn_auto_memory).
  • Retrieved text is untrusted. The server’s instructions tell agents to treat retrieved documents as reference material, not instructions (imprint/server.py). That is right for indexed content, which anyone’s file or transcript may have written. It is also why ordinary memories cannot simply be handed to an agent as rules.

A core memory is one short statement about the user that the user has approved, for example “Commit only when asked” or “Prefers concise answers that lead with the result”. Each has:

  • a kind: preference, instruction or fact about the user;
  • a scope: everywhere, or one project;
  • the evidence it came from: the memory ids and verbatim quotes of a proposal, or “written by the user”;
  • a state: proposed, active or retired;
  • when it was approved, and on which device.

Core memories live in their own store, outside every workspace’s collection. They describe the user, not a project’s content, so they apply in every workspace. Only an approval in the dashboard makes one active:

  • Agents can propose, never approve. A store with type preference adds a proposal and says so in its reply.
  • The background job of ADR 0130 proposes them from preference-typed memories, imported client memory files and conversation turns in which the user states how they want things done. Each proposal needs a verbatim quote, as ADR 0099 requires of model suggestions.
  • The user can write one directly on a Core memories page, which also edits, scopes and retires them.

The MCP server appends the active core memories to its handshake instructions when a session starts it: the global ones, then those scoped to the session folder’s project, within 2,000 characters. The session’s projects are the nearest manifest’s name and the folder names. The block says the user approved them. Because the user approved each one, they are instructions, unlike retrieved documents. A store that cannot be read leaves them out rather than stopping the server.

The instructions reach every client that honours them, desktop apps without hooks included. Claude Code already passes them to its model. The SessionStart hook, which the proposal named, would have sent the same list a second time there, and it reaches only the clients Imprint installs hooks for.

Core memories sync to paired devices as a fourth manifest dataset, keyed by id and versioned by their last change. The newest change of each wins, so an approval or retirement made on one device holds on all of them. A device sends them only to a peer whose handshake offers core. Older peers neither send nor receive them, and keep syncing as before.

  • A core flag on ordinary memories, or a memories_core Qdrant collection. Core memories are few, edited by hand and handed over whole, not searched by similarity. Qdrant records are immutable apart from classification (ADR 0051), so editing one would mean a new memory id each time.
  • Injecting preference-typed memories automatically. The type comes from regular expressions and an LLM, and those memories are untrusted text. A quoted third-party instruction would become a standing rule.
  • Attaching core memories to search results. ADR 0021 kept compact search free of session context. Session start is the one moment every client already gives Imprint.
  • Every session starts with the user’s approved rules, in every client that passes MCP handshake instructions to its model. A client that ignores them gets none. Codex and Copilot are not yet checked.
  • A change takes effect in the next session, because the instructions are read when the server starts.
  • The user curates a short list instead of hoping the right memory ranks first.
  • It adds a store (data/core-memories.sqlite3), a sync dataset with deletion-free state changes, a dashboard page and an instructions block. Each is a decision this record makes for core memories only.
  • Every write redacts the statement (ADR 0060), imports from paired devices included.
  • A long list costs every session tokens. The budget keeps it bounded, and the page shows how much of it is used.
  • If users rarely approve proposals, drop proposals and keep the hand-written list.
  • If clients gain a standard way to receive user rules (for example a shared rules file every client reads), write core memories there instead of through hooks.
  • If sync gains general mutable records, store core memories as those.
  • tests/test_core_memories.py:
    • the user’s are active and agents’ are proposals;
    • a statement held in any state, a retired one included, is not proposed again;
    • statements are bounded and redacted;
    • approving, retiring and restoring work, and nothing returns to a proposal;
    • a session gets the global ones, then its project’s, within its budget;
    • sync keeps the newest change of each and skips invalid records;
    • core memories export as a stream the receiver accepts, and the paired import stores them;
    • a peer without core is sent none;
    • the MCP instructions carry the session’s core memories;
    • a stored preference becomes a proposal.
  • tests/test_sync_delta.py: the manifest lists core memories, and an older peer’s manifest decodes without them.
  • imprint/ui/tests/core.spec.ts: adding, approving a proposal with its quote shown, editing it before approval, retiring, restoring, and the budget meter.
  • Not verified: how Codex and Copilot handle MCP handshake instructions, and a sync between two devices on hardware.