# Changelog

All notable changes to mneme are documented here. The format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the
project adheres to [Semantic Versioning](https://semver.org/) — see
[`book/src/versioning.md`](book/src/versioning.md) for the project's
pre-1.0 cadence and 1.0 gates.

Subsequent entries are auto-generated by `release-plz` from
Conventional Commits on the `main` branch; this seed entry summarises
the work that landed before automation was wired up.

## [0.2.6](https://github.com/tr0mb1r/mneme/compare/v0.2.5...v0.2.6) - 2026-05-07

Bug-fix release. Pre-1.0 PATCH (additive behaviour — no schema, API,
or config break; the `forget` tool's `inputSchema` is unchanged).

### Fixed

- **`forget(id=…)` now resolves IDs across L0 procedural and L3
  episodic, not just L4 semantic.** Previously the tool only reached
  `SemanticStore::forget`, so pin IDs and episodic event IDs both
  returned `no such memory`. That broke the agent-driven invalidation
  contract in ADR-0010 — agents had no way to clean up a polluted L3
  scope, and the only working point-delete was L4. The fix adds a
  resolution chain `L4 → L0 → L3` (first hit wins) inside the `forget`
  tool, plus a new `EpisodicStore::forget(id)` helper that walks hot
  then warm tier and reports a bool. Cold-archive entries stay out of
  reach by design — the cold quarter files are append-only and the
  180-day window is a deliberate privacy floor that pairs with the
  `record_event` opacity discipline. Verify by recording an event,
  noting its id, calling `forget(id=…)`, and confirming
  `recall_recent` no longer returns it.

### Documentation

- `book/src/mcp-surface.md` — `forget` row updated to reflect the
  cross-layer resolution and the cold-archive boundary.
- `docs/CLAUDE_CODE_SETUP.md` — clarified that `recall` accepts
  `scope` as a filter while `forget(id=…)` is scope-agnostic.
- `docs/examples/claude-code-config/CLAUDE.md` template — `forget`
  description spells out the resolution order and the cold-archive
  boundary.

## [0.2.5](https://github.com/tr0mb1r/mneme/compare/v0.2.4...v0.2.5) - 2026-05-01

Bug-fix + doc-drift sweep. Pre-1.0 PATCH (additive only — no
schema, API, or config break). One real bug fix; the rest closes
documentation drift surfaced by an audit of every user-facing
file against the actual source surface in v0.2.4.

### Fixed

- **`tags` writes from MCP clients that double-encode array
  arguments.** `remember`, `pin`, and `record_event` now tolerate
  `tags` arriving as a JSON-encoded string (e.g.
  `"tags": "[\"a\",\"b\"]"`) by parsing the string back to a
  `Vec<String>` and accepting the result. The strict array path
  (`"tags": ["a","b"]`) is still the documented contract — the
  `inputSchema` is unchanged — but the stringified shape is no
  longer rejected with `-32602`. Observed in some Claude Code
  releases whose MCP wrapper double-encodes array-typed tool args
  before forwarding the `tools/call` frame; without this fallback
  every tagged write from those clients would fail despite the
  client and the server both agreeing on the schema. Verified
  end-to-end against a fresh `mneme run` over stdio: real arrays
  succeed, stringified arrays succeed, malformed strings still
  reject. Tags-parsing is now factored into a single
  `mcp::tools::parse_tags_arg` helper used by all three tools so
  the rule cannot drift between them.

### Added

- **`book/src/cli.md` — CLI reference chapter.** Mirrors
  `book/src/mcp-surface.md`'s role for the eight CLI subcommands
  (`init`, `run`, `stats`, `inspect`, `export`, `stop`, `backup`,
  `restore`). Documents the lockfile contract explicitly: which
  subcommands acquire / refuse-while-held / read. Linked from
  `SUMMARY.md` under Reference. Closes a Phase 6 doc gap — the
  CLI surface was reachable only by grepping through prose
  scattered across `installation.md` / `claude-code-setup.md` /
  `configuration.md`.

### Changed

- Tags parsing factored into `src/mcp/tools/mod.rs::parse_tags_arg`;
  `remember`, `pin`, and `record_event` delegate. Three near-
  identical inline `match args.get("tags")` blocks collapsed.

### Documentation

- `README.md` and `book/src/intro.md` Status lines bumped
  0.2.3 → 0.2.4 (this commit also bumps to 0.2.5).
- `README.md` dropped "12-tool MCP-surface freeze" wording per
  the SOURCE-OF-TRUTH pin (counts live in `book/src/mcp-surface.md`,
  not in README); reconciled the "What works today" table with
  `book/src/intro.md` so the L1 row and `switch_scope` are no
  longer omitted.
- `book/src/configuration.md` broken cross-ref anchor
  `./mcp-surface.md#tools-12` → `./mcp-surface.md#session-helpers`
  (the heading was renamed when the surface grew to 13 tools).
- `book/src/versioning.md` 1.0 gate 5: replaced the "Twelve tools
  as of `0.2.0`" enumeration with a link to mcp-surface.md so the
  count doesn't have to be maintained in two places.
- `docs/examples/claude-code-config/CLAUDE.md` (transcluded into
  `book/src/claude-code-config.md` and copy-pasted into every
  user install): ADR-0010 → ADR-0007 on the "L3 events are not
  embedded" line. ADR-0010 is the agent-driven invalidation
  decision; ADR-0007 is the L3-stays-unembedded decision.
- `docs/MEMORY_LAYERS.md`:
  - Stub embedder dim 768 → 32 (matches `STUB_DIM` in
    `src/embed/stub.rs:34`; the 32-dim choice is deliberate).
  - Dropped the stale "`mneme://session/{id}` resource is still
    outstanding" paragraph; that resource shipped in v0.2.0.
  - Stripped internal phase-version markers (`v0.12`, `v0.13`,
    `v0.14`, `v0.15`) from the honesty note, the L1+L3 quick-ref
    rows, and the L1+L3 "What runs today" blocks. User-facing
    docs only need to say what runs today.
- `book/src/troubleshooting.md`: dropped "v0.10" phase reference
  (never shipped as a crate release) from the EISDIR-on-symlinks
  fix note; rewrote to describe the actual fix.
- `src/mcp/resources/mod.rs` module rustdoc: removed dangling
  `mneme://scopes` promise (resource was never built and isn't on
  the v1.0 / v1.1 roadmap; was a Phase 1 leftover comment).
- (Already on `main` from earlier in the cycle:) doc realignment
  with v0.2.4 + decoupling README from tool counts; CHANGELOG
  backfill of v0.2.3 + v0.2.4 entries (#18) and the doc-update
  discipline pin.

### Tests

- 11 new tests pinning the tags-parsing contract:
  - 7 unit tests in `tools::tests` covering every input shape
    (None, Null, valid array, invalid array, valid stringified,
    empty stringified, unparseable string, non-array non-string).
  - `harness_double_encoded_tags_are_accepted` regression on each
    of `remember` / `pin` / `record_event`.
  - `pin.rs` previously had no in-file tests; this release adds a
    fixture and two tests there for the first time.
- Lib tests 312 → 323. `scripts/manual_test.sh --stub` still
  34/34 green.

## [0.2.4](https://github.com/tr0mb1r/mneme/compare/v0.2.3...v0.2.4) - 2026-05-01

Closes the L1 + L3 conversation-content gap that left those layers
tool-name-only through v0.2.3. Five architecture decisions ratified
(ADR-0007 through ADR-0011) and the v0.2.4 implementation lands the
agent-driven half. Pre-1.0 PATCH (additive only — no schema, API,
or config break; `EpisodicEvent` schema unchanged per ADR-0011).

### Added

- **`mneme.record_event` MCP tool (ADR-0008).** Unified agent-driven
  L3 producer:
  `record_event(kind, payload, tags?, scope?, retrieval_weight?)`.
  Free-form `kind: String` matches the existing `EpisodicEvent`
  schema (no enum); canonical kinds documented in the tool
  description: `user_message`, `assistant_message`, `decision`,
  `problem`, `resolution`, `milestone`, `preference`, `pivot`,
  `observation`, `summary`. Agents can invent new kinds without a
  schema migration. For `kind ∈ {user_message, assistant_message}`
  the server pushes a matching turn to `ActiveSession` (L1 mirror)
  — one tool call, both layers populated. **MCP surface 12 → 13
  tools.**
- **Enriched `tool_call` auto-emit payload (ADR-0009).** Every
  successful `tools/call` now includes the per-tool value-bearing
  arg in payload: `remember` / `pin` get `content`, `recall` gets
  `query`, `forget` / `update` get `id`, `switch_scope` gets
  `new_scope`. Diagnostic tools (`stats`, `list_scopes`, `export`)
  keep just `{tool: <name>}`. The full `arguments` object is
  **never** mirrored — explicit privacy invariant. New tools added
  to the MCP surface MUST add an extraction entry in the same PR.
  Closes the v0.2.3 gap where payloads were content-free.
- **Lifecycle auto-emits (ADR-0009).** Three new server-side events
  fire automatically with no agent action required:
  - `session_start` on `mneme run` boot — `{session_id,
    started_at, embedder, embed_dim, version, protocol}`.
  - `session_end` on graceful shutdown — `{session_id, ended_at,
    clean_shutdown, turns_total, checkpoints_total}`.
  - `tool_call_failed` on dispatch error — `{tool, error_kind,
    message}` (message capped at 500 chars). Mirrors the
    success-path emit but does NOT push a turn to L1 (failures
    don't count as turns; same logic that protected `turns_total`
    pre-v0.2.4).

### Changed

- **`Server::with_session(...)` signature** extended internally to
  take `Arc<EpisodicStore>` and `Arc<ScopeState>` so the L3
  producer + scope-aware emit have the handles they need.
  Production wiring lives in `cli::run::async_main`. Public MCP
  tool surface unchanged.
- **`docs/examples/claude-code-hooks/{stop,precompact}.sh`**
  rewritten to walk the consolidation workflow:
  `summarize_session` → `record_event(kind="summary")` →
  `remember` for durable facts → `pin` for binding rules. The
  `Stop` nudge now also recommends per-turn
  `record_event(kind="user_message"/"assistant_message")`.
- **`scripts/manual_test.sh`** strengthened from 26 → 34 checks:
  drives `record_event` end-to-end with `user_message`,
  `assistant_message`, `decision`, and an out-of-range
  `retrieval_weight`; asserts the privacy invariant on `tool_call`
  payloads (content captured, args.tags omitted); asserts
  `session_start` and `tool_call_failed` events appear in L3.

### Architecture decisions

Five ADRs ratified 2026-05-01 (`proj_docs/decisions/`):

- **ADR-0007** — L3 stays unembedded; similarity remains L4's
  exclusive job. `EpisodicStore::record_*` does not call the
  embedder; there is exactly one HNSW index in the system, owned
  by L4.
- **ADR-0008** — `record_event` tool ships in v0.2.4 (agent-driven
  conversation capture); v1.1 adds a Unix-domain-socket daemon
  control channel at `~/.mneme/control.sock` so Claude Code's
  `Stop` hook can write deterministically.
- **ADR-0009** — server-side auto-emit policy: enriched `tool_call`
  payload + lifecycle events; full `arguments` object never
  mirrored.
- **ADR-0010** — L3 → L4 invalidation is agent-driven; mneme
  provides `update` / `forget` / `recall` primitives but does no
  contradiction detection (cardinal rule #3: no LLM in the
  binary).
- **ADR-0011** — cross-reference graph (`references`,
  `amended_by`) deferred to v1.1 schema migration v1→v2; v1.0
  freezes `EpisodicEvent` at the current shape so the 30-day soak
  clock isn't restarted.

### Documentation

- New `proj_docs/mneme-memory-architecture.md` v1.1 — incorporates
  the five ADRs; layer responsibility matrix added; consolidation
  workflow (`summarize_session` × `record_event`) documented as
  §3.3.8.
- `proj_docs/mneme-implementation-plan.md` v0.21 — Phase 4 status
  grew six rows for the v0.2.4 deliverables; §9.1 Track A daemon
  channel promoted from "highest provisional priority" to explicit
  v1.1 commitment.
- `book/src/mcp-surface.md` — documents `record_event` as the 13th
  tool with full canonical-kinds and server-emitted-kinds tables.
- `docs/MEMORY_LAYERS.md` — quick-reference table updated to
  reflect the v0.2.4 producer reality (auto-emit + agent-driven
  via `record_event`).
- `proj_docs/homebrew-release.md` (added in v0.2.3 cycle) —
  manual procedure for bumping the `tr0mb1r/homebrew-mneme`
  formula on each tag.

### Tests

- Lib tests 297 → 312 (+15): 11 unit tests for `record_event`
  (kind validation, payload encoding, message-kind L1 mirror,
  scope fallback, weight bounds, descriptor shape); 4 server-level
  tests covering payload enrichment, the failed-call emit path, the
  per-tool extraction table (catches drift if a new tool forgets
  to add an entry), and message truncation.
- `tests/hook_scripts.rs` updated to assert `record_event`
  references in the precompact + stop nudges and to assert the
  message kinds (`user_message`, `assistant_message`) appear in
  the stop nudge.
- `tests/e2e_mcp.rs` updated for the 13-tool surface.

## [0.2.3](https://github.com/tr0mb1r/mneme/compare/v0.2.2...v0.2.3) - 2026-04-29

Phase 4 / Phase 6 honesty corrections — closes two real
"shipped-but-not-actually-running" gaps surfaced by re-auditing
the implementation plan against source. Pre-1.0 PATCH (additive
only — no schema, API, or config break).

### Fixed

- **L3 episodic producer wired in production
  (Phase 4 honesty correction).** The episodic store, ranking,
  hot/warm/cold tiers, and consolidation scheduler all shipped
  through v0.2.2, but **no production code path called
  `EpisodicStore::record(...)`** — every caller lived inside
  `#[cfg(test)]`. The hot tier was permanently empty in real
  sessions; `recall_recent` returned `[]`; `summarize_session` had
  nothing to fold. Closed by emitting one `EpisodicEvent { kind:
  "tool_call", payload: {"tool": <name>} }` from
  `Server::handle_tools_call` after each successful invoke, scoped
  to `ScopeState::current()`. Failed calls do not emit (same logic
  that protects the L1 turn counter). Emit failures log a warning
  and proceed rather than failing the request. Same shape as the
  v0.11 honesty corrections that caught the L3 consolidation
  scheduler and L1 checkpoint scheduler gaps; this is the third
  in that family.
- **`mneme export` CLI replaced its 7-line stub.** The CLI
  subcommand had been a `println!("not yet implemented — Phase 6")`
  stub since Phase 0. The MCP `export` *tool* shipped in v0.6 but
  the CLI body was never replaced. Now real: opens redb directly
  (no server needed), scans `mem:` / `epi:` / procedural JSONL,
  optional `--scope` filter, supports `--format json` (default,
  pretty-printed) and `--format ndjson` (one row per line, each
  tagged with `layer`). Refuses while `~/.mneme/.lock` is held —
  same lockfile contract as `mneme inspect`.

### Changed

- **`Server::with_session(...)` signature** extended internally to
  take `Arc<EpisodicStore>` + `Arc<ScopeState>` so the new emit
  path has the handles it needs. Public tool surface unchanged.
- **`scripts/manual_test.sh`** strengthened from "any array" to
  "n ≥ 1" so a producer regression fails CI; tool-list expectation
  fixed (was missing `switch_scope` since v0.15) — now 26/26
  checks pass against the v0.2.3 binary.

### Documentation

- `proj_docs/mneme-implementation-plan.md` v0.20 — added Phase 4
  honesty correction for the L3 producer gap (matching the v0.11
  pattern); flagged the export-CLI stub.
- `docs/MEMORY_LAYERS.md` — L3 row in the quick-reference table
  now correctly says "Producer wired (v0.2.3)"; the "What runs
  today" block is no longer aspirational.
- `README.md` and `book/src/intro.md` Status mention bumped to
  0.2.3.

### Tests

- Lib tests 293 → 297: two server-level regression tests pin the
  producer contract (success emits one `tool_call` event, failure
  does not); two `cli::export::tests` tests pin the new CLI data
  path (per-layer collection + scope filter).

## [0.2.2](https://github.com/tr0mb1r/mneme/compare/v0.2.1...v0.2.2) - 2026-04-29

### Documentation

- *(install)* document Homebrew + cargo install paths
- *(book)* trim claude-code-config permissions to mneme-only
- *(book)* add Claude Code config kit page

## [0.2.1] - 2026-04-29

First release published under the `mneme-mcp` crate name on crates.io.
No code changes vs. `0.2.0`; this version exists to anchor the rename
so future `cargo install mneme-mcp` users land on the renamed crate.

### Changed

- **Crate renamed on crates.io: `mneme` → `mneme-mcp`.** The bare
  `mneme` name is held by an unrelated event-sourcing library
  ([github.com/jwilger/mneme](https://github.com/jwilger/mneme), last
  published 2025-03-14). To unblock `cargo install` post-1.0 without
  contesting the name, we publish under `mneme-mcp`. The binary,
  library, project name, data directory (`~/.mneme/`), MCP server
  identity, repo URL, and all CLI commands are unchanged — only the
  crates.io package identifier moved. From the user's perspective:
  `mneme run` / `mneme stats` / etc. work exactly as before; only
  `cargo install mneme-mcp` (when crates.io publishing is eventually
  enabled post-1.0) replaces the hypothetical `cargo install mneme`.

## [0.2.0] - 2026-04-29

Phase 6 (polish + release) baseline. Code-side feature work for 1.0
is complete; remaining gates are operational (30-day soak, Homebrew
formula, public release pipeline).

### Added

- **L4 semantic store** — `remember`, `recall`, `update`, `forget`
  tools backed by redb + WAL + an `instant-distance` HNSW index;
  atomic re-embedding on `update` via `WalOp::VectorReplace` +
  `HnswIndex::replace`.
- **L3 episodic store** — `recall_recent` and `summarize_session`
  tools backed by a redb hot tier and zstd-compressed cold quarters;
  consolidation scheduler promotes hot → warm → cold on idle.
- **L1 working session** — checkpoint scheduler (wall-clock + turn-
  count triggers), `mneme://session/{id}` resource, fold-in into
  `Orchestrator::build_context` so working turns participate in
  auto-context.
- **L0 procedural** — `pin` / `unpin` tools backed by a JSONL file
  with hot-reload via `notify`; `mneme://procedural` resource.
- **Auto-context** — `mneme://context` resource assembles all four
  layers under a token budget with per-layer reservations.
- **Diagnostics** — `stats`, `list_scopes`, `export` tools and
  `mneme://stats` resource with consolidation + working observability
  blocks.
- **Scoping** — `switch_scope` tool plus process-lifetime
  `ScopeState`; write tools fall back to the live scope when the
  caller omits one, filter tools treat omission as no-filter.
- **Embeddings** — BGE-M3 (default, ~1.5 GB) and MiniLM-L6 (~80 MB)
  via `candle`; automatic re-embed migration when
  `config.embeddings.model` changes; `pytorch_model.bin` weight-file
  fallback for BGE-M3; dim-mismatch tolerance during WAL replay.
- **Backup / restore** — `mneme backup` and `mneme restore`
  subcommands with full round-trip; covered by
  `cli::restore::tests::backup_then_restore_preserves_data`.
- **Schema migration** — v0 → v1 path with realistic-fixture test
  (`migrate::tests::migrates_realistic_v0_install_to_v1`).
- **CLI** — real `mneme stats` and `mneme inspect` (previously stubs
  through v0.9), `scripts/install.sh` from-source installer with
  `--minilm` and `--prefix` flags.
- **MCP** — JSON-RPC over stdio against protocol `2025-06-18`,
  12 tools, 5 resources; survives malformed JSON, oversize frames,
  and EOF cleanly.
- **Documentation** — `docs/CLAUDE_CODE_SETUP.md`,
  `docs/MEMORY_LAYERS.md`, mdBook scaffold under `book/`,
  Claude Code lifecycle hook examples under
  `docs/examples/claude-code-hooks/` with regression coverage in
  `tests/hook_scripts.rs`.
- **CI** — cross-build matrix (5 targets), `cargo-deny`,
  `cargo-audit`, GitHub Pages publication for the mdBook.
- **Release infrastructure** — `release.yml` (tag-triggered
  cross-build + GitHub Release), `release-plz.yml` +
  `release-plz.toml` (auto-bump release PRs, CHANGELOG generation).

### Fixed

- `mneme backup` `EISDIR` on symlink-to-dir entries — walked with
  `symlink_metadata` so symlinks land in the archive as symlinks.
- Windows CI timing-budget block — gated the `Duration` import and
  dropped the timing assertion on Windows; correctness checks intact.
- Field-schema unification across all three memory layers (canonical
  `kind` + `created_at` + `tags` + `scope`); `PinnedItem` accepts
  `#[serde(alias = "added_at")]` for one release.

### Documentation

- Issue and PR templates added under `.github/`.
- `CONTRIBUTING.md` durability-path policy made concrete:
  `scripts/manual_test.sh --stub` is the floor for any change
  touching WAL / redb / HNSW / backup-restore.

[0.2.1]: https://github.com/tr0mb1r/mneme/releases/tag/v0.2.1
[0.2.0]: https://github.com/tr0mb1r/mneme/releases/tag/v0.2.0
