# Hall memory
> What ivar records from each session, the pages your agent writes under docs/memory, how it finds them again, the review before delivery, and handoffs between sessions.
Source: https://ivar.run/docs/guide/memory

import { Callout } from 'fumadocs-ui/components/callout';

The hall remembers in two layers. `ivar` records what happens in each session
into `.ivar/memory.db`, a local SQLite file. Your agent writes what is worth
keeping as markdown pages under `docs/memory/`, which you commit with the hall.
The same MCP server that serves the [code graph](/docs/guide/graph) serves both
to the agent. Nothing leaves the machine.

## What gets recorded

Each session records four events: `session.started`, `session.prompted`,
`session.tool` and `session.ended`. A provider hook runs
`ivar memory capture`, which redacts known secret patterns and the values of
the hall's `.env` before the event reaches disk, then appends one line to a
spool under `.ivar/spool/memory/`. The `ivar graph mcp` leader drains the spool
into `.ivar/memory.db` within about a quarter of a second. The hook never opens
the database and always exits 0, so it cannot block a prompt.

A provider records only the events its hooks expose:

- OpenCode has no session end, so no `session.ended` is recorded for it.
- T3 records the session end when you archive the thread, not when a turn
  finishes. An idle open thread has no end yet.

The binary also records delivery events into the same database: push, pull
request, rebase, integrate, merge, cleanup, close and delete. Pages written
through the memory tools and each memory review are recorded too.

Capture is on by default. To turn it off, set the key in `ivar.json` and
rewrite the provider hooks:

```json title="ivar.json"
{
  "memory": { "capture": false }
}
```

```sh
ivar sync
```

The `ivar graph mcp` leader deletes session events 30 days after the session's
last observation, at start and then every hour. Delivery, memory, handoff and
message events are kept. `ivar memory status` shows the counts per event, the
spool, the last drain and the retention sweep.

## Pages

A page is a markdown file at `docs/memory/<kind>/<slug>.md`. The durable kinds
are `product`, `update`, `decision`, `gotcha`, `procedure` and `concept`.
`product` and `update` pages carry an `NNN-` number prefix. Pages are files in
the hall, so they are committed, reviewed and shared like any other file.

The memory tools write files and never commit. A page your agent writes stays
uncommitted until you commit it, and `ivar doctor` reports it as
`memory.page_uncommitted` until then.

`ivar` also writes one `session` page per provider conversation under
`.ivar/memory/session/`, from the template and its observations, with no model
call. A conversation gets its page once it ends, after 30 minutes without
observations, or on `ivar session stop`. These pages are not committed and
expire 30 days after the conversation's last observation; `pinned: true` keeps
one longer.

`.ivar/memory.db` holds an index of the pages. It follows the files on disk,
committed or not, and refreshes on `ivar memory query`, `ivar memory status`,
`ivar sync` and every 5 seconds in the MCP leader. Rebuild it by hand, or check
every page against the page rules:

```sh
ivar memory reindex
ivar memory lint
```

`lint` exits non-zero when a page breaks a rule. The
[reference](/docs/reference/memory#pages) lists the front matter fields and the
rules.

## Finding them again

Your agent searches with the `memory_query` tool. From a terminal, the same
search runs as:

```sh
ivar memory query "release lanes" --kind decision --kind gotcha
```

`--kind`, `--tier` and `--tag` narrow the results, and `--limit` caps them
(10 by default, 20 at most). Expired pages stay hidden unless you pass
`--include-expired`.

A page cites code as anchors: the code spans and links in its body that
resolve to a file or symbol in the graph. Each hit lists its anchors, broken
ones first, so the agent sees which pages still point at real code.

`graph_explore` works the other way round. An answer ends with a
**Related memory** section that names up to 3 durable pages anchored to the
files it returns. The agent reading code learns that a decision or a gotcha
covers it, without asking.

When no page matches and no filter is set, the retained observations answer
in a separate section. A query with a filter never falls back.

## Writing during a feature, reviewing before delivery

During a feature, the agent writes a page once an insight is settled, not at
the end:

- a `decision` when an option is chosen, with the alternatives and why;
- a `gotcha` when a trap is found, with its symptom, cause and fix;
- a `product` page for what a delivered capability does today;
- an `update` page for what this delivery changed and what stayed open.

Before delivery, the agent calls `memory_review`. It returns three lists:

- **Written**: the pages written in the feature.
- **Gaps**: a failed tool with no `gotcha`, no `update` page, or an Analysis
  approved without a `memory_query`.
- **Stale**: durable pages anchored to files the feature's diff changed, or
  with a broken anchor.

The agent gives each stale page a verdict, `updated` or `still_valid`, and
confirms. The confirmation records the review. Gaps never block it.
`ivar memory review <feature>` prints the same report from a terminal and
records nothing.

`ivar feature deliver` refuses to apply without that review, with
`deliver.memory_not_reviewed`. When you have a reason to deliver without it,
say so:

```sh
ivar feature deliver <feature> --fingerprint <fp> --skip-memory-review "docs-only change"
```

The skip lifts only the memory blocker, records its reason, and never counts
for a later delivery. `ivar feature cleanup` warns when the review is missing
but does not refuse; like any warning, it makes the CLI exit 1.

<Callout title="Commit what the review wrote">
  A page updated after a stale verdict is a file in `docs/memory/` like any
  other. Commit it with the hall before you deliver.
</Callout>

## Handoffs and messages

A session can leave a handoff for the next one: a summary, pending tasks,
open decisions and changed files. The agent opens one with
`memory_handoff_begin`; from a terminal:

```sh
ivar memory handoff begin --summary "Lanes split; rollback guard left" --pending "Add the per-target guard"
ivar memory handoff list
ivar memory handoff cancel <id>
```

A handoff goes to the feature's queue, or to the hall's outside a feature. A
session sees its feature's queue and the hall's. Accepting a handoff is
MCP-only, and only one session wins the claim.

On Claude Code, T3 and OMP, `ivar memory brief` runs when a session starts and
shows up to five open handoffs and the count of pending messages, framed as
untrusted historical data. OpenCode has no brief because its plugins cannot
inject context at session start, so its agent calls `memory_handoff_list` and
`memory_message_list` instead.

Messages carry a note from one session to another, addressed to a provider or
a session, through `memory_message_send` and `memory_message_pop`. They are
MCP-only.

Handoffs and messages expire after `memory.handoff_ttl_days` days, 7 by
default:

```json title="ivar.json"
{
  "memory": { "handoff_ttl_days": 14 }
}
```

`ivar session stop` warns with `session.handoff_missing` when the session
worked and left no handoff. The stop still goes through.

## Limits

- Capture records only what a provider's hooks expose. OpenCode reports every
  tool as succeeded, so no OpenCode tool counts as a failure and the
  "failed tool without a gotcha" gap never fires for it.
- Redaction matches known secret patterns. A secret in a shape no pattern
  matches is stored as written.
- A session spool is capped at 8 MiB, which bounds the disk one session
  can fill while no drain runs; past the cap events are dropped and
  `ivar doctor` reports `memory.spool_dropped`.
- The retention sweep runs in the `ivar graph mcp` leader. A hall with no
  leader running never sweeps, and `ivar doctor` reports
  `memory.retention_overdue` after 37 days.
- A `session` page is regenerated whenever its conversation gets new
  observations, so hand edits to its body are lost.
- A stale page is found only through its anchors. A page that cites changed
  code in prose without an anchor is not listed.
- The MCP server runs outside the session write guard. The write tools confine
  themselves to `docs/memory/<kind>/`, and approving the server in Claude Code
  approves the write tools along with the read tools.
- An `ivar` that predates the `memory` keys of `ivar.json` cannot read a hall
  that sets them.

The rest of `ivar`'s limits are in [Limitations](/docs/reference/limitations).

Every `ivar memory` verb, every memory tool, the page front matter and the
doctor findings are in the [memory reference](/docs/reference/memory).
