Every AI coding session starts with a blank slate. The assistant reads your code, maybe your instruction file, and then it guesses. It proposes the library you removed last month. It "fixes" the odd-looking retry loop that exists for a good reason. It asks about the deploy target you explained yesterday.

The code is not the problem. The missing piece is memory: the short record of what you decided, what you tried, and what is still open. Humans keep it in their heads. Assistants cannot.

This guide shows you how to build project memory that an assistant actually uses. You will get a file layout, templates for each kind of entry, a list of moments that should trigger a note, and a routine to keep it all current. Everything here works with plain Markdown files, whatever assistant you use.

What project memory is, and what it is not

Project context comes in three kinds. Mixing them up is the most common reason memory files grow useless.

KindAnswersChangesExample
Instructions"How do we work here?"Rarely"Run pnpm test before committing"
Memory"What did we decide, and why? Where are we?"Often"Chose SQLite over Postgres for the desktop build, 2026-08-12"
Sessions"How did we get there?"Never, once savedThe full conversation where the sync design was worked out

Instructions are rules. They live in CLAUDE.md for Claude Code or AGENTS.md for Codex and many other agents. The guide on writing project instructions your assistant follows covers them.

Memory is the working knowledge of the project. It is what a good colleague would tell you on your first day: the choices already made, the traps, the current state.

Sessions are raw material. Memory is often a two-line summary of a long session, with a link back to it if the details matter. The article on which AI coding sessions are worth keeping explains how to pick them.

Project memory is not a diary, a changelog or a copy of your issue tracker. If it would not change what someone does next, it does not belong.

What to write down

Six kinds of entries cover almost everything worth remembering.

1. Decisions

A choice between real options, with the reason. This is the highest-value entry, because reasons are the first thing to disappear.

### 2026-08-12 — SQLite for the desktop build
Decision: Use SQLite, not Postgres, for the offline desktop app.
Why: Users run it without a server; a single file is easy to back up.
Consequence: No JSON columns. Keep queries portable.
Status: Active

For a longer format, see lightweight decision records for AI-assisted projects.

2. Rejected approaches

What you tried and dropped. This is the entry that stops the assistant from proposing it again.

- Tried: server-sent events for live updates (2026-09-03).
  Dropped: the corporate proxy used by our main client buffers them.
  Use polling every 10 s instead. Do not reintroduce SSE.

3. Constraints

Facts about the world the code must respect but cannot show. A partner API that rate-limits hard. A customer who still runs an old browser. A file format fixed by a contract.

4. Open questions

Things you have not decided yet. Writing them down stops an assistant from quietly deciding for you.

- Should exports include archived projects? Not decided.
  Until then: exclude them and leave a TODO in export.ts.

5. Current state

A short handoff note: what you were doing, what is half done, what comes next. Overwrite it each time; it is a snapshot, not a log.

## Current state (2026-09-22)
- Migrating auth middleware to the new session format. 3 of 5 routes done.
- Remaining: /admin and /billing. Billing needs the webhook test first.
- Known broken: `npm run e2e` fails on the login step until /admin is migrated.

6. Glossary

Words that mean something specific in your project. If "workspace" and "project" are different things in your domain, say so once, and every future session will use them correctly.

When to write it down

Memory fails when writing it is a separate chore. Tie it to moments that already happen. Write an entry when:

  • You reverse a decision. Record the old one as superseded and the new one with its reason.
  • You explain something twice. The second time you type the same explanation to an assistant, put it in memory instead.
  • A bug takes longer than expected. Record the cause in one line. The next person to see that symptom will thank you.
  • You say "no" to an assistant's suggestion for a non-obvious reason. That reason is a rejected approach.
  • You stop for the day or switch machines. Update the current state note. The guide on continuing an AI coding project on another machine builds on this.
  • Someone joins the project. Whatever you explain to them out loud is missing from memory.

A useful end-of-session prompt for your assistant:

Before we stop: draft updates for .prjcontext/memory/.
1. Any decision we made, in the decisions.md format.
2. Anything we tried and dropped, with the reason.
3. A new "Current state" section.
Show me the diff. Do not write the files until I approve.

The assistant drafts, you review. That review matters: an assistant will happily record a guess as a decision.

A file layout that works

Keep memory in a small number of files with clear jobs. One folder, beside the code:

my-app/
├── CLAUDE.md
├── AGENTS.md
└── .prjcontext/
    └── memory/
        ├── decisions.md        # decisions and rejected approaches, newest first
        ├── constraints.md      # external facts the code must respect
        ├── open-questions.md   # undecided items, with a default for now
        ├── current-state.md    # overwritten each session
        └── glossary.md         # project-specific terms

For a small project, a single memory.md with these five headings is fine. Split it when one section gets long enough that you scroll past it.

Then point your assistant at it. Assistants read their instruction file at the start of a session; they do not know about your memory folder unless that file tells them. Add a short section to CLAUDE.md or AGENTS.md:

## Project memory
- Before proposing a design change, read .prjcontext/memory/decisions.md.
  Do not reintroduce anything listed as rejected.
- At the start of a session, read .prjcontext/memory/current-state.md.
- If a question is listed in open-questions.md, ask me; do not decide it.
- Terms are defined in .prjcontext/memory/glossary.md.

Writing entries an assistant can use

An assistant reads your memory literally. Vague notes produce vague behaviour. Compare:

Before

- auth stuff is tricky, be careful
- we don't do it the old way anymore
- caching was a bad idea

After

- Auth: sessions are validated in middleware/session.ts only.
  Do not add checks inside route handlers.
- Replaced the REST client with the generated SDK on 2026-07-30.
  Do not import from lib/rest-client (kept for the migration script only).
- Removed the Redis cache on 2026-06-18: stale prices were shown after
  updates. Reads go straight to the database. Revisit only with
  per-product invalidation.

The rules behind the rewrite:

  • Name files, functions and folders. "The auth stuff" means nothing to a fresh session.
  • Date every decision. Dates let you and the assistant tell which entry is newer.
  • State the instruction, not just the history. "Do not import from X" is actionable. "We moved away from X" is not.
  • Give the reason in one line. The reason is what lets someone judge whether the decision still holds.
  • Leave out secrets and private paths. Memory gets shared. Write .env.example, not the values. See keeping secrets out of your AI context.

Keep it alive

Memory that is wrong is worse than no memory, because an assistant trusts it. A few habits prevent rot:

  1. Supersede, don't delete. Change Status: Active to Status: Superseded by 2026-09-10 entry. The old reason still explains old code.
  2. Overwrite current state every session. It should never be more than one session old.
  3. Close open questions. When one is decided, move it into decisions.md.
  4. Review monthly. Skim each file. Remove anything that would not change what someone does next.
  5. Change memory with the code. If a pull request reverses a decision, the memory update belongs in the same change.

A quick health check: open a fresh session, ask the assistant "What are the three most important decisions in this project, and what is currently in progress?", and compare its answer with reality. If it is wrong, your memory is missing something or saying it unclearly.

How PrjLab handles this

We built PrjLab so this memory travels with the project instead of staying on one laptop.

  • .prjcontext/memory/ is captured as project memory, with its own content type, next to your files, .prjcontext/instructions/ and .prjcontext/sessions/. CLAUDE.md and AGENTS.md at the root are recognised too.
  • Every prj push creates an immutable version you can open in the browser, with a list of changes and a line diff, so you can see exactly when a decision entry changed.
  • prj clone and prj pull bring the memory onto another machine with the code. prj pull never overwrites local edits; conflicting files are reported instead.
  • Repositories are private by default. Share by handle as a reader or writer so a teammate gets the same decisions you have. Details are in the getting-started guide.

Frequently asked questions

Does my assistant read .prjcontext/memory/ automatically? Not unless you tell it to. Assistants read their instruction files, such as CLAUDE.md or AGENTS.md. Add a short section there that says which memory files to read and when. Some assistants also keep notes of their own, but those usually live outside the project folder, so they do not travel with it.

How long should a memory file be? Short enough that you would read it before starting work. When a section needs scrolling, split it into its own file or archive old entries.

Should the assistant write memory itself? Let it draft, and review every change before it is saved. Assistants sometimes record a guess or a passing idea as a decision.

Is this different from a changelog? Yes. A changelog lists what shipped. Memory records why, what was rejected and what is still open.

Ready to keep your project memory with your code? Create a free account.