Record a milestone
A git log says what changed. It rarely says why the change was worth making, and never says which areas moved together. Six months later that is what somebody needs — usually while deciding whether they are allowed to undo it.
When to write one
For a change big enough that a newcomer would ask "why is this part like that?"
- a migration — a database, an auth model, a framework
- a rewrite of a module, or a service split in two
- a design reversal: something was done one way, then deliberately undone
Roughly a handful a year on an active repository. If you are writing one a week, they are commits, not milestones, and the file stops being readable.
When not to
- A task, or something in progress. That is
STATE.md, or a session record. - A single decision with no structural change. That is
/chamnan:remember→.chamnan/memory/decisions/. - A repeated procedure. That is
/chamnan:capture. - Anything with a status, an owner, or a due date. This is not project management, and adding those fields would quietly turn it into a worse version of a tool you already have.
Where it goes
.chamnan/milestones.md
One file, and entries are appended at the end. Newest last is deliberate: appending keeps every diff to added lines, where prepending would rewrite the context of the whole file each time.
The format
## 2026-08-20 — Authentication migration
**Why:** sessions dropped under load; the old design held state per node.
**Affected:** auth module, API layer
**Decisions:** short-lived tokens; the old endpoint stays for one release
Four parts, and the middle two carry the value:
| Date and title | ISO date, then a short name for the change |
| Why | The problem that made it worth doing. Not "we migrated auth" — what was going wrong. |
| Affected | Which areas moved together. This is what nobody can reconstruct later. |
| Decisions | Choices made along the way, especially anything ruled out. |
Leave out a field rather than filling it with a dash.
Write the why as the problem, not the activity. "Migrated to short-lived tokens" is the diff restated; "sessions dropped under load because the old design held state per node" is the reason, and it is the half that stops somebody re-treading it.
What reaches a session
Only the two most recent titles, with their dates. The bodies stay in the file, and the agent reads it when a title looks relevant. So a repository with forty milestones costs the same per session as one with two.
What not to put in it
- No credentials, hostnames or connection strings. This file is committed. The session-start hook redacts what it injects; that does not clean the file on disk.
- No incident write-ups. Name the failure in one clause; a post-mortem belongs in
docs/.
Housekeeping
Not pruned by age, on purpose — the oldest entry is usually the one nobody can reconstruct. Because only titles are injected, length costs nothing per session. Edit an entry if it turns out to be wrong; a false milestone is worse than a missing one.
Set "milestones": false in .chamnan/config.json to switch it off.