# ivar memory
> Memory commands, the memory_* MCP tools, page kinds and front matter, and the memory doctor findings.
Source: https://ivar.run/docs/reference/memory

`ivar memory` inspects, queries and maintains the hall memory: markdown pages
under `docs/memory/`, the handoffs between sessions and the session hooks. The
pages are the durable memory; `.ivar/memory.db` holds an index of them that a
reindex rebuilds, plus observations, handoffs and messages, which are lost when
the file is deleted.

Every subcommand accepts the global options:

| Option | Description |
| --- | --- |
| `--json` | Emit machine-readable output |
| `--color <COLOR>` | `auto` (default), `always` or `never` |
| `-C <dir>` | Run as if `ivar` was started in `<dir>`, like `git -C`; repeated `-C` options compose |

`--compact` is for `ivar graph` only; a memory verb refuses it with exit 2.

## Commands

### status

```sh
ivar memory status
```

Show the memory database: path, schema version, application id, size,
observation counts per event, the capture spool, the last drain and the
retention sweep. It also reports pages per kind, the last reindex, pages that
failed validation, the last consolidation of session pages, pages waiting for
anchors and 30 days of query usage with the miss rate per source. It creates
`.ivar/memory.db` when it is missing and drains the capture spool.

### clean

```sh
ivar memory clean
```

Delete the memory database after confirmation. Nothing else deletes it. The
pages under `docs/memory/` stay; observations, handoffs and messages go.

### query

```sh
ivar memory query "page index" --kind decision --limit 5
```

Search the pages under `docs/memory/`. When no page matches and no filter is
set, retained observations answer in a separate section.

| Argument / option | Description |
| --- | --- |
| `<TEXT>` | Words to search for |
| `--kind <KIND>` | Keep only pages of this kind (repeatable) |
| `--tier <TIER>` | Keep only pages of this tier: `semantic`, `procedural`, `episodic` or `working` |
| `--tag <TAG>` | Keep only pages carrying this exact tag |
| `--limit <LIMIT>` | Most results to return, default `10`, at most `20` |
| `--include-expired` | Also return pages whose `expires_at` has passed |
| `--explain` | Show how each source ranked every result |

Text is quoted term by term, so quotes, `AND`, `*` and `NEAR` never raise a
syntax error. Each hit lists up to 10 anchors (`repo`, `path`, `symbol`,
`broken`), broken ones first.

### reindex

```sh
ivar memory reindex
```

Rebuild the page index from `docs/memory/`. Observations are not touched. A
reindex also runs before `memory query` and `memory status`, in `ivar sync`
and on the MCP leader tick, so an uncommitted page appears at once.

### lint

```sh
ivar memory lint
```

Check the pages under `docs/memory/` against the page rules. It reads the
files, not the index, so it covers uncommitted pages. It exits non-zero when a
rule is broken.

| Code | When |
| --- | --- |
| `lint.kind_unknown` | A directory under `docs/memory/` that is not a durable kind |
| `lint.frontmatter_invalid` | Unreadable YAML, a field of the wrong type, a `kind` that differs from the directory, or a malformed `expires_at` |
| `lint.field_unknown` | A field outside the six front matter fields |
| `lint.link_broken` | A markdown link to a missing `.md` under `docs/memory/` |
| `lint.misplaced` | A subfolder, a slug that is not kebab-case ASCII, or a `product` or `update` page without `NNN-` |
| `lint.anchor_broken` | An anchor whose file or symbol the graph no longer has (warning) |
| `lint.reindex_failed` | The index refresh before the anchor check failed (warning) |

`lint.anchor_broken` and `lint.reindex_failed` are lint issues of warning severity, reported in
the output; they are not CLI warnings, so they do not change the exit code.

### review

```sh
ivar memory review billing-currency
```

Show what a feature wrote to memory, its gaps, the pages its diff may have made
stale, and the last review. It is read-only: the review that
`ivar feature deliver` requires is confirmed with the `memory_review` MCP tool.

| Argument | Description |
| --- | --- |
| `[FEATURE]` | Feature to review; defaults to the session's feature |

### handoff begin

```sh
ivar memory handoff begin --summary "Index rebuilt; lint still fails" --pending "Fix lint.link_broken"
```

Open a handoff for the next session, in this session's feature queue or the
hall's outside a feature.

| Option | Description |
| --- | --- |
| `--summary <SUMMARY>` | Where the work stopped, in markdown (required) |
| `--pending <PENDING>` | A task the next session should pick up (repeatable) |
| `--decision <DECISION>` | A decision still open (repeatable) |
| `--file <FILE>` | A file the work touched (repeatable) |
| `--feature <FEATURE>` | Open it in this feature's queue instead |
| `--hall` | Open it in the hall queue |
| `--to-provider <TO_PROVIDER>` | Only sessions of this provider see it |
| `--to-session <TO_SESSION>` | Only this `ivar` session sees it |
| `--session <SESSION>` | Open it on behalf of this `ivar` session, which becomes its owner |

### handoff list

```sh
ivar memory handoff list --hall
```

List open handoffs. Read-only; it never creates `memory.db`.

| Option | Description |
| --- | --- |
| `--feature <FEATURE>` | Only this feature's queue |
| `--hall` | Only the hall queue |

### handoff cancel

```sh
ivar memory handoff cancel 7f3a9c
```

Cancel an open handoff by `<ID>`, as `list` shows it, whoever owns it. Accepting
a handoff and every message verb are MCP-only.

### capture

```sh
ivar memory capture --provider claude-code
```

A hook entry point: it records one session hook event, read from stdin, into
the capture spool. It always exits 0 and prints nothing. Without `--provider`
nothing is recorded.

### brief

```sh
ivar memory brief --provider claude-code
```

A hook entry point for session start: it prints up to five open handoff headers
and the count of pending messages this session can see, framed as untrusted
historical data. It always exits 0 and prints nothing when there is nothing to
show or on any error. `--provider` names the provider whose hook runs it, for a
session outside `ivar session`.

## MCP tools

The memory tools are served by the same MCP server as the graph tools. None
takes a workspace or project argument: the hall is the scope. The memory tools
never commit; a written page stays uncommitted until you commit it.

| Tool | Arguments | What it returns |
| --- | --- | --- |
| `memory_query` | `query` (required), `kind[]`, `tier`, `tag`, `limit`, `include_expired` | Pages with title, kind, tier, path, excerpt, `pinned` and up to 10 anchors; observations only as a fallback |
| `memory_read_page` | `path` | Front matter and body of one page, `.ivar/memory/` included; refuses files over 256 KiB |
| `memory_write_page` | `kind`, `title` (required), `body`, `slug`, `tags`, `pinned`, `relations`, `expires_at`, `replace` | `path`, `hash`, `indexed` and `similar` titles; refuses `session` |
| `memory_patch_page` | `path` (required), `old`, `new`, `tags`, `pinned`, `relations`, `expires_at` | `path`, `hash`, `indexed`; only `pinned` and `expires_at` on a `session` page |
| `memory_delete_page` | `path` | `path`, `indexed` |
| `memory_lint` | none | `issues`, each `{path, code, severity, message}` |
| `memory_review` | `confirm`, `stale[{path, verdict}]`, `not_required` | Written pages, gaps, stale pages and the last review; with `confirm: true` it records the review delivery requires |
| `memory_status` | none | The `ivar memory status` block, pending anchors and 30-day usage |
| `memory_recent` | `limit`, `source_event`, `feature` | Observations, newest first, each with a one-line summary |
| `memory_read_session_observations` | `session` (required), `limit`, `cursor` | One session's observations, oldest first, with `next_cursor` |
| `memory_handoff_begin` | `summary` (required), `pending_tasks[]`, `open_decisions[]`, `modified_files[]`, `feature` or `hall`, `to` | `handoff_id`, `expires_at` |
| `memory_handoff_list` | `feature` or `hall`, `limit` | Open handoffs the caller sees, newest first |
| `memory_handoff_accept` | `handoff_id` | `claimed` with the handoff, `already_claimed` or `none_pending` |
| `memory_handoff_cancel` | `handoff_id` (required) | `cancelled`; only the owner cancels, and only an open handoff |
| `memory_message_send` | `body` (required), `subject`, `feature` or `hall`, `to` | `message_id`, `expires_at` |
| `memory_message_list` | `box` (`inbox` or `outbox`), `feature` or `hall`, `limit` | Pending messages, oldest first |
| `memory_message_pop` | `message_id` | `popped` with the message, `already_claimed` or `none_pending` |
| `memory_message_cancel` | `message_id` | How many of the caller's pending messages were retracted |

`accept` and `pop` claim an item once; only the winner gets the content, and
every answer that returns content marks it as untrusted data. Handoffs and
messages expire after `memory.handoff_ttl_days` days, 7 by default. Before
`ivar feature deliver`, call `memory_review` and give each stale page a verdict;
delivery refuses a feature without that review unless you pass
`--skip-memory-review` with a reason.

The full argument rules for writing, path confinement, review, handoffs and
messages are in the
[MCP tools reference](https://github.com/mnzsss/ivar/blob/main/docs/reference/mcp-tools.md)
of the `ivar` repository.

## Pages

A page is a markdown file at `<root>/<kind>/<slug>.md`, with a kebab-case
slug. Pages are flat: a file in a subfolder, outside a kind directory or with an
uppercase extension is reported, not indexed. Nothing creates a kind
directory ahead of time: the first page written there creates it. `ivar` never
commits a page.

| Kind | Directory | Tier | Numbered |
| --- | --- | --- | --- |
| `product` | `docs/memory/product/` | semantic | yes |
| `decision` | `docs/memory/decision/` | semantic | no |
| `concept` | `docs/memory/concept/` | semantic | no |
| `gotcha` | `docs/memory/gotcha/` | procedural | no |
| `procedure` | `docs/memory/procedure/` | procedural | no |
| `update` | `docs/memory/update/` | episodic | yes |
| `session` | `.ivar/memory/session/` | episodic | no |

The six durable kinds are committed. `session` pages are not: `ivar` writes one
per provider conversation once it ends or has been idle 30 minutes, and deletes
it 30 days after the conversation's last observation unless it is `pinned`.

`product` and `update` slugs start with a three-digit `NNN-` prefix. The next
number is the highest one in the directory plus one, counting the
`.cleanup.json` records beside the `update` pages.

### Front matter

Front matter is optional, and so is every field. A page without it takes its
title from the first H1, then from the slug.

| Field | Type | Meaning |
| --- | --- | --- |
| `title` | string | Page title |
| `kind` | string | Must equal the directory |
| `tags` | list of strings | Matched exactly by `--tag` |
| `pinned` | boolean | Ranks the page higher in `memory query` |
| `relations` | list of `{type, target}` | Typed links; `target` is `kind/slug` |
| `expires_at` | string | `YYYY-MM-DDTHH:MM:SSZ`, UTC only |

```yaml
---
title: Page index follows the disk
tags: [memory, index]
relations:
  - type: supersedes
    target: decision/old-index
expires_at: 2027-01-31T00:00:00Z
---
```

Any other key is kept and reported as a warning. A page past `expires_at` stays
on disk; `memory query` omits it unless `--include-expired` is passed. The body
links to other pages with `[[slug]]`, `[[kind/slug]]` and markdown links to
`.md` files.

## Doctor findings

`ivar doctor` reports these memory findings; each names the command that fixes
it.

| Code | Meaning | Fix |
| --- | --- | --- |
| `memory.db_missing` | `.ivar/graph.db` exists without `.ivar/memory.db` | `ivar memory status` creates it |
| `memory.db_corrupt` | `.ivar/memory.db` fails to open, fails `quick_check`, or carries another `application_id` | `ivar memory clean`, then `ivar memory status` |
| `memory.schema_newer` | Memory schema newer than the binary | `ivar upgrade` |
| `memory.db_polluted` | An older `ivar` created graph tables inside `.ivar/memory.db` | Stop the older `ivar`, then `ivar memory status` repairs it |
| `memory.split_busy` | A legacy graph at `.ivar/memory.db` is open in another process, so it was not moved | Stop sessions and MCP servers of the older `ivar`, then `ivar doctor` |
| `memory.split_failed` | Moving the legacy graph failed for another reason | Check `.ivar/` permissions and free space, then `ivar doctor` |
| `memory.spool_orphan` | A capture spool file has waited over an hour to be drained | `ivar memory status` drains it |
| `memory.spool_dropped` | A session spool reached 8 MiB and dropped events within the last 7 days | `ivar memory status`, or keep `ivar graph mcp` running; clears after 7 days without new drops |
| `memory.drain_failed` | The last drain of the capture spool failed | `ivar memory status`; if it repeats, read the database findings of `ivar doctor` |
| `memory.sweep_failed` | The last retention sweep failed | `ivar memory status`; if it repeats, read the database findings of `ivar doctor` |
| `memory.consolidate_failed` | The last consolidation of session pages failed | `ivar memory status`; if it repeats, read the database findings of `ivar doctor` |
| `memory.retention_overdue` | Session observations older than 37 days are still stored | Keep `ivar graph mcp` running; its leader deletes them hourly |
| `provider.capture_hook_missing` | `memory.capture` is on and a provider has no capture hook | `ivar sync`, or set `memory.capture` to false in `ivar.json` |
| `memory.legacy_docs` | Pages still live in `docs/product` or `docs/updates` | The `git mv` commands it prints |
| `memory.page_invalid` | Front matter does not parse, `kind` differs from the directory, or `expires_at` is malformed | Edit the page |
| `memory.page_kind_unknown` | A directory under `docs/memory/` or `.ivar/memory/` is not a kind of that root | Rename or move it |
| `memory.page_misplaced` | A page is outside `<kind>/`, nested, has a non-kebab slug, does not end in lowercase `.md`, or lacks `NNN-` | Move or rename it |
| `memory.page_field_unknown` | Front matter has a key the format does not know (warning) | Remove it or ignore it |
| `memory.page_uncommitted` | Page files under `docs/memory/` have uncommitted changes | Commit them |
| `memory.index_failed` | The last page reindex failed | `ivar memory reindex` |
