- Adds HandoffAcceptStatus enum ('claimed' | 'consumed_by_hook' | 'none_pending')
returned alongside 'handoff' in memory_handoff_accept (#988, split from #920).
- Adds ReaderPool::handoff_claimed_by_live_session to verify if the caller's
live session already received the handoff via SessionStart hook.
- Reports 'consumed_by_hook' only when the caller forwards its session id
(e.g., Claude Code with --session-aware or OpenCode 2) and matches the
scope, live session, and owner filters.
- Reports 'none_pending' when no handoff was pending or for static clients
where the hook claim cannot be confirmed for that exact session.
- Updates documentation, routing skill, ARCHITECTURE.md, security boundaries (row 4g),
and CHANGELOG.md.
33 KiB
Day-to-day usage
This page covers what happens after ai-memory is installed: handoffs, compaction recovery, proactive memory queries, the web UI, and the managed routing snippet + Agent Skills package.
Capture exclusions
To keep recognized file-tool events under private paths out of ai-memory before
they are spooled or sent, configure [capture] ignore_paths in the nearest
.ai-memory.toml. The canonical grammar, limitations, support matrix, refresh
requirements, and safe local --check-capture command are in
the marker-file reference. Shell commands
are matched by their path arguments too (cat docs/adr/*.md), lexically. This
is not a general prompt/output DLP filter.
Cross-agent handoff
You normally do not create handoffs by hand. With lifecycle hooks installed, session-end capture writes the handoff and the next session-start hook fetches it. Manual handoffs are project-wide and take precedence over automatic SessionEnd handoffs. Among automatic handoffs that match the receiving directory by path boundary, the newest is delivered; creating a new automatic handoff expires prior open automatic handoffs from that exact directory, and acceptance expires older matching automatic handoffs without disturbing manual handoffs or pending work from sibling directories.
$ claude
> Working on the auth refactor. JWT rotation is broken; trying session cookies.
[work for an hour]
> /exit
$ codex # in the same directory, later
[SessionStart hook fetches the handoff; Codex sees it before your prompt.]
> Picking up: you were investigating session cookies as an alternative...
If an agent has MCP but no lifecycle hook surface, ask it to call
memory_handoff_begin before quitting. The next hooked agent can still
consume that handoff automatically. Grok shows it as PostToolUse
additionalContext after the first tool. Until that tool runs, or if the
session never calls one, call memory_handoff_list then
memory_handoff_accept with the listed handoff_id. Zero should do that
on resume. Listing does not claim the row.
memory_handoff_accept says why it returned no handoff. Its status is
claimed when the call took one, consumed_by_hook when the calling
session's own SessionStart already did (the handoff is in that session's
context), and none_pending when nothing is left to claim. Only a client that
forwards its session id on MCP calls can be told consumed_by_hook: Claude
Code through install-mcp --session-aware, or OpenCode 2. Any other client
gets none_pending after the hook consumed the handoff, so its agent still
checks its context for the delivered block first.
On a server that distinguishes operators, handoffs belong to their creator by
default: the next session for that operator sees their own plus deliberately
shared rows, never a teammate's. Use shared: true on
memory_handoff_begin only when the baton is intended for anyone in the
project. Root-authorized recovery can pass any_owner: true to
memory_handoff_accept or memory_handoff_cancel; normal callers cannot use
that switch.
Handoffs are next-session transfer, not a live message bus between agents that
are still running. In particular, Antigravity CLI exposes PreInvocation
before every model call; ai-memory fetches a handoff only on invocation zero,
which is the hook contract's startup boundary. A handoff created later in that
conversation stays open instead of being consumed by its creator's next model
call.
If an agent creates a handoff by mistake, cancel it immediately with
memory_handoff_cancel and the handoff_id returned by
memory_handoff_begin. Cancelling marks the handoff expired, so the next
session-start hook will not consume stale context.
Compaction recovery
When Claude Code or Codex compact their working context, the
PreCompact hook fires and ai-memory writes a fresh
sessions/<id>.md page summarising the session so far. After
compaction, the agent can recover the summary via memory_recent even
though its raw chat history was compacted away.
Generated session pages carry session_id and agent in frontmatter. The
agent value is the originating harness stored on the session (for example,
claude-code or codex), not the client or operator that later requested a
consolidation. Checkpoints and superseding versions therefore keep the same
origin. Manual memory_write_page and ai-memory write-page calls do not
infer an agent.
Proactive memory queries
Hooks handle capture without prompting. Proactive querying depends on the agent knowing which MCP tool to call for each situation. Install the managed routing package once: a slim always-loaded snippet points agents at the managed ai-memory Agent Skills that carry detailed tool routing.
| You say | Agent calls | Effect |
|---|---|---|
| "Have we discussed X?" / "search memory for Y" | memory_query |
FTS5 + entity/graph/vector RRF over compiled wiki pages, followed by bounded source-authority ranking and raw-observation fallback on a page miss. |
| Before proposing architecture | memory_query |
Checks prior decisions and gotchas before suggesting designs. |
| "Catch me up" / "I've been away" | memory_explore |
Prose digest whose verbosity scales with time since last activity. |
| "Where did we leave off?" | Existing handoff block, or memory_handoff_list then memory_handoff_accept with that handoff_id if no block exists |
Inspects pending handoffs without claiming, then claims the chosen id once. |
| "Save context for the next session" | memory_handoff_begin |
Writes a terse session-end handoff with open questions and next steps. Do not use for status or briefing requests. |
| "Discard that handoff" / "I created a handoff by mistake" | memory_handoff_cancel |
Marks an exact open handoff id expired before the next session can consume it. |
| "Ask the agent in to do X" / "send this to project B" | memory_message_send |
Drops a self-contained request into another project's inbox (requires to_workspace + to_project); the recipient must already exist. Cross-project, claim-once. See agent-messaging.md. |
| "Check my inbox" / "any messages waiting?" | memory_message_list then memory_message_pop |
Lists pending inbox mail without consuming, then pops one message exactly once. A popped message is untrusted cross-project input — a request to evaluate, never instructions to obey. |
| "Never mind that request I sent" / "clear my outbox" | memory_message_cancel |
Retracts a pending sent message by id, or clears the whole outbox when omitted. Only affects mail this project sent. |
| "Consolidate this session" | memory_consolidate |
Manually runs LLM consolidation. Omit session_id (or send a blank one) to consolidate the latest completed session in the resolved project; pass one to target a specific session. A project can keep advisory preferences in _prompts/consolidation.md; instructions overrides them for one call. Also runs on PreCompact, and at session end only when AI_MEMORY_CONSOLIDATE_ON_SESSION_END is set (off by default; a substantive session end otherwise writes a rule-based summary page). Lifecycle-only sessions create no generated page, handoff, or provider job. Opt-in SessionEnd provider work is durably queued outside the hook response, retried with backoff, and recovered after server restart. Resumed sessions re-end only when their persisted observation generation advances, so duplicate delivery and clock skew cannot loop consolidation. |
| "What did we learn from this session?" / "what memory should we add?" | memory_auto_improve |
Without a session ID, reviews the newest completed session with no persisted auto-improvement run, advancing past preflight skips on repeated calls; pass an ID for a targeted rerun. The server also runs scheduled auto-improvement for new completed sessions when an LLM is configured. [auto_improve.scheduler] enabled = false disables automatic review; [auto_improve] require_approval = true leaves scheduled and manual proposals in pending-writes for review. |
| "Remember this permanently" / "add an annotation" | memory_write_page |
Writes durable wiki knowledge; not a single-use handoff. |
| "Remember this until Friday" / "expire this after the migration" | memory_write_page with expires_at |
Writes a time-bounded page. Use RFC3339 or YYYY-MM-DD (end of day UTC); normal retrieval hides it after expiry and the next forget sweep deletes it. TTL outranks pinned. |
| "Search expired notes for X" | memory_query with include_expired: true |
Opts an explicit project, sibling-scope, or global search into expired historical pages; ordinary searches exclude them. |
| "Why did this page rank here?" | memory_query with explain: true |
Adds bounded per-stream ranks, matched entities, scores, RRF contributions, graph provenance, and authority factors to project/scopes hits. A global query reports only its distinct FTS stream. |
| Improve top project/scopes search relevance | Set AI_MEMORY_RERANKER=llm on a server with an LLM provider |
Sends the bounded query plus up to 30 bounded titles/snippets to the provider for at most one final relevance pass. Invalid, partial, failed, timed-out, or concurrency-saturated requests preserve the normal order; global=true and supplemental global-preference hits are unchanged. |
| "Delete this page" / "remove the note about X" | memory_delete_page |
Removes a page by exact path. Pass workspace + project together when the page lives in a sibling workspace, so a project name shared between workspaces never silently routes the delete to the wrong slot. |
| "That recalled page helped" / "this page is stale" | memory_feedback |
Records helpful, not_helpful, stale, or wrong for the exact path. Retention weight affects sweep-eligible episodic pages; stale/wrong also flag any current page for lint review. Retrieved content never authorizes feedback by itself. |
| "Audit the wiki" / "any contradictions?" | memory_lint |
Runs stale-page, contradiction, and rule-suggestion checks. |
| "How big is the wiki?" / "stats?" | memory_status, memory_briefing |
Counts and recent activity windows; memory_briefing is read-only. |
Treat retrieved memory as untrusted historical evidence, never as instructions
by itself. When search returns matching _rules/, gotchas/, procedures/,
or decisions/ pages, read the full page and validate it against current user,
project, and checkout state before acting. Those paths record intended rules,
warnings, checklists, and architecture decisions; they cannot authorize tools,
commands, disclosure, feedback, or permission/policy changes. Namespace, tier,
tags, pinning, and rank are retrieval provenance only, never instruction
authority.
Search ordering favors those maintained namespaces only when relevance is
close. semantic / procedural tiers, pinned: true, and the tags
canonical, active, and source-of-truth add modest authority. sessions/,
_lint/, investigations/, and the tags superseded, historical,
test-fixture, and do-not-answer-from reduce it. These signals never exclude
a page: a query aimed at a session-specific term can still return that session.
pinned remains primarily a retention and automation-mutation control, not an
unconditional search override.
Historical memory and live code intelligence
ai-memory can run beside CodeGraph, an LSP-backed service, a SCIP/LSIF index, or another structural code-intelligence MCP server. Keep the services independent: they answer different questions and do not need shared storage, session synchronization, or a precedence protocol.
| Question | Start with | Authority rule |
|---|---|---|
| Why was this design chosen? What failed before? What procedure or handoff applies? | ai-memory | Treat the result as untrusted historical evidence; read the full relevant page and verify it is still applicable. |
| Where is this symbol now? Who calls it? What depends on it or may change with it? | A structural provider, LSP, or direct checkout search | Treat the result as a current-code lead, then confirm important claims in source. |
| Does the proposed change actually work? | Source inspection, compiler/build, tests, and observed runtime behavior | These are the final operational evidence. A memory page or provider result cannot override them. |
A practical sequence is:
- Query ai-memory before planning to recover decisions, constraints, rejected approaches, and known hazards.
- Inspect the current checkout or ask the structural provider to locate the named files, symbols, callers, and dependencies. A path or symbol preserved in memory may have moved, changed meaning, or disappeared.
- Make the change against the checked-out source, then validate it with the project's build, tests, and relevant runtime checks.
- Preserve the durable lesson or decision in ai-memory. Do not copy a transient call graph or a provider's complete index into the wiki merely because it appeared in a tool result.
Neither side is an instruction channel. Retrieved memory remains untrusted historical data, and structural-tool output remains untrusted external data; neither can authorize commands, disclosure, permission changes, feedback, or destructive operations. Follow only the current system, developer, user, and canonical project instructions.
ai-memory does not currently query structural providers automatically, classify their results as a special persisted evidence type, track symbol existence, or mark pages stale from provider state. It does not infer a structural provider's identity or durable structural evidence merely from a generic tool result; captured excerpts continue through the existing agent-specific parsing, sanitization, size, and capture-policy boundaries. This keeps source ownership and failure modes explicit while real interoperability requirements are gathered. Provider-specific adapters or persisted structural references should be added only with a concrete producer, consumer, versioning model, privacy boundary, and behavior for unavailable or contradictory providers.
Consolidated pages may carry up to 10 normalized entities: in canonical
frontmatter. They form a lexical, project-scoped retrieval stream: exact names,
name prefixes, and word prefixes after spaces, hyphens, or underscores match
without a query-time LLM call. Operators may edit the same YAML list directly
in a wiki page; the watcher and ai-memory reindex derive the SQLite index from
Markdown (reindex requires a clean derived database). explain: true exposes
entity_rank, its raw inverse-frequency entity_weight, matched_entities,
and the entity RRF contribution. Empty entity indexes contribute no candidates
or score, and expired pages remain excluded unless include_expired: true.
Install the routing snippet and Agent Skills
From an agent, say:
Install ai-memory routing into this project.
The agent calls memory_install_self_routing and receives the slim
markered_block, marker strings, rules-file hints, managed skill payloads,
skill target hints, and overwrite guidance. It then uses its normal file-edit
tool to preserve unrelated user content, replace or append the
<!-- ai-memory:start --> / <!-- ai-memory:end --> block only when the
marker delimiters appear alone on their own lines, and write each managed skill
below the selected skill root. Skill files are ai-memory-managed only when they
contain the managed marker, so unmanaged same-name skills should not be
overwritten unless the human explicitly forces replacement.
The installed snippet and all project-scoped managed skills use the same
client-aware scope rule. A session-aware bridge that forwards the real hook
session id may omit scope for the current repository. A static MCP client must
send exact workspace and project values on every project-scoped call, even
when lifecycle hooks are installed, because hooks alone do not identify the MCP
request. Declare both names in the nearest .ai-memory.toml,
or obtain them from the operator/server configuration; never infer them from a
directory name or rely on the server's last active project. Global query and
global preference modes still omit project scope arguments.
From a terminal:
ai-memory install-instructions
ai-memory install-instructions --target AGENTS.md
ai-memory install-instructions --print
ai-memory install-instructions --no-skills
install-instructions installs or updates managed skills by default. Use
--no-skills only when you intentionally want a snippet-only refresh.
The CLI replaces only the markered ai-memory block, preserves unrelated content,
and writes a timestamped backup before changing an existing instruction file.
install-instructions --print previews the instruction snippet only; use
install-skills --print to preview skill payloads. Skill flags mirror
install-skills with an --skills- prefix:
--skills-scope project|global, --skills-agent claude-code|agents|devin|grok|both,
--skills-target-dir <dir>, and --skills-force.
Auto-detect extends CLAUDE.md when it exists, AGENTS.md when it
exists, both when both exist, or creates CLAUDE.md when neither exists. Use
--target AGENTS.md for non-Claude-only projects. The skill target follows the
instruction target unless you override it: CLAUDE.md implies
.claude/skills, AGENTS.md implies .agents/skills, and both files imply
both skill roots. For Grok Build CLI, select --skills-agent grok so skills
install under its .grok/skills root; for Hermes Agent, --skills-agent hermes
installs under .hermes/skills (project) or ~/.hermes/skills (global).
When a project keeps AGENTS.md as its canonical instruction file, give it a
CLAUDE.md whose first line is a bare @AGENTS.md import. Claude Code loads
CLAUDE.md and does not read AGENTS.md, so without that import a block
installed with --target AGENTS.md, along with every project rule in the same
file, is absent from context at session start. A prose "read AGENTS.md" pointer
does not load the file; it asks the agent to open it, which leaves adherence to
whether the agent does. See
Claude Code memory.
To refresh only the managed Agent Skills:
ai-memory install-skills
ai-memory install-skills --scope global --agent agents
ai-memory install-skills --scope global --agent devin
ai-memory install-skills --scope global --agent grok
ai-memory install-skills --scope global --agent hermes
ai-memory install-skills --agent both --print
ai-memory install-skills --target-dir .custom/skills --force
For Devin, project-local skills are installed under .devin/skills. Global
Devin installs use %APPDATA%\devin\skills on Windows and ~/.devin/skills
on non-Windows systems. For Grok Build CLI, project-local skills go under
.grok/skills and global under $GROK_HOME/skills (default
~/.grok/skills).
Project-local skill roots are .claude/skills for Claude-compatible installs,
.agents/skills for cross-client installs, .devin/skills for Devin, and
.grok/skills for Grok. Global Claude/Agents roots are ~/.claude/skills and
~/.agents/skills; global Devin roots are platform-specific as described
above; global Grok is $GROK_HOME/skills (default ~/.grok/skills).
--target-dir points at an explicit skill root and bypasses scope/agent
inference. --print previews target paths and SKILL.md contents. --force
allows replacement of unmanaged same-name skills; without it, user-authored
skills are preserved. Uninstall removes ai-memory-managed skills from the
default project/global roots after marker validation; custom --target-dir
roots are a manual cleanup path.
This is prompt packaging only. ai-memory does not run a runtime skill router,
does not store durable memory in SKILL.md, and does not turn the
auto-improvement loop into a skill-authoring system. Durable knowledge still
lives in the wiki.
Bootstrap an existing project
If you install ai-memory into a project that already has months of
history, the wiki starts empty. ai-memory bootstrap seeds it from the
existing repo history and docs.
export AI_MEMORY_SERVER_URL="http://localhost:49374"
ai-memory bootstrap --dry-run
ai-memory bootstrap
The bootstrap collector reads git log, the root README, docs/,
project rule files, and Rust module docs, then POSTs the selected
sources to the running server. It requires an LLM provider on the
server. See Installation cookbook - bootstrap mid-project
for flags, token budgets, and source priority.
Migrate from another memory tool
When replacing an existing memory system, treat the old data as untrusted historical input until you curate it. Do not pipe raw transcripts or old memory stores directly into ai-memory.
Migration checklist:
- Export the old memory or history before changing hooks.
- Keep the raw export as an archive, not as current project truth.
- Scrub secrets, tokens, credentials, API keys, and raw logs that should not become durable memory.
- Curate the useful material into reviewed Markdown pages under a temporary
docs directory or directly into
concepts/,decisions/,gotchas/,procedures/,notes/, or_rules/. - If this checkout might be ambiguous, add
.ai-memory.tomlto pin the intended workspace/project before importing or installing hooks. - Start
ai-memory servelocally and confirmai-memory statuscan reach the server before touching existing client configs. - Import curated material first; avoid importing the full legacy raw history.
- Verify expected pages with full hybrid
memory_query; useai-memory searchonly when a terminal FTS5 lookup is sufficient. - Configure MCP and lifecycle hooks for one client at a time.
- Only after ai-memory capture and retrieval work, disable the old memory hooks, plugins, or MCP servers.
- Search each client config for stale references to the old tool and remove
stale
Authorizationheaders or env vars if bearer auth changed. - Restart each agent CLI after changing hooks, plugins, or MCP config.
Client cleanup hints:
- Claude Code: check plugins, hooks, old SessionStart injection, and MCP servers.
- Codex: check MCP config plus session/user-prompt/tool/compaction/stop hooks.
- Command Code: check
~/.commandcode/mcp.jsonand the four stable lifecycle events in~/.commandcode/settings.json. - Devin CLI: check
.devin/config.json,.devin/hooks.v1.json, and.devin/skillsfor stale MCP, hook, or routing-skill entries. - Gemini CLI and Antigravity CLI: check
settings.jsonor equivalent hook/MCP config files. - Kimi Code: check
~/.kimi-code/mcp.jsonand the[[hooks]]entries in~/.kimi-code/config.toml(both under$KIMI_CODE_HOMEwhen set) for stale MCP or hook entries. - Kiro CLI: check the
hooksobjects inside~/.kiro/agents/*.json(v2),~/.kiro/hooks/ai-memory.json(v3), and~/.kiro/settings/mcp.json(all under$KIRO_HOMEwhen set) for stale ai-memory entries. - OpenCode, OpenClaw, and OMP: check MCP config and plugin/extension directories;
move old memory plugins to a disabled/quarantine directory before deleting.
For the OpenCode 2 beta the plugin file is
ai-memory-opencode2.tsand the MCP entry lives undermcp.serversin the sameopencode.json(c). - VS Code Copilot, Claude Desktop, and Zed: these are MCP-only, so confirm
whether the old tool was providing capture hooks elsewhere. Zed's MCP
entries live under
context_serversin its usersettings.json.
If you want a visible startup reminder during the transition, keep it small. A rules-file note such as “Active memory: ai-memory; legacy export is historical reference only; use memory_query for retrieval” is safer than dumping large legacy context into every session.
If you use the ChatGPT/Codex OAuth provider, sign in once before starting the
server with AI_MEMORY_LLM_PROVIDER=openai-oauth:
ai-memory auth login openai-oauth
ai-memory auth status
The login command stores only provider credentials in <data_dir>/auth.json.
It is separate from AI_MEMORY_AUTH_TOKEN, the machine-root Bearer used by
MCP, hooks, handoffs, workstreams, and machine calls to dual-auth APIs.
For GitHub Copilot, use the matching provider login before starting the server
with AI_MEMORY_LLM_PROVIDER=copilot:
ai-memory auth login copilot
ai-memory auth status
Copilot auth stores a GitHub user token, then the provider exchanges it for a short-lived Copilot API token before each LLM call.
Browse the wiki in a browser
Start the server with --enable-web and open
http://<host>:49374/web.
ai-memory serve --transport http --bind 127.0.0.1:49374 --enable-web
On macOS the menu bar app already starts
the LaunchAgent with --enable-web; Open Web UI opens that same URL.
Docker compose users can add the flag to the service command:
command: ["serve", "--transport", "http", "--bind", "0.0.0.0:49374", "--enable-web"]
The web UI is read-only: project list, per-project page tree,
breadcrumbs, rendered markdown, metadata, and FTS5 search. In rendered
pages, [[wiki links]] become clickable links to the target page —
[[path]], [[path|label]], [[project:path]], and
[[workspace/project:path]] are all supported (resolved against the
current page's project unless the target carries its own scope).
[[…]] stays literal inside fenced code (``` and ~~~ close
only by their own glyph), inline `…` code, and 4-space-indented
code; external schemes inside the brackets (http://, https://,
mailto:, data:, javascript:, vbscript:, tel:, file:)
stay literal too.
With no authority configured on loopback, the built-in wiki remains
anonymous. For human-authenticated administration, serve the compiled admin SPA
with --web-ui-dir: it signs in through /auth/login and uses an HttpOnly web
session plus CSRF protection. Before human auth is active, deprecated GET-only
browser compatibility accepts the root bearer through HTTP Basic and an
HttpOnly ai_memory_auth cookie; it stops immediately after a human password or
completed bootstrap exists. Browser-stored Bearers remain unsupported.
AI_MEMORY_AUTH_TOKEN and aim_ API keys remain machine-only credentials sent
as Authorization: Bearer <token> by MCP, hook, handoff, and workstream clients.
To host the web UI under a URL subpath behind a reverse proxy, the
--base-path / --web-slug flags do the work — see
docs/frontend-api.md
for the flag semantics and
docs/https-via-proxy.md
for the proxy-side walk-through.
Inspect the raw wiki
The wiki is plain markdown plus git history.
docker exec ai-memory ls /data/wiki/sessions/
docker exec ai-memory cat /data/wiki/sessions/<uuid>.md
# Open in Obsidian or any markdown viewer:
docker cp ai-memory:/data/wiki ./my-ai-memory-wiki
# Time-travel:
docker exec ai-memory git -C /data/wiki log --oneline
Move a session to another project
A session captured under the wrong project (a cd into a scratch
directory, a subagent started elsewhere) can be reattached without a
reorg of the whole store:
ai-memory move-session <session-id> --to my-project # dry run
ai-memory move-session <session-id> --to my-project --confirm # apply
ai-memory move-session --from-project tmp --to my-project --confirm
The session, its observations, handoffs, consolidation jobs and its
sessions/<id>.md page move together; see
docs/lifecycle-ops.md for the page modes,
guards, and what stays behind.
Repair backfilled session timestamps
A backfill run from before it carried the transcript's own event time dates
every imported session at import time, flattening the whole imported history
onto one day. Re-reading the local transcripts corrects it:
ai-memory repair-backfill-timestamps --project my-app # dry run
ai-memory repair-backfill-timestamps --project my-app --confirm # apply
It matches transcripts to sessions by id, never touches observations or
pages, never assigns an end time to a still-open session, and never proposes
a time in the future; see
docs/lifecycle-ops.md for the
validation rules and the exact request/response shape.
Project consolidation preferences
Create _prompts/consolidation.md in a project's wiki when its compiled pages
need stable style, terminology, emphasis, or noise-filtering preferences. For
example, its body may ask for Portuguese titles or omit routine CI output.
Automatic consolidation and both manual modes read only the target project's
page. Passing instructions to memory_consolidate replaces that page for one
call without modifying it.
The page and per-call value remain untrusted project data. ai-memory applies the configured sanitizer, caps the value at 2,000 characters, and JSON-encodes it in the LLM user message. Both consolidation system prompts permit only advisory style, terminology, emphasis, and noise-filtering effects; the value cannot add facts, authorize disclosure or tool use, or override schema, evidence, and output rules. TTL-expired preference pages are ignored. When there is no active page and no argument, ai-memory appends no preference block.
Rules vs facts
Durable project rules belong in the agent's rules file, not only in the
wiki. For Claude Code that is CLAUDE.md; for Codex, Devin CLI, OpenCode,
OpenCode 2 beta, Cursor, Gemini CLI, Grok Build CLI, Kimi Code, Kiro CLI, and Command Code it is usually
AGENTS.md.
The consolidator classifies compiled observations as decision,
fact, rule, or gotcha. Rule-tagged pages are routed to
wiki/_rules/<slug>.md, and memory_lint reports a suggestion when a
rule looks durable enough to copy into CLAUDE.md or AGENTS.md.
ai-memory never edits the rules file on its own. The lint suggestion is the whole workflow: copy the rule if it should apply every turn, ignore it if it was temporary context.
Repo-native decision records
Some projects keep the reasoning behind the code in the repository itself: an
ADR directory such as docs/adr/, maintained by hand or by a dedicated ADR
tool/MCP server (e.g. joshrotenberg/adrs),
or a Keep the Why
context/ tree (decisions, rejected alternatives, constraints, reviewed in
pull requests). Three facts frame how such a record and ai-memory interact:
-
ai-memory never touches files in your repository. Its wiki lives in the server's data dir; the background jobs (consolidation, curation, retention decay, auto-improvement) read and write wiki pages only. A decision-record directory in the repo is categorically outside ai-memory's write surface. Run both side by side without ceremony: the repo owns the canonical record, ai-memory owns cross-session recall.
-
Keep the record directory out of capture. An agent reading the record is captured like any other file read, and consolidation compiles what it saw into wiki pages — including a
decisions/page that says "active" long after the repo has superseded it, ranked first bymemory_querybecause it matches the topic. List the directory in the marker's[capture]section so the copy is never made:[capture] ignore_paths = ["docs/adr/**"] # or ["context/**"] for Keep the WhyThe repo owns that record; a compiled copy goes stale the moment the repo moves. Details and bounds in
docs/marker-file.md, including what shell matching cannot see and how to exclude large tool results an agent saves and re-reads from another path. -
Wiki pages marked
pinned: trueare immutable to automation. Retention decay and curation skip them, multi-page consolidation skips any update whose path names one (with a warning in the server log;_slots/keep their own state/invariant regime), and the auto-improvement apply path hard-refuses to rewrite them (the proposal is recorded as a conflict with the reason). Unpinning is the explicit opt-out.
For a project without a repo-side record, decisions go in the wiki, and
the managed durable-pages Agent Skill teaches agents the recipe:
decisions/<slug>.md, ADR structure (Status / Context / Decision /
Consequences, including rejected alternatives), pinned: true, and
supersede-by-new-page instead of editing history. Ask an agent to "record this as an architectural
decision" and the skill does the rest; the structured shape also
retrieves noticeably better through memory_query than free-form
prose.

