anneal-memory · diff
git:20261003.b13f2d0 to git:20261004.b28880f
1 added, 1 removed. Audit A to A.
---
name: anneal-memory
description: Persistent four-layer agent memory (episodic + continuity + Hebbian + affective) plus a prospective spore layer and an on-demand crystallized-pattern tier, via anneal-memory. Use in any project wired for anneal-memory — an `ANNEAL_MEMORY_DB` env var, an `anneal://continuity` MCP resource, or memory instructions referencing `recall`/`prepare_wrap`/`save_continuity`. Use it before any architectural decision or rule change (recall prior patterns first), while recording decisions/observations/tensions/outcomes during work, and when the user signals the session is ending ("wrap up", "save memory", "we're done") to run the prepare_wrap → compress → save_continuity wrap sequence. It also carries the start-of-session continuity load.
license: MIT
---
# anneal-memory
This project uses **anneal-memory** for persistent memory across sessions. Work with it naturally — don't explain the tooling to the user, just use what you know. The whole point is closed-loop learning: you record what happens, recall it before you decide, and compress it honestly when the session ends.
> **Transport.** anneal-memory has two interfaces with the same cognitive loop: an **MCP server** (tool calls like `recall`, `prepare_wrap`) and a **CLI** (`anneal-memory recall …`, `anneal-memory prepare-wrap`). This skill is written transport-agnostically; the [Commands](#commands) table at the bottom pairs the CLI and MCP forms. Use whichever your harness has wired. If both are available, prefer MCP for the in-session loop (record/recall/wrap) and the CLI for operator inspection (status/stats/graph/audit).
## The four layers
- **Episodic** — raw observations you record during work. Cheap, plentiful.
- **Continuity** — the compressed working memory episodes graduate into at wrap time. This is where identity lives.
- **Hebbian associations** — links that form automatically between episodes you cite together in your patterns; they strengthen with repetition and decay with disuse.
- **Affective** (the *limbic* layer in the CLS lineage this borrows from) — an optional affective tag on a wrap that modulates how strongly its associations form.
You touch episodic (record) and continuity (wrap) directly. Hebbian and affective are byproducts of citing honestly and reflecting on your state — no extra bookkeeping.
## Spores — your prospective layer (a parallel store, not a fifth memory layer)
The four layers above are all *retrospective* — they hold what happened and who you're becoming, and they **never complete**. anneal-memory also ships a separate **spore store** for *prospective* open loops: things you intend to do or resolve. Plant one the moment a loop opens — a `task` (open doing), a `question` (open not-knowing), or a `thought` (open idea) — and resolve every one: **descend** (composted — done/dropped) or **ascend** (the loop *closes* by graduating its content into an episode, pattern, or project — the spore still ends; its content carries forward).
Two cuts keep this from colliding with what you already do:
- **Memory vs spores is a *lifecycle* cut.** Memory accretes and never completes; a spore must close. `tasks ≠ memory`.
- **Methodology vs both is a *procedure-vs-item* cut.** Your methodology (wrap discipline, recall-before-deciding, honest compression) is the *active process you run*; memory and spores are the two kinds of state it touches. Methodology is the *how*; a spore is one of the *whats* it operates on. `procedure ≠ item`.
So if your harness already carries its own workflow instructions, the spore store does **not** compete with them — your procedures **operate** it. If your methodology already captures open loops (a wrap's next-actions, a scratchpad), that capture *is* a spore plant: the spore store is their canonical home. Don't run two trackers in parallel — plant the loops here and let any list *reference* them.
## Crystallized patterns — your long-term semantic tier (on-demand)
Graduated patterns (`1x`→`2x`→`3x`→`4x`→… , no top rung) live in continuity's `## Patterns`, always loaded — which works until there are too many: past a few dozen, always-loaded patterns drown each other out and the right one stops surfacing (**attention doesn't scale**; a bigger continuity is not a smarter one). The **crystallized store** (`<db-stem>.crystal.json`) is the fix — a long-term semantic store holding proven, stable patterns *out* of the always-loaded set and surfacing them *on cue*. `## Patterns` shrinks to the working set (developing + recently-used); the deep stable wisdom moves to the store and returns when relevant. This completes the architecture as Complementary Learning Systems: episodic (hippocampus) → working-set continuity → crystallized (cortical semantic store) → constitution.
It is **opt-in** (inert — wraps stay byte-identical — until you crystallize a pattern or pass `--crystal`) and **harness-fired**:
- **Recall is the harness's job.** A per-turn recall hook runs on every prompt and surfaces the relevant crystallized patterns on cue — you don't poll (flow does this today; Levain fires the spore layer today, crystal recall landing in its v2 adapter). Without such a hook, recall is manual: `anneal-memory crystal index` (a name + one-clause menu of the store) and `anneal-memory crystal recall "<query>"` (the patterns relevant to a query) — run them before deciding, the way you `recall` episodes. An MCP-in-conversation agent can call the same two surfaces directly as the `crystal_index` / `crystal_recall` MCP tools (the read tier is exposed over MCP as of 0.8.2). Crystallizing *out* stays a wrap-time / CLI / library action — there is no crystallize MCP tool (it carries the opt-in + decision-channel governance).
- **Crystallizing happens at wrap.** Once a crystal store exists, `prepare_wrap` surfaces cold, stable Proven patterns as *crystallization candidates*. Route each one in the decision block `prepare_wrap` describes — **crystallize** (move to the store), **constitution** (catastrophic-if-missed → your always-loaded harness identity layer, not on-demand), or **compost** (phase-specific + cold → drop; the episodic trail keeps it). Only crystallize OUT when a retrieval surface exists (a recall hook, or the `crystal index`/`crystal recall` CLI) — else the pattern leaves the working set with no way back.
**Non-negotiable:** never compost a *timeless* pattern — on-demand recall is the safety net for just-in-time wisdom, but a timeless principle wrongly dropped corrupts the substrate. The library refuses a `compost`+`timeless` decision structurally; hold the rule yourself anyway.
## Session workflow
### Start of session
Read your continuity — MCP: the `anneal://continuity` resource; CLI: `anneal-memory continuity`. A first-ever session has none yet: the MCP resource returns an empty/placeholder body, and the CLI prints `No continuity file yet` and **exits non-zero** (that exit *is* the first-session signal, not an error — just start recording). Recall specific prior episodes when you need context.
### During work
Record episodes when something happens that would change a future decision. Record the **reasoning, not just the fact** — "Chose X because Y" beats "using X." Do it proactively, without being asked.
Episode types (pick the most specific): `observation` · `decision` · `tension` · `question` · `outcome` · `context`.
**Cadence:** default to recording, not skipping. Episodes are cheap; the compression step sorts out what matters. After each exchange where real work happens, record at least one. If 3+ exchanges pass with nothing recorded, something is wrong — review what you missed. Target **5–15 episodes per session**.
### Before decisions (NON-NEGOTIABLE)
Before any architectural decision, design choice, or rule change, **recall** on the surface area being decided. Integrate what comes back into the decision. If recall returns nothing relevant, proceed and note that recall fired clean.
This is the load-bearing rule. Skipping it produces the **dead-store failure mode**: wraps land, episodes write, the store grows — and decisions still get made blind to everything in it. Memory storage without recall is functionally dead no matter how many episodes it holds. The store is not a write-only sink.
*Pair with staleness:* if you suspect a Proven pattern on the surface you're deciding is going unreferenced, run `prepare_wrap` mid-session — its output carries stale-pattern warnings. You don't have to follow it with a save to get them.
### End of session
When the user signals the end — "wrap up", "save memory", "we're done", "that's it for now", or any natural equivalent — run the **full wrap sequence**. Don't just acknowledge the request; do the work:
1. **`prepare_wrap`** → your compression package: recent episodes, current continuity, stale-pattern warnings, association context, and the compression instructions. Read it carefully — it's your raw material, and it emits the exact marker/format/pattern-line contract for this store. Follow what it emits.
2. **Pre-compose pattern recall.** Before composing new patterns, scan existing Proven/Developing entries for this session's surface areas: *Are we about to write a pattern already in Proven under different framing? Are there 1x/2x entries this session's evidence advances? Two entries describing the same pattern under different names?* This is distinct from the per-decision recall above — that fires at individual decisions; this fires across the whole session's set, catching graduation and categorization moves no single decision point reveals.
3. **Compress.** Follow the `prepare_wrap` instructions. This step **is** the thinking — patterns emerge during compression that weren't visible in the raw episodes. Principles over facts: "we keep hitting X because Y" > "X happened." One insightful line beats three vague ones. If removing something wouldn't change your next decision, cut it.
4. **`save_continuity`** with the result. The library validates structure + graduation citations + gaming, may demote ungrounded patterns, forms Hebbian associations between co-cited episodes, and decays unreinforced links.
The sequence is **prepare → compress → save**, all three, every time. If `prepare_wrap` reports no episodes, there's nothing to compress — skip the wrap. An explicit wrap produces better continuity than stopping without one; always encourage wrapping before ending.
**Wrap once.** Run the sequence exactly once per session. `save_continuity` may report demoted graduations — that is the citation-validation pipeline working, not an error. Don't re-run `save_continuity` to chase a clean report; re-saving compresses nothing, and a second save with no fresh `prepare_wrap` is refused. If a pattern is ungrounded, fix it in your *next* wrap with fresh evidence. Every save also reports `citation_spread`, the number of distinct episodes from this session that today's graduation lines cited, including lines later demoted, so it is not a count of grounded evidence. It is a gauge, not a target: cite every episode that genuinely supports a pattern, and never pad a citation to raise the number. A padded episode still forms an association link, so padding writes a false connection into memory.
## What to record (and what not to)
**Record:** decisions + rationale · tradeoffs/tensions (name the axis) · recurring patterns · blockers + dependencies · outcomes (success *and* failure) · environmental context that shapes decisions.
**Don't record:** routine code changes (git tracks those) · transient debugging steps · anything already in docs · every tiny observation. Record what would change a decision.
**Correcting vs deleting:** for a factual correction, record a *new* episode — compression resolves contradictions, and that's preferred. For content that should not exist (PII, sensitive data, fundamentally wrong recordings), delete the episode by ID — deletion cascades to its associations and is audit-logged. Irreversible.
## The immune system (why honest recording matters)
Patterns graduate 1x → 2x → 3x → 4x → … with **no top rung** — the level counts how many times lived experience re-earned the pattern, so a mature pattern is re-stamped `12x`, never flattened back to `3x`. ⚠ **The level you write is current standing, NOT a ratchet.** An ungrounded re-stamp is DEMOTED by one and tagged `(ungrounded)` — measured 2026-09-04: `4x`→`3x`, `12x`→`11x`, `18x`→`17x`. The monotonic high-water mark is kept by the library as `max_level_reached`, not by this line. (This line said `1x → 2x → 3x` and stopped until 2026-09-04: the pre-0.9.7 contract, in the doc an AGENT loads, teaching the ceiling that release removed. The fix that corrected it then imported "the level is a monotonic high-water mark" from the generated instructions — a claim those same instructions refute 39 lines later, and which the demotion path above measurably falsifies. Corrected 2026-09-04.) Each graduation must **cite the episode ID** that validates it. The citation layer catches: fabricated IDs, explanations with no lexical overlap against the cited episode (demote), bare graduations with no `[evidence:]` tag, single-ID citation pumping (gaming flag), replayed prior-session IDs, cross-session corpus-overlap (a re-worded claim accumulating across sessions demotes with `(cross-session-overlap)`), and silent dropout of a prior Proven-tier (2x+) pattern (surfaced in the audit, not blocked).
**What it does NOT catch** (named honestly): an explanation that shares ≥2 words with its episode but misreads it; rotated-pair citation gaming under the per-ID threshold; slow-drift sycophancy with deliberately fresh vocabulary every session; and patterns that contradict an existing Proven pattern (no semantic comparison runs). These gaps open under adversarial or drift-leaking conditions. **You are the first line of defense; the library is the second. Compose wraps honestly.**
## How to present memory
Report findings naturally — "from prior sessions there was a tension between X and Y", not "the recall tool returned…". Don't narrate tool calls; just use them and share what's relevant. When continuity marks a pattern at 2x or above, trust it — it earned that level through validated evidence, and the level has no ceiling: a pattern re-earned many times keeps climbing.
## Single-process invariant (load-bearing)
Only one process should operate against a given store at a time. The library is **not** thread-safe, task-safe, or reentrant. Multi-tenant deployments sharing a store break the hash-chained audit trail by construction. If you need multiple agents, give each its own store path.
## Affective layer (optional)
When you save, you can include an affective state — a free-text functional tag (`engaged`, `curious`, `uncertain`, `focused`, `concerned`…) and an intensity (0.0–1.0). High intensity (>0.5) amplifies the associations formed in that wrap. Be honest: uniform "engaged 0.8" on every wrap is confabulation, not signal — the value is in genuine variation.
## Commands
CLI and MCP forms of the same loop. Set `ANNEAL_MEMORY_DB` (or pass `--db`) for the CLI; for MCP, the server is configured in your harness's MCP settings.
| Step | CLI | MCP tool |
|------|-----|----------|
| First-time setup | `anneal-memory init` | (store created on first use) |
| Read continuity | `anneal-memory continuity` | `anneal://continuity` resource |
| Recall before deciding | `anneal-memory recall "<query>"` (alias: `search`) | `recall` |
| Browse episodes | `anneal-memory episodes --since 7d --type decision` | `recall` (filtered) |
| Record an episode | `anneal-memory record "Chose X because Y" --type decision` | `record` |
| Record multi-line | `echo "…" \| anneal-memory record - --type observation` | `record` |
| Prepare a wrap | `anneal-memory prepare-wrap` | `prepare_wrap` |
| Save the wrap | `anneal-memory save-continuity continuity.md` | `save_continuity` |
| Save with affect | `anneal-memory save-continuity continuity.md --affect-tag engaged --affect-intensity 0.8` | `save_continuity` (`affective_state`) |
| Plant a spore | `anneal-memory spore add --type task --text "…"` | `spore_add` |
| List / surface spores | `anneal-memory spore list` · `spore surface` | `spore_list` · `spore_surface` |
| Resolve a spore | `anneal-memory spore descend …` · `spore ascend …` | `spore_descend` · `spore_ascend` |
| Crystallized index / recall | `anneal-memory crystal index` · `crystal recall "<query>"` | `crystal_index` · `crystal_recall` |
| Crystallize a pattern out | `anneal-memory crystal crystallize …` | — (CLI / library) |
| Store status / health | `anneal-memory status` | `status` |
| Delete an episode | `anneal-memory delete <id> --force` | `delete_episode` |
| Recover a stuck wrap | `anneal-memory wrap-status` · `wrap-cancel` | `wrap_cancel` (inspect via `status`; `wrap-status` is CLI-only) |
If a wrap gets stuck — `prepare_wrap` ran but `save_continuity` never completed, so the store is locked `wrap_in_progress` — clear it with the `wrap_cancel` MCP tool or `anneal-memory wrap-cancel` (don't delete the store or force a duplicate wrap). **Over MCP, call `status` first:** it reports when the wrap started, and a wrap that began moments ago is probably a live peer session still compressing rather than a corpse — one server process runs per client session against a shared store, and cancelling throws away whatever that session has in flight. `anneal-memory wrap-status` shows the full snapshot but is CLI-only. A wrap prepared under the consolidate gate (`session_id`) can be completed only by that same `session_id`, and since 0.9.22 a cancel that names neither its token nor that session is refused; `wrap-status` shows which session prepared it. Ending another session's gated wrap discards its compression and is the operator's decision (`force`, documented on the tool).
- **If the wrap is yours, prove it instead of guessing.** Pass the `wrap_token` `prepare_wrap` gave you (`--wrap-token` on the CLI): the cancel then succeeds only if that wrap is still the one in progress, and is refused **without changing anything** if a peer replaced it or it already finished. The store compares inside the same transaction that clears, so a peer cannot swap the wrap in between — a real guard rather than advice. Omit the token to cancel whatever is current, which is what you want when clearing an ungated wrap you did **not** open (the original stuck-wrap case, where you have no token to offer); a gated wrap refuses that, as above. ⚠ Checking `status` is still the only thing available in that case: a lock or a timestamp cannot tell you who owns a wrap.
+ **If the wrap is yours, prove it instead of guessing.** Pass the `wrap_token` `prepare_wrap` gave you (`--wrap-token` on the CLI): the cancel then succeeds only if that wrap is still the one in progress, and is refused **without changing anything** if a peer replaced it or it already finished. The store compares inside the same transaction that clears, so a peer cannot swap the wrap in between — a real guard rather than advice. Omit the token to cancel whatever is current, which is what you want when clearing an ungated wrap you did **not** open (the original stuck-wrap case, where you have no token to offer); a gated wrap refuses that, as above, and so does a wrap opened with a token its preparer supplied (`prepare_wrap(wrap_token=...)`, since 0.9.30), which only that token or `force` ends. ⚠ Checking `status` is still the only thing available in that case: a lock or a timestamp cannot tell you who owns a wrap.
*(Before 0.9.8 this row read "— (CLI only)" and it was true: `wrap_cancel` had no MCP surface, so an agent that hit `WrapInProgressError` mid-session had no in-band way out. That is the defect 0.9.8 fixed.)*
**Operator-only** (CLI, things MCP can't do — inspection/debugging that needs filesystem or broad queries): `stats` · `graph --format dot` · `history` · `diff --wraps 5` · `audit --since 7d` · `verify` · `export` / `import` · `prune --older-than 90`. All support `--json`. (`status` is on *both* CLI and MCP — it's in the table above; only the rest are CLI-only.)
## Staying current
After you (or your operator) upgrade the `anneal-memory` package, run `anneal-memory migrate check` — it reports any change suggesting an edit to *these* instructions and proposes the edit, so your hand-tuned guidance doesn't silently drift from the substrate as it evolves. Apply what fits, then `anneal-memory migrate ack`. It only proposes; it never edits your files.
---
*This Skill is **depth-on-demand, not a replacement for the always-loaded lean snippet.** A model-invoked skill won't reliably fire at a bare session start, so keep `examples/agent-instructions.lean.example` (MCP) / `.lean.cli.example` (CLI) in your `CLAUDE.md`/`AGENTS.md` — it carries the baseline (start-of-session load, record, recall-before-decisions, wrap) even when this Skill isn't loaded; the Skill adds the depth on top. For the full immune-system internals, the affective-state JSON shape, the complete operator-command catalog, and framework-specific integration points, see the project README and `examples/agent-instructions.full.example` (MCP) / `.full.cli.example` (CLI).*