Skip to content
Prompt Engineering

Giving an AI Assistant Project Memory That Survives the Session

Chat history is a transcript, not a record. A small set of status-marked files turns every new session from twenty minutes of re-explaining into a two-minute read.

Rajiv KumarAugust 11, 202612 min read
Giving an AI Assistant Project Memory That Survives the Session

AI coding sessions end and take their context with them. The fix isn't a bigger context window — it's a small, disciplined set of files: what's true right now, what's already been decided, and a status marker on every claim so a plan never gets mistaken for a fact.

Anyone who has worked with an AI coding assistant on a real project past the toy-example stage has hit the same wall. The session ends — the context window fills up, you switch models, you close the laptop for the night — and the next session starts with an assistant that remembers none of the decisions you spent an hour reaching. You re-explain the architecture. You watch it propose an approach you already rejected last week, for the same reasons that killed it then. Occasionally it "fixes" something that was working, because it had no way to know the code was intentional.

None of this is a model capability problem. It's a state problem. And state problems have a boring, reliable fix: write the state down somewhere the next session will actually read it.

Why Chat History Doesn't Count as Memory

It's tempting to think the conversation log already is the memory — just point the assistant at the transcript. It doesn't work, for a specific reason: a transcript is chronological, not current. It contains every abandoned approach right alongside the one you settled on, in the order you tried them, with nothing marking which is which. Ask an assistant to "remember what we discussed" from a long history and it has no reliable way to tell a rejected idea from an accepted one — both were discussed with equal enthusiasm at the time.

Usable project memory needs different properties than a log does:

Current, not chronological. What's true right now matters more than the sequence of how you got there.

Decided, not considered. The chosen approach and the reason for it, recorded so it doesn't get silently re-litigated in three weeks.

Honest about state. Explicit about what's built versus what's merely planned — because an assistant treating a plan as a fact is the single most expensive mistake this system exists to prevent.

Verifiable. Every claim points at something in the repository that proves it.

A chat transcript satisfies none of these. A handful of purpose-built files can satisfy all four.

What Actually Auto-Loads Today

Before designing the files, it's worth being precise about what "the assistant reads this automatically" means in practice, because it differs by tool and the differences matter for how you wire the system in.

Tool

Auto-loaded file(s)

Notes

Claude Code

CLAUDE.md (project root or .claude/CLAUDE.md), CLAUDE.local.md, .claude/rules/*.md

Loaded from the working directory and every parent directory at launch; nested CLAUDE.md files load on demand as files in that subdirectory are opened. Files are concatenated, not merged by override.

Cursor

.cursor/rules/*.md (the older single .cursorrules file is deprecated)

Rules can be scoped to file globs, similar to Claude Code's path-scoped rules.

GitHub Copilot

.github/copilot-instructions.md

Read at the start of a Copilot Chat/agent session.

Codex, Gemini CLI, Jules, Aider, Zed, Windsurf, Devin, Factory, and others

AGENTS.md

An open, cross-tool convention (maintained under the Agentic AI Foundation) with no required fields — plain markdown at the repo root.

AGENTS.md is the closest thing this space has to a standard: it's read natively by most of the field, and it's meant to hold the instructions any agent needs regardless of vendor — build commands, test commands, code style, and anything else "agents most often get wrong," kept to roughly 20–30 lines rather than a full README rewrite.

Claude Code is the deliberate exception: it reads CLAUDE.md, not AGENTS.md. If a repository already has an AGENTS.md for other tools, the fix isn't maintaining two documents — put @AGENTS.md as the first line of CLAUDE.md (or symlink CLAUDE.md to AGENTS.md on Linux/macOS) and add any Claude-specific instructions below the import. Claude Code also has a second, separate mechanism worth knowing about: auto memory, notes Claude writes for itself in ~/.claude/projects/<project>/memory/ based on corrections you give it during a session. It's genuinely useful, but it isn't a substitute for what this article is building — auto memory decides on its own what's worth keeping, and it's scoped to one machine, not committed to the repo where a teammate or a differently-configured session can read it.

Note: There's early empirical support for the AGENTS.md convention specifically. A 2026 study by Lulla et al. measuring Codex and Claude Code across 10 repositories and 124 pull requests found a 28.6% median reduction in wall-clock execution time and a 16.6% reduction in output tokens when an AGENTS.md was present, with comparable task-success rates. The sample is small and the authors frame it as early evidence rather than a settled result, but it's a reasonable data point for the intuitive claim: giving the agent the boring facts up front means it burns fewer turns rediscovering them.

A Three-File Structure That Works

None of the files above are built to hold a decision log or a "what's actually built" record — they're instruction files, read once and treated as standing rules. The memory this article is about is a separate, smaller set of files, conventionally kept in a .ai-memory/ folder in the repo, that your instruction file simply points at.

Three files carry most of the value:

File

Answers

Changes

PROJECT-CONTEXT.md

What is this project, and what's fixed about it?

Rarely

CURRENT-STATE.md

What exists right now, and what doesn't yet?

Every session

DECISIONS.md

What was decided, and why?

Append-only

The split matters more than it looks. Mixing stable context with volatile state produces one document that's either perpetually out of date or never opened again after the first week — nobody wants to hunt through "what we decided about auth" to find "what's currently deployed."

Decisions Are Append-Only

Never delete a decision, even a reversed one. Supersede it with a new entry that says what changed and why the earlier choice no longer holds.

## 2026-07-14 — Session storage: Redis, not in-memory
Chose Redis over an in-process cache because we run multiple app
instances behind a load balancer and sessions need to survive
instance restarts. Rejected in-memory caching for this reason —
do not reintroduce it without also removing horizontal scaling.

## 2026-08-02 — SUPERSEDES 2026-07-14: Redis dropped for Postgres-backed sessions
Redis added an operational dependency we didn't need at our scale.
Sessions now live in the existing Postgres instance via
`connect-pg-simple`. Revisit only if session write volume becomes
a measurable bottleneck.

This is what prevents the single most tedious failure mode in AI-assisted work: an assistant, or a new teammate, proposing something that was already tried and specifically rejected. Without a record, that rejection has to be rediscovered from scratch, sometimes by shipping it and watching it fail the same way. With the record, the answer is one line, and it comes with the reason attached — which matters more than the decision itself, because reasons are what let you tell whether the original constraint still applies.

Every Claim Needs a Status Marker

This is the single highest-value convention in the whole system. Every one of these files opens with a block like:

Status: SPECIFICATION | PARTIAL | IMPLEMENTED
Last verified: 2026-09-05
How to verify: ls src/lib/auth/

Documentation drifts by nature. A note written when a feature was planned reads exactly like one written after it shipped — the sentence "the system uses OAuth with refresh token rotation" carries no information about whether that's true today or was true when someone wrote it eight months ago. An assistant reading that sentence cannot distinguish the two cases, and neither can a person skimming quickly. Marking it SPECIFICATION removes the ambiguity outright, and the verification command means the next session — human or AI — can check the claim in seconds instead of taking it on faith.

Tip: Put the verify command in your instruction file too, as a habit: "Before treating anything in .ai-memory/CURRENT-STATE.md as true, run its How to verify command if the session is more than a day old." One line, and it turns an optional discipline into a default behavior.

The Rule That Makes This Safe

A memory system without one more rule is actively dangerous, not just stale. Write this into the memory files themselves, not just into your head:

Code is the truth. Documentation is a claim about the truth. Where they disagree, the code is right and the documentation needs fixing. Never change working code to match a stale document.

Without this stated explicitly, an assistant that trusts a document over the code it's looking at will happily "fix" working code to match a description that was aspirational the day someone wrote it. This is a real, observed failure mode, not a hypothetical one — it's exactly the kind of confident, plausible-looking mistake that's expensive to catch in review because the diff looks intentional.

A second rule earns its place next to it:

Never start over. Having no memory of a previous session is not evidence that something is missing.

A fresh assistant session looking at an unfamiliar-feeling codebase has a natural pull toward rebuilding what it can't immediately verify exists. Stating the instinct is wrong, in writing, heads off a specific and costly mistake: reimplementing a working module because the current session doesn't personally remember writing it.

Wiring It Into Claude Code, Cursor, or Copilot

The files only earn their keep if something loads them without being asked every time. The mechanism differs slightly by tool, but the pattern is the same: keep the instruction file short, and have it point at the memory files rather than trying to contain them.

For Claude Code specifically, a project CLAUDE.md can pull the memory files in with @path imports, which are expanded into context automatically at session start:

# Project Instructions

See @.ai-memory/PROJECT-CONTEXT.md for what this project is and
what's architecturally fixed.

See @.ai-memory/CURRENT-STATE.md for what exists right now —
check its "Last verified" date before trusting it.

See @.ai-memory/DECISIONS.md for settled decisions. It is
append-only. Never delete an entry; supersede it with a new one.

## The one rule that matters
Code is the truth. Documentation is a claim about the truth.
Where they disagree, fix the documentation — never the code.
Never start over: no memory of a previous session is not
evidence that something wasn't built.

Keep the CLAUDE.md itself under roughly 200 lines — Claude Code loads it in full at every session start, and longer files measurably reduce how consistently instructions get followed. Detail belongs in the files it points to, not in the entry point itself. If a single repository has instructions that only matter for one subdirectory (frontend lint rules that don't apply to the backend, say), Claude Code's .claude/rules/ directory supports path-scoped files that load only when matching files are opened, which keeps the always-loaded portion smaller.

If the same repository is also used with Cursor or Copilot, don't maintain three separate copies of the same rules. Put the shared, tool-agnostic instructions in AGENTS.md at the repo root, and have CLAUDE.md import it with @AGENTS.md as its first line, adding anything Claude-specific below. Cursor and Copilot read their own native files for tool-specific settings, but the substance — what this project is, where the memory files live, the code-beats-docs rule — only needs writing once.

Common Mistakes and Better Practice

Writing documentation for humans instead of state for machines. Prose explaining rationale has its place, but a one-line status marker saying what currently exists is what actually prevents wasted work. If a file reads like a design document, it's probably not doing this job.

Updating only at the end of a project. A memory file updated once, at the finish line, is wrong again within a day of the next work session starting. Update at each milestone, as part of finishing it — not as cleanup afterward.

Deleting superseded decisions. The rejected options are the valuable part of the record. Deleting them turns the file back into a plain README that only describes the current state, losing exactly the information that stops old arguments from restarting.

Skipping status markers. Without them, a plan and a fact are typographically identical, and that's the whole problem this system exists to solve.

One enormous file. A 600-line CURRENT-STATE.md gets skimmed, not read, and the stable parts drown out what actually changed this week. Split by how often content changes.

Writing secrets into memory files. These files get committed to source control. Describe a credential's shape and where it comes from — never its value.

Trusting auto-generated context files over hand-written ones. Whether it's an AGENTS.md produced by /init or a state file an assistant wrote itself, treat generated content as a first draft to review, not a finished artifact — the available evidence suggests hand-maintained files hold up better than auto-generated ones.

Trusting the file without running the verify command. The command exists to be run, not to look reassuring in the header.

Troubleshooting the Practice Itself

Symptom

Likely cause

Fix

Assistant rebuilds work that already exists

CURRENT-STATE.md doesn't say it exists, or wasn't loaded

Record what's built, with a verification command; confirm the file is actually being loaded (in Claude Code, check /context)

Same decision gets debated repeatedly

No decision log, or it isn't referenced from the instruction file

Append the decision with its rationale; link DECISIONS.md from CLAUDE.md or AGENTS.md

Assistant "fixes" working code

It trusted a stale document over the code

Add the code-beats-documentation rule explicitly; correct the document

Notes go stale within days

Updated only at project milestones or the end of a project

Update as part of each milestone's work, not as a separate step

Instruction file seems to be ignored

File is too long, or isn't in a location the tool actually loads

Trim to pointers and rules; verify load location and order for your specific tool

Frequently Asked Questions

Isn't this just a README?

A README explains a project to a newcomer joining once. This records current state and settled decisions for whoever — human or AI — resumes work next Tuesday. Related jobs, but different ones; a good README rarely needs a status marker or an append-only decision log.

How much effort does maintaining this actually take?

A few minutes at each milestone — updating CURRENT-STATE.md and appending anything to DECISIONS.md that would be annoying to re-argue. That's considerably less than the twenty minutes of re-explaining context that it replaces at the start of every session.

Should these files be committed to source control?

Yes. They're project artifacts and benefit from version history the same way code does — which is exactly why no credential value ever goes in them, only a description of where to find one.

Does this help human teammates too, not just AI assistants?

Yes, and it's a reasonable quality test: if a new engineer could get productive from these three files alone, an AI assistant can too. If they couldn't, the files probably aren't specific enough for either audience.

Does Claude Code's auto memory replace this system?

No — they solve different problems. Auto memory is Claude noting your corrections and preferences on its own initiative, stored locally on your machine. It's useful, but it's not committed to the repo, it's not something a teammate's session shares, and it doesn't guarantee it captured the one decision you'd be most annoyed to re-litigate. Keep DECISIONS.md explicit and let auto memory handle the smaller, personal stuff it's actually designed for.

Which assistant does this work with?

All of them — it's a file convention, not a product feature. Only the auto-loaded entry-point filename and its load order differ: CLAUDE.md for Claude Code, AGENTS.md for most everything else, native rule files for Cursor and Copilot if you're not consolidating into AGENTS.md.

Writing Down What You'd Otherwise Repeat

An assistant can write these files for you once you tell it what to put in them, but it can't decide on its own which decisions were load-bearing — that judgment is still yours. The few minutes spent at each milestone, recording current state and anything you'd hate to re-argue, is the entire practice. What it buys back is the start of every future session: not twenty minutes of re-explaining, but a two-minute read that gets straight to where you actually left off.

Share