Persistent memory for AI coding

AI··ai, coding

1 The problem

1.1 What AI coding agents forget

AI coding agents are clever, but each session starts mostly blank. They know whatever was in their training data, plus whatever files they read or you tell them in this conversation. When the session ends, that working understanding is usually gone. Some tools have a separate memory feature, but even then the memory is often local, personal, or tool-specific.

If you’ve ever found yourself explaining the same project conventions to an agent 3 times in a week (“we use Go, not Python; the build command is make test, not go test; vendor X’s data has a quirk in column 4”), you’ve hit it.

The practical cost: repetition and lower-quality output on the early turns of every session.

1.2 Why it gets worse with parallel agents

Modern tools like Conductor let you run several AI agents in parallel, each in its own isolated workspace. That’s a productivity multiplier, but it also multiplies the amnesia problem. Now you have 5 fresh sessions a day instead of one, and each one starts cold.

Put project knowledge in a place every new session reliably looks.

1.3 What you’ll have at the end

A setup where:

  • A new agent session (from a supported tool, launched inside the repo or configured to load repo instructions) picks up project conventions, architectural decisions, and known gotchas.
  • When an agent discovers something worth remembering during a task, there’s a clean ritual to capture it before the work merges.
  • The knowledge survives even when individual workspaces are deleted.
  • The same pattern works whether you’re using Claude Code in a terminal, Conductor running several agents, Codex, or an IDE with built-in AI features.

2 Background concepts

2.1 Git, in brief

Git is the version control system most teams use. The minimum vocabulary you need:

  • Repository (repo): a directory whose history Git is tracking.
  • Commit: a saved snapshot of changes. You make many commits over the life of a project.
  • Branch: an independent line of commits. You typically start a new branch for each feature or fix.
  • main (sometimes master): the canonical branch representing the official current state of the project. It’s the trunk; everything else is a side branch that eventually gets merged back in.
  • Merge: combining a side branch’s commits into main.
  • Pull request (PR): a proposal to merge a branch, reviewed by teammates (or yourself), usually via a hosted service like GitHub or GitLab. When the PR is approved, the merge happens.
  • Rebase: updating your branch so it is based on the latest commits from main. In practice, this means your branch incorporates changes that merged after your branch was created.

The mental picture: main is the shared truth. Side branches are temporary scratch space where work happens. Once a side branch merges, its individual existence stops mattering; only what made it into main survives.

2.2 Worktrees

Normally, a Git repo on your machine has one working directory: one place on disk where you see the project’s files. If you switch branches, those files change in place.

A worktree is an additional working directory for the same repo, checked out to a different branch. You can have several worktrees of the same repo, each on a different branch, all live at the same time.

Why this matters here: if you want to run 2 AI agents in parallel on different features, you don’t want them editing the same directory. They’d collide. Worktrees give each agent its own isolated directory, but all point at the same underlying repo. When the agent’s work is merged, the worktree can be deleted or archived without losing anything; the commits are already in main.

This is the mechanism Conductor-style workflows automate. You can also do it by hand:

git worktree add ../my-feature feature-branch

That command creates a new directory ../my-feature with feature-branch checked out, leaving your main working directory untouched.

2.3 The cast of AI coding tools

You’ll see these names throughout:

  • Claude Code: Anthropic’s coding agent, available as a CLI. Its project instruction file is called CLAUDE.md.
  • Codex CLI: OpenAI’s coding agent CLI. It reads AGENTS.md files, including global, project, and nested variants.
  • Cursor: an editor with AI features. It supports AGENTS.md and can also use tool-specific rules in .cursor/rules/.
  • Gemini CLI: Google’s coding agent CLI. It commonly uses GEMINI.md, and can be configured to read AGENTS.md.
  • Windsurf, Amp, Devin, Aider, Goose, and others: various tools with their own conventions. Many now support AGENTS.md, but exact behaviour differs.
  • Conductor: a Mac app that runs multiple agents in parallel, each in its own isolated workspace. Not an agent itself; it’s an orchestrator. There are other similar tools, but this article will just refer to Conductor (but the techniques outlined should be portable enough).

You’ll likely use more than one of these. The setup here keeps project knowledge in one tool-neutral place while still allowing thin tool-specific adapters when needed.

2.4 Memory files

A memory file is a markdown file in your repo that an agent automatically reads when it starts a session. It contains the project-specific instructions you’d otherwise repeat by hand.

The agent doesn’t memorize it the way a human does. It re-reads it every session. So it’s not really “memory” in the brain sense; it’s stable starting context, present in each conversation where the tool loads it.

AGENTS.md has emerged as an open, cross-tool convention. The AGENTS.md project describes it as a README for agents: a predictable place for setup commands, tests, conventions, security considerations, and gotchas. It is now stewarded by the Agentic AI Foundation under the Linux Foundation.

Many major coding agents support AGENTS.md, but don’t assume identical loading or precedence rules. Some tools read the nearest file, some concatenate parent and nested files, and some use tool-specific fallbacks or override files. Treat the pattern as portable, but verify the exact behaviour in the tools you use.

3 The core principle

Here’s the single idea this whole tutorial rests on:

Knowledge that matters must live in files committed to main. Anything else is ephemeral.

When you finish a side branch and merge it, that branch’s working directory can disappear. If the agent learned something during the work and that learning only lives in:

  • the chat history,
  • a local-only file,
  • the agent’s internal session state,
  • tool-specific auto-memory,
  • a scratch note on your desktop,

then it may disappear when the worktree is cleaned up, when you change machines, when a cloud session ends, or when you start your next session in another tool.

The corollary: if you want a piece of context to be there for the next session and for the team, it has to be a committed file that lands in main via a PR. That’s the only path that is both lasting and shared.

This sounds obvious. In practice, most people accumulate knowledge in places that don’t survive, and only realize it when they find themselves explaining the same vendor quirk for the 4th time.

4 Setting up the canonical memory layer

4.1 Pick one source of truth

Use AGENTS.md as your canonical memory file. Reasons:

  • Many major tools read it directly or can be configured to read it.
  • The format is plain markdown; no special syntax to learn.
  • It avoids maintaining parallel copies of the same rules for each tool.
  • It can be reviewed, tested, and changed through the same PR workflow as the code.

Create one at the repo root:

your-project/
└── AGENTS.md

We’ll fill it with real content in a moment.

4.2 Route Claude Code at AGENTS.md

Claude Code’s project instruction file is CLAUDE.md, not AGENTS.md. You have 2 clean ways to point it at the canonical file. Either works; pick one and stick with it.

Option A: relative symlink (Linux/macOS). Git tracks symbolic links natively via file mode 120000, so a relative symlink commits cleanly and is reconstructed by every subsequent git checkout and git clone on POSIX systems:

ln -s AGENTS.md CLAUDE.md
git add AGENTS.md CLAUDE.md
git commit -m "chore: link CLAUDE.md to canonical AGENTS.md"

Use this if your team is all on Linux or macOS. Caveats: Windows clients need core.symlinks = true and developer-mode-or-admin on Windows; some Git GUIs and cross-platform sync tools handle symlinks poorly. If any of those apply, prefer Option B. (And if you are using Windows, why?)

Option B: @-import. Claude Code’s documented memory mechanism supports inline file imports. Make CLAUDE.md contain a single line:

@AGENTS.md

Claude Code expands the import at session start, inlining the contents of AGENTS.md. It’s a regular tracked file, so it behaves identically across platforms, Git GUIs, and tooling. Imports work recursively up to 5 levels deep, so the same trick works for nested CLAUDE.md files in subdirectories.

Whichever you pick, the goal is one canonical source of truth (AGENTS.md) with CLAUDE.md as a pointer. You never maintain parallel copies.

4.3 What to put in AGENTS.md

Keep AGENTS.md short and scannable. The Claude Code documentation and the broader community converge on a rough soft target of around 200 lines for the root file; the right number is “as short as practical without losing meaning.” The reason isn’t arbitrary: long files suffer from attention degradation (the “lost in the middle” effect), consume context budget on every session, and tend to accumulate stale rules that quietly steer the agent wrong.

The principle: include only what a fresh agent would otherwise get wrong, in the most direct phrasing. Use plain markdown structure: headings, short bullets, fenced code blocks where commands appear. Don’t try to encode rules in shouting uppercase or pseudo-formal KEY_VALUE syntax; agents read plain markdown well, and humans have to maintain this file.

A useful starting template:

 Project: <name>

# What this project is
One or two sentences. The agent will figure out the rest from the code.

# Conventions
- Language and version (e.g., Go 1.22)
- Code style rules that aren't obvious from a linter config
- Naming patterns we follow

# Build, test, lint
- To build: `make build`
- To test: `make test`
- To lint: `make lint`
- Always prefer Makefile targets to invoking tools directly.

# Definition of done
- Run `make test` before declaring a task complete.
- Run `make lint` if any source or config files changed.
- Add tests for new behaviour.

# Non-obvious invariants
- Vendor X's CSV has a misnamed column for dates before 2018.
- The `legacy/` directory is unmaintained; don't touch it.
- Direct commits to `main` are forbidden; always go via PR.

# Security
- Never commit credentials, keys, or customer data. Use environment
  variables or a secret manager and reference them by name only.
- Require explicit human approval before adding new third-party dependencies.

# Where to find more detail
- Architecture decisions: `docs/adr/`
- Module-specific notes: nested `AGENTS.md` files in subdirectories

4.4 What belongs where

Don’t turn AGENTS.md into a second README, architecture manual, policy manual, and local scratchpad all at once. Use the right channel for the right kind of information:

Put it hereUse it for
AGENTS.mdShort, high-signal project rules every agent should see.
Nested AGENTS.mdSubsystem-specific constraints close to the relevant code.
ADRsImportant design decisions, trade-offs, and rationale.
README or human docsHuman onboarding, tutorials, long-form explanations, and product context.
CI, linters, hooks, testsEnforcement. Don’t rely on agent instructions for enforceable rules.
Local memoryPersonal preferences and temporary discoveries only.
Chat or PR descriptionsCurrent-task communication; not lasting project memory.

4.5 Instructions are context, not enforcement

Treat memory files as strong hints, not hard configuration. Agents usually receive these files as context and try to follow them, but vague or conflicting rules reduce reliability. A few rules of thumb:

  • Prefer specific instructions over slogans. “Run make test” is better than “be careful.”
  • Avoid duplicate rules stated differently in multiple places.
  • Put module-specific rules as close as practical to the relevant code.
  • Enforce critical requirements with tests, CI, linters, code review, permissions, or hooks; not only with prose in AGENTS.md.

4.6 Nested AGENTS.md for modules

If your project has subdirectories with their own conventions or quirks, put a smaller AGENTS.md in each one:

your-project/
├── AGENTS.md                  # project-wide
├── CLAUDE.md                  # pointer to AGENTS.md (symlink or `@AGENTS.md`)
├── internal/
│   ├── ingest/
│   │   ├── AGENTS.md          # ingest-specific notes
│   │   └── CLAUDE.md          # pointer to ./AGENTS.md
│   └── store/
│       ├── AGENTS.md
│       └── CLAUDE.md

A note on relative paths for nested pointers: if you’re using symlinks, run ln -s AGENTS.md CLAUDE.md from within each directory. The link target is interpreted relative to the link’s location, not the repo root. If you’re using @-imports, the @AGENTS.md line in a nested CLAUDE.md resolves relative to the file containing the import, which is what you want.

The broad pattern: project-wide rules live at the root, module-specific gotchas live with the module. Exact precedence differs by tool. Codex, for example, concatenates global and project files from the root down to the current working directory, with closer files appearing later in the combined prompt. Other tools may use nearest-file precedence or tool-specific rule systems. When available, use the tool’s “show loaded instructions” or memory/debug command to confirm what the agent actually loaded.

4.7 Architecture Decision Records (ADRs)

The most expensive context to lose is why a decision was made. The code shows what; the reasoning behind it usually doesn’t.

An Architecture Decision Record (ADR) is a short markdown file capturing one decision: the situation, the options considered, what was chosen, and why. Convention is to number them and keep them in docs/adr/:

docs/adr/
├── 0001-database-choice.md
├── 0002-error-handling-style.md
└── 0003-deployment-strategy.md

Each file is short. Half a page is fine. The format is up to you, but a minimal one:

 0001: Database choice

# Status
Accepted

# Context
We needed to pick a database for time-series data.

# Decision
We chose ClickHouse.

# Rationale
- Considered Postgres + TimescaleDB, InfluxDB, ClickHouse.
- ClickHouse's columnar storage gave 10x better compression on our data.
- The TimescaleDB option was tempting because we already use Postgres,
  but query performance on our specific workload was 3x slower in benchmarks.

# Consequences
- We now operate two database systems in production.
- Backups and migrations are more complex.

You don’t need an ADR for every decision. You need one when:

  • A future contributor or agent will reasonably ask “why did we do it this way?”
  • The decision is hard to reverse.
  • The reasoning isn’t obvious from the code.

Mention the docs/adr/ directory in your root AGENTS.md so agents know to look there for the “why.”

5 The capture cycle

You’ve now got a place to put lasting knowledge. The next question: how does knowledge actually flow from an agent’s working session into that committed file?

The pattern: nominate during the session, promote before the PR merges.

5.1 During the session: nominate

When you or the agent notices something worth remembering, capture it as part of the same work, in the same branch. Different tools have different conveniences:

  • Claude Code: type # followed by your note. Claude will offer to add it to a memory file; choose the project-level one (which routes to your AGENTS.md). You can also just ask Claude to add a specific line to AGENTS.md directly.
  • Codex CLI, Cursor, IDE-based tools: open AGENTS.md and edit it directly. It’s plain markdown.
  • Any tool: ask the agent to propose a one-line addition to the nearest AGENTS.md based on what just happened, and review the suggestion before saving.

The key word is nominate. You’re proposing something for memory, not committing to it yet. The review step is where it gets accepted, narrowed, moved to an ADR, or rejected.

5.2 Pre-PR: review what to keep

Before opening a PR, run a deliberate review step. Paste a prompt like this into whichever agent you’re using:

Review the diff of this branch against origin/main.
Identify any non-obvious invariants, third-party edge cases, or architectural
choices a future contributor (or agent) starting cold would benefit from knowing.
Propose at most 2-3 short bullet points to add to the nearest AGENTS.md,
or a new ADR file if the item is substantial.
Do not propose generic best practices, restatements of what the code does,
or speculative rules. If nothing this branch encountered would have saved
real time as a captured rule, reply "N/A".

Every new line in AGENTS.md should respond to an actual moment in the work where its absence cost time or risked a wrong change.

5.2.1 Prune rules that tools now enforce

Memory files stay useful by staying thin. When a rule becomes enforced by a real tool (a lint setting, a test, a CI check, a pre-commit hook), remove the corresponding line from AGENTS.md. The tool is now the source of truth, and a prose duplicate just adds noise and a future drift hazard. Treat this as part of the same PR ritual: every time you add enforcement, check whether a prose rule in AGENTS.md is now redundant.

5.3 Pre-PR: the checklist

Your PR template, for example .github/PULL_REQUEST_TEMPLATE.md, should include a checkbox:

- [ ] AGENTS.md or an ADR updated to reflect anything I learned in this branch
      that a future contributor would want to know. (Or: N/A, with a one-line reason.)

This makes the question visible at review time. The reviewer can push back if the branch clearly learned something that didn’t get captured.

5.4 Make it tool-agnostic via the Makefile

Expose a non-blocking target like this:

BASE_REF ?= origin/main

memory-check:
	@git fetch origin main --quiet || true
	@if ! git diff --name-only $(BASE_REF)..HEAD | grep -qE '(^|/)(AGENTS\.md|AGENTS\.override\.md|CLAUDE\.md|GEMINI\.md|docs/adr/)'; then \
		echo "Reminder: consider whether AGENTS.md, a tool instruction file, or an ADR should be updated for this branch."; \
	fi

Why origin/main..HEAD and not local main: In multi-worktree workflows, your local main branch reference can fall behind the remote. Agents in other worktrees may have merged work that hasn’t been pulled into this worktree’s view of main. Diffing against origin/main (after git fetch) avoids stale comparisons. The double-dot form (A..B) here means “what’s in B that isn’t in A” for git diff --name-only, which is what we want for “did this branch touch AGENTS.md.” The triple-dot form (A...B) compares from the merge base of A and B to B; useful for PR-style review diffs but unnecessary for this name-only check.

5.5 Optional: a git hook

If you want the reminder to fire automatically before pushing, add a pre-push hook in .git/hooks/pre-push:

!/bin/sh
make memory-check
exit 0  # always succeed - this is a reminder, not a gate

The exit 0 keeps it non-blocking. If you want it shareable across teammates, use a hook manager like Lefthook or pre-commit . The per-repo .git/hooks/ directory isn’t tracked by Git, so a hook manager is what makes hooks reproducible across the team.

6 Tool-specific notes

The pattern above is the same regardless of tool: keep project knowledge in committed files, keep local memory local, keep tool-specific adapters thin. The surface details differ.

6.1 Conductor and parallel worktrees

Conductor workflows run multiple agents in parallel across separate git worktree directories. A few things to know:

  • Auto-memory is shared across worktrees of the same repo. Per Anthropic’s documentation, the <project> path Claude Code uses for its auto-memory under ~/.claude/projects/<project>/ is derived from the Git repository, so all worktrees and subdirectories of the same repo share one auto-memory directory. A note Claude makes in Worktree A’s session will be visible to a session in Worktree B, when both are sessions inside the same underlying repo.
  • Auto-memory is still single-machine. It lives in your home directory, not in the repo, so it doesn’t travel to teammates, CI runners, or a different laptop. Treat it as a personal-machine convenience, not lasting team knowledge. If a learning is worth sharing, promote it to AGENTS.md in the same branch as the related code change.
  • Auto-memory doesn’t synchronize live. Even within one machine, 2 agents working concurrently in 2 worktrees aren’t reading and writing the same in-memory state; they’re appending to a shared on-disk store. Don’t rely on real-time cross-worktree coordination through auto-memory. Rely on the canonical AGENTS.md in main that both worktrees see when they’re created or rebased.

The practical rule is the same as everywhere else here: the only lasting, team-shared layer is the committed AGENTS.md. Auto-memory is a useful local accelerator, not a replacement.

6.2 Claude Code CLI

Run claude in any directory inside your repo. Claude Code walks up the directory tree, finds every CLAUDE.md along the way, and concatenates them as the session’s starting context. Because CLAUDE.md points at AGENTS.md (via symlink or @-import), Claude Code picks up your canonical guidance automatically.

To verify which files Claude actually loaded for the current session, use:

/memory

This lists every memory file Claude Code is currently using: project, user, and local. Use it whenever you’re unsure whether your nested AGENTS.md made it into context, or whether an old CLAUDE.local.md is contributing rules you forgot about. Don’t ask the model “what’s in your context?” LLMs can’t reliably introspect on their own context window, and the answer will look plausible whether or not it’s accurate. /memory reports what the runtime actually loaded.

Related: /context shows context-window usage and can warn when you’re approaching capacity.

Claude’s auto-memory lives under ~/.claude/projects/<project>/. Treat it as a single-machine convenience. If it captures a fact worth keeping, manually promote it to AGENTS.md so it travels with the repo to teammates, CI runners, and other agents.

6.3 Codex CLI

Codex reads AGENTS.md before doing work. Its documented discovery chain includes:

  1. global guidance in the Codex home directory, usually ~/.codex/AGENTS.md or ~/.codex/AGENTS.override.md;
  2. project guidance starting at the repository root;
  3. nested guidance down to the current working directory;
  4. at most one instruction file per directory, with AGENTS.override.md taking precedence over AGENTS.md;
  5. concatenation from root to leaf, so more local guidance appears later and can override earlier guidance.

Codex caps total instruction size at 32 KiB by default (configurable via project_doc_max_bytes) and skips empty files. Keep your root file short for the same reason it matters for every other tool: long files cost context budget on every session and tend to dilute the rules that matter most.

6.4 Editor-based tools

Cursor, GoLand AI, VS Code extensions, and other editor-based tools may read AGENTS.md directly or via configuration. Some have their own scoped rule formats, such as Cursor’s .cursor/rules/*.mdc.

The principle to hold to:

  • AGENTS.md stays the canonical source of truth.
  • Tool-specific files exist only when you need a feature that AGENTS.md can’t express, such as glob-scoped activation for one editor’s behaviour.
  • Tool-specific files should be thin, ideally referencing AGENTS.md rather than duplicating its content.

If you let each tool maintain its own parallel rules file, you’ll get drift: the same rule, slightly differently worded in 3 places, and a future you who can’t tell which one is current.

7 Pitfalls

7.1 Storing knowledge in local-only files

CLAUDE.local.md, Claude auto-memory under ~/.claude/projects/<project>/, Codex’s local/global personal guidance, Cursor’s local rule cache, and similar files are session-scoped, machine-scoped, or user-scoped. They don’t reliably survive a fresh checkout, a different machine, a cloud session, or a new teammate joining.

If you find yourself adding something important to one of these, stop and promote it to AGENTS.md or an ADR.

7.2 Storing secrets in memory files

Never put passwords, API keys, production credentials, private keys, customer data, or confidential contract terms in agent memory files. If an agent needs configuration, use environment variables, secret managers, local ignored config files, or documented setup steps with fake placeholders.

AGENTS.md may contain security rules. It should not contain security secrets.

7.3 Letting an LLM auto-generate the memory file

It’s tempting to ask the agent to “draft me a comprehensive AGENTS.md from this codebase.” Resist that.

A 2026 ETH Zurich / LogicStar.ai study (arXiv:2602.11988 ) evaluated coding agents with no context file, with LLM-generated context files, and with developer-written context files across real-world software engineering tasks. Their finding was unflattering: context files tend to reduce task success rates compared to providing no repository context, while increasing inference cost by over 20%. LLM-generated files were worst, but even developer-written files often hurt. The paper concludes that “unnecessary requirements from context files make tasks harder, and human-written context files should describe only minimal requirements.”

The actionable takeaway: a small, ruthlessly-pruned file is much more likely to help than a large one, regardless of who wrote it. Start tiny (maybe 30 lines) and only add a rule when you can point to a specific moment in real work where its absence cost time. Use the agent to propose wording where useful, but a human decides whether the rule earns its place in main.

7.4 Bloat

Once root AGENTS.md grows beyond a few hundred lines, agents suffer from attention degradation. Worse, the file becomes harder to keep accurate, and it accumulates stale rules that quietly steer the agent wrong.

Prune as part of the same PR ritual: if a rule no longer reflects how the project works, remove it. If a rule is too detailed for every task, move it to a nested file or ADR.

7.5 Conflicting instructions

Conflicts are worse than omissions. If the root AGENTS.md says one thing and a nested file says another, make the override explicit. If a tool-specific file duplicates a rule from AGENTS.md, it will eventually drift.

Prefer a single canonical statement plus narrow local overrides.

7.6 Putting context in PR descriptions

Agents don’t reliably read old PR descriptions on future sessions. If a decision deserves to be remembered, it needs to be in AGENTS.md or an ADR, not just in the PR.

7.7 Forgetting to rebase parallel workspaces

If you’ve got several worktrees open and one of them merges, the others won’t see its AGENTS.md updates until they incorporate the latest main. Make rebasing routine when picking up work in a long-lived workspace.

8 Summary

Layout

your-project/
├── AGENTS.md                # Canonical memory, kept short (~200 lines or less)
├── CLAUDE.md                # Pointer to AGENTS.md (relative symlink or `@AGENTS.md`)
├── docs/
│   └── adr/                 # One short file per significant decision
├── .github/
│   └── PULL_REQUEST_TEMPLATE.md   # Includes the memory checklist
├── Makefile                 # Includes the `memory-check` target
└── <code>
    └── <subdirs with their own AGENTS.md when useful>

Workflow per branch

  1. Start work in a new branch, worktree, or parallel workspace.
  2. As the agent helps, note real friction points; nominate short additions to the nearest AGENTS.md or a new ADR.
  3. Before opening the PR, run the review prompt against the diff (origin/main..HEAD). Drop anything speculative; drop anything now enforced by tests, linters, or hooks.
  4. Tick the PR template’s memory checkbox (or mark N/A with a reason).
  5. Reviewer checks: were actual learnings captured? Was anything speculative dropped?
  6. Merge. The worktree can be archived. Knowledge now lives in main for every future session.

Hard rules

  • Team knowledge lives in committed files. Anything else is volatile.
  • AGENTS.md is the canonical source of truth. CLAUDE.md is a pointer to it.
  • Add a rule only in response to a real mistake or moment of confusion, never speculatively.
  • Local auto-memory (~/.claude/projects/<project>/, CLAUDE.local.md, tool-specific caches) is a single-machine scratchpad, not lasting storage.
  • Never put credentials, keys, or customer data in memory files.
  • For anything that must not be violated, back the rule with enforcement (tests, linters, CI, hooks). Prose in markdown is a hint, not a guarantee.

9 Sources for tool-specific claims