Two weeks pass. You open the project, open a fresh Claude Code session, and type "continue where we left off". That session has never seen the old one. It reads a few files, forms a confident theory about what you were doing, and starts on something you had already decided against.

The idea

Between sessions, only the repository survives. That is not a problem as long as the repository contains a pointer: one short file, at a path you always use, that says which piece of work is active, what state it is in, what "finished" means for it, and what the single next step is. You are writing a note to a stranger who has read your code but attended none of your meetings.

code
  docs/PROGRESS.md   ← the pointer. Read first, every session.
  │
  ├─ Active: section 4, waitlist emails
  ├─ Done:   schema + insert path, verified against local Postgres
  ├─ Done means: a real address gets a confirmation mail in the inbox
  ├─ Next:   wire the Resend template, then re-run scripts/smoke.sh
  └─ Ruled out: sending from the client. Rate limits. See DECISIONS.md:112
  │
  └──► docs/plans/04-waitlist.md   the detail, only when needed

How it works

The pointer answers four questions, in this order.

  • Where am I? The name of the current piece of work, and where its detail lives. One line, plus a path.
  • What is actually finished? Finished means verified, not written. "Signup form built" is a claim. "Signup form built, submitted a real address, row appears in the waitlist table" is a fact. Write the second kind.
  • What is the next step? One concrete action, small enough to start cold. "Continue the feature" is not a step. "Add the Resend call in src/server/email.ts, then run pnpm check" is.
  • What did I already rule out? This is the part people skip and the part that saves the most time. Without it, a fresh session will confidently re-propose the approach you spent an afternoon rejecting.

Two conventions make it work. First, a fixed path. If the pointer moves, nobody finds it, so docs/PROGRESS.md at the same place forever beats a cleverer scheme. Second, a size limit. Past roughly sixty lines a pointer has turned into a diary, and a diary does not get read. Older entries move to a history file and the pointer stays lean.

What to do

  1. Create docs/PROGRESS.md today, even on a project that has been running for months. Fill it in from what you currently remember, which is more than you will remember next month.
  2. End every session by updating it before you close the terminal. Two minutes. The moment you have stopped is the moment you know the most.
  3. Add one line to your project CLAUDE.md: read docs/PROGRESS.md before doing anything. That connects the file to the session, and turns the next cold start into a thirty-second orientation.
  4. Commit it. A pointer sitting uncommitted in your working folder is a pointer that disappears when you switch .

Where it breaks

A stale pointer is worse than none, because it is believed. If you fix something in a hurry and do not update the file, the next session starts from a false picture and every conclusion after that inherits the error. Treat the pointer as part of the change, the same way you would treat a test.

It also cannot record the things you never wrote down. The reason you chose Postgres over SQLite, the client's constraint that shaped the schema, the bug you found and worked around: those need a decisions log, not a status line, and the pointer should link to it rather than absorb it. Finally, if the gap is long enough, the world moved. Dependencies have new versions, the deploy target changed its dashboard, an API you called was deprecated. The pointer tells you where you stopped. It cannot tell you what changed underneath you while you were gone.