A maintained, repo-backed memory system that keeps AI coding agents continuous across hundreds of sessions — without relying on hidden model memory or chat-only notes.
The core idea: durable agent context lives in version-controlled files, split
into separate layers, each with exactly one job. A small, dependency-free
validator enforces the shape so the system stays small and usable instead of
decaying into one giant NOTES.md that nobody trusts. It's strict about file
shape precisely so your content, workflow, and runtime can stay loose —
opinionated about structure, agnostic about everything else.
Per-project memory, not per-user memory. The repo is the boundary — an agent working in one project can't see another's context, because isolation here is the substrate, not a setting.
No API key, no VPS, no daemon, no vector database, no custom harness. It
rides the Claude Code / Codex CLI you already
run — cp a folder, chmod +x, run one stdlib Python script. Works with any
agent that can read files in your repo. This is the current public, reusable
distribution of a memory model proven across hundreds of sessions in a
long-lived production project. It is maintained as the implementation, not as a
placeholder for a newer private version.
Agents forget everything between sessions. The usual fixes each fail in their own way:
- One big notes file grows without bound, mixes current truth with stale history, and becomes too long and contradictory to trust.
- Hidden / model memory isn't in the repo, can't be reviewed in a PR, and silently drifts from what the code actually does.
- Memory daemons and vector vaults (auto-capture + embeddings + a SQLite store) add real infrastructure to chase the same goal — and optimize recall when the problem is truth. (Why not a vault.)
The differentiator here is enforcement. Most "agent memory" projects are a
NOTES.md convention with nothing stopping the decay. This system makes memory
layered, repo-backed, and self-limiting: each fact lives in the smallest
file that owns it, current state is kept separate from history, and a
dependency-free validator fails CI when the shape rots.
The hard part
everyone else skips is the forcing function — that's the part this ships.
No single file carries more than one of these:
| # | Layer | File(s) | Holds |
|---|---|---|---|
| 1 | Agent contract | AGENTS.md (+ CLAUDE.md mirror) |
Small, stable behavior rules and the source-of-truth order |
| 2 | Runbooks | docs/runbooks/*.md |
Repeatable task workflows and completion checklists |
| 3 | Live task state | .git/agent-memory/tasks/ via tools/task_state.py |
Worktree-scoped objective, scope, next action, and blocker — never committed |
| 4 | Resume packets | docs/resume/*.md |
Bounded future-only state for deliberate agent/machine transfers |
| 5 | Topic memory | docs/memory/<topic>.md + topics.json |
Current durable facts and explicit domain ownership |
| 6 | Plans | docs/plans/*.md |
Deferred or multi-phase future work with an exit condition |
| 7 | Structured history | docs/repo_history/, indexed by CHANGELOG.md |
Immutable one-file-per-completion records, completed plans, and archived context |
One file ties the layers together without being one of them: PROJECT_MEMORY.md
is the router — a thin index into the topic memory (layer 5), kept small on
purpose. It's injected at session start so the agent boots with a table of
contents, not the whole encyclopedia, and the validator fails it the moment it
stops being an index.
Three rules tie them together:
- Live command output beats stored memory. Verify before claiming current state.
- Current memory beats historical context.
CHANGELOG.mdanddocs/repo_history/are audit context, not authority. - External notes and model memory are leads, not authority. Check them against the repo.
Assistant-style agents that pool everything into one identity — projects, devices, calendars, chats — get cross-domain context bleed by default, and have to add isolation back with profiles or separate instances. This inverts that: the unit of memory is the project, not the user, and isolation is the substrate rather than a setting.
It's one rule — scope memory to the smallest unit that owns it — at two zoom levels:
- Across projects: the repo is the wall. Another project's memory is files in a directory that were never injected, so an agent here can't be poisoned by them — there's no shared pool to leak from in the first place.
- Within a project: the seven layers and the thin router keep even a large monorepo from drowning in its own context; only the topic files a task needs get pulled in.
The pooled-memory approach separates by configuration. This separates by construction.
From the root of the repo you want to add memory to:
# 1. Copy the scaffold in (replace the path with this clone's location)
cp -r /path/to/long-running-project-memory/template/. .
# 2. Make the hooks and tools executable, then enable the staged-scope gate
chmod +x .claude/hooks/session-start-context.sh hooks/pre-commit tools/*.py
git config core.hooksPath hooks
# 3. Confirm the shape is valid
python3 tools/check_memory_system.py
# -> memory-system check passedThen customize (see Adopt it below) and commit.
The heavyweight memory tools distribute as software — so you inherit their install surface, upgrade path, and breakage. This distributes as a spec your agent can satisfy. The installer is the agent you already have.
It works because of what the artifact is made of: the files are markdown (an agent reads and writes them natively) and the success condition is a stdlib validator (a hard pass/fail the agent can target). So "set this up for project X" isn't a vague ask — it's a well-formed, verifiable task. Point your agent at this repo and say:
Adapt this memory system to fit my project, then make
python3 tools/check_memory_system.pypass.
None of the daemon/vault/embedding stacks can be installed that way — you can't hand an agent "run my SQLite vault and observer model" and have it be done and verifiable in one pass.
README.md You are here
LICENSE MIT
.gitignore
.github/workflows/memory-system.yml CI that validates the shipped template
template/ Drop-in scaffold — the whole system lives here
├── AGENTS.md Agent contract (canonical)
├── CLAUDE.md Byte-for-byte mirror of AGENTS.md
├── PROJECT_MEMORY.md Router / topic index
├── CHANGELOG.md Generated structured-history index
├── .github/workflows/memory-system.yml Ready-to-copy CI (runs the validator)
├── .claude/
│ ├── settings.json Registers the SessionStart hook
│ ├── hooks/session-start-context.sh Injects contract + router + live task state
│ └── skills/long-running-project-memory/ Claude skill (SKILL.md), bundled in-repo
├── .agents/
│ └── skills/long-running-project-memory/ Codex skill (SKILL.md + agents/ + references/)
├── docs/
│ ├── memory/README.md
│ ├── memory/topics.json Topic ownership and audit policy registry
│ ├── memory/workflow.md Starter topic for the memory system itself
│ ├── memory/_topic-template.md Current-only topic shape
│ ├── plans/README.md
│ ├── resume/README.md Explicit transfer-packet boundary
│ ├── repo_history/ Structured ledger + completed plans + archives
│ └── runbooks/ triage, task-state, docs-change,
│ memory maintenance and review
├── hooks/pre-commit Staged scope + memory-system gate
└── tools/ Semantic knowledge routing/audit, validator,
task state, scope, plans, and history
The runbooks shipped here are the memory-system ones. Add your own project
runbooks (build, deploy, release, verification...) beside them and list them in
AGENTS.md.
After the Quickstart copy, do the project-specific part:
- Write your contract. The scaffold works zero-edit with generic defaults.
Edit
AGENTS.mdonly for durable cross-task behavior, universal safety, and runbook routes. Put stack setup, deployment details, domain rules, and volatile project facts in the relevant runbook or topic memory. Keep the contract small; it is the one file every session reads. - Mirror it.
cp AGENTS.md CLAUDE.md(they must stay byte-for-byte identical; the validator enforces this). - Add real topics. The scaffold includes
docs/memory/workflow.mdfor the memory system itself. Copydocs/memory/_topic-template.mdtodocs/memory/<topic>.mdfor each project domain (e.g.deployment.md,frontend.md). Prefertools/project_knowledge.py create-topicso the topic file,topics.jsonownership registry, router, and transaction receipt update together. - Add project runbooks. Drop
docs/runbooks/<workflow>.mdfiles for your recurring tasks and reference them fromAGENTS.md. - Configure protected scope. Tailor
PROTECTED_PATTERNSintools/check_task_scope.pyfor runtime paths that should require explicit task authorization. Set a nonstandard primary branch withgit config memory.primaryBranch <branch>. - Validate, then let CI enforce it so routing, plans, history, transfer
packets, and required files cannot silently drift. Run it locally:
A ready-to-copy GitHub Actions workflow ships at
python3 tools/check_memory_system.py
.github/workflows/memory-system.yml— it runs the same check on every push and PR, no dependencies to install.
Not using Claude Code? Leave
.claude/inert — the file layout, runbooks, and tools stand on their own. The shipped validator checks the complete cross-agent scaffold; if you remove one assistant surface, tailor its required file list deliberately rather than leaving a half-installed integration.
Don't migrate everything at once — that just moves the big-ball-of-notes from one place to another. Convert lazily, in this order:
- Drop in the contract. Add
AGENTS.md, trim it to your real rules,cp AGENTS.md CLAUDE.md. This alone gives agents a stable starting point. - Sort your existing notes by layer, not by topic. Current facts →
docs/memory/<topic>.md; deferred work →docs/plans/; finished work → structured entries underdocs/repo_history/changelog/entries/; retired plans and old context →docs/repo_history/. - Create topic files only for domains you actually touch. Leave the rest until a task needs them — an empty topic is better than a stale one.
- Retire static handoff files. Use the project-native transient state
mechanism for live coordination and create a
docs/resume/packet only for deliberate pause or transfer. Preserve useful old handoff content in history instead of keeping the old file in startup context. - Add a runbook the first time you repeat a workflow, not before.
- Turn on CI and let
check_memory_system.pytell you what's still out of shape. Fix what it flags; ignore the rest until it matters.
- At session start, the Claude Code
SessionStarthook injectsAGENTS.md,PROJECT_MEMORY.md, Git-local task-state output, and any explicit resume routes, so the agent boots with the contract, live state, and an index — not the whole encyclopedia. - The skill — methodology for designing, auditing, and repairing the system
— is bundled in the repo, never installed globally, so it only loads where
the system is actually used. Claude Code auto-loads it from
.claude/skills/; Codex auto-discovers repo-scoped skills from.agents/skills/when launched inside the repository. Nothing is copied into~— per-project, by construction, exactly like the memory it manages. - During work, the agent routes through
PROJECT_MEMORY.mdto the one or two topic files it needs, follows a runbook, and keeps risky or multi-session work scoped in Git-local state.
tools/check_memory_system.py is pure Python stdlib (no install) and enforces
the invariants that keep the system honest:
- all required contract / runbook / skill / archive files exist;
CLAUDE.mdis a byte-for-byte mirror ofAGENTS.md;- static handoff files are absent from current startup and workflow surfaces;
- the agent contract remains below its enforced size ceiling and declares the boundary that routes project setup and history elsewhere;
- explicit resume packets are bounded and schema-valid;
- every
docs/memory/*.mdtopic file has the four current-only sections and is routed bidirectionally throughPROJECT_MEMORY.md; PROJECT_MEMORY.mdstays a thin router and includesdocs/plans/plusdocs/repo_history/;docs/plans/contains indexed future-only plans with exactly one status, next action, and exit condition;- structured history and completed-plan lifecycle checks pass;
- the topic registry, Markdown semantics, source-prose references, bundled
skills, and optional domain coverage pass
project_knowledge.py; - the
SessionStarthook is registered, executable, and injects task state; - the memory-review runbook keeps its anti-churn language.
Say an agent turns a future plan into a progress diary — exactly the rot a
NOTES.md would swallow silently:
# Opening explorer migration
Status: `Incomplete`.
Next action: Finish the bounded rebuild.
Exit condition: The acceptance gate passes.
+ - [x] Deployed commit abcdef123456 and live-verified it.CI goes red:
$ python3 tools/check_memory_system.py
memory-system check failed: docs/plans/opening-explorer.md contains checked work
$ echo $?
1The plan is forced to remain future-only; that result belongs in one structured
history entry. The same gate catches a CLAUDE.md that drifts from AGENTS.md:
$ python3 tools/check_memory_system.py
memory-system check failed: CLAUDE.md must be an exact byte-for-byte mirror of AGENTS.mdRun it locally, through the shipped pre-commit hook, and in CI — the scaffold
ships a GitHub Actions workflow at
.github/workflows/memory-system.yml that runs it on every push and PR with no
dependencies to install.
tools/project_knowledge.py is the portable sorting layer. It has no
agent_control.py, model, database, or network dependency. It can:
- create and route a topic atomically with
create-topic; - add, update, or move current facts and workflow steps with Git-local receipts;
- reject active task state from durable Markdown;
- audit every tracked Markdown file for stale references, mixed current/history sections, oversized contracts, and unintended duplicates;
- inspect source comments and docstrings for stale documentation references;
- validate skill resource links and size; and
- report unexplained code/config domains from
docs/memory/topics.json.
Start with advisory domain coverage so the scaffold can be adopted incrementally:
python3 tools/project_knowledge.py check
python3 tools/project_knowledge.py auditAfter the topic registry covers the repository, change coverage to strict.
Transaction receipts are also optional by default; enable
require_transaction_receipts only when the team wants all durable-memory edits
to go through the transaction commands or an explicit maintenance receipt.
The vector-vault approach (auto-capture + embeddings + a SQLite store, shared across runtimes) is the popular answer, and it's solving the wrong problem.
- A vault optimizes recall, not truth — and rot is a truth problem. Auto-capture ingests indiscriminately, so every stale, superseded, and contradictory fact goes in too. Retrieval is by similarity, and similarity is blind to currency: a deprecated fact is still relevant, so the vault happily surfaces it with a high score and presents it as current. There is no way to encode "this used to be true." This system's current topic memory beats repository history rule is an explicit authority hierarchy a vector store structurally cannot represent.
- Rot stays visible. Rot in a markdown file shows up as a line in a PR diff
a human catches. Rot in a vault shows up as a subtly wrong retrieval six
sessions downstream that you can't trace back to a cause — invisible and
unauditable, which is strictly worse than the
NOTES.mdit replaced. - What actually kills rot isn't storage or retrieval — it's legibility (you can read it), separation (current split from historical), and a forcing function (CI that fails when the shape rots). This repo is built entirely from those three.
Stated up front, because they're the real shape of the deal — not "set and forget," but "cheap to adopt, honest by construction":
- The validator checks shape, not substance. It guarantees structure, not truth — an agent can produce structurally valid files that are semantically hollow. The session-start hook, the runbooks, and a human in the PR loop are what keep the content honest.
- Capture and retrieval are manual. The agent has to write to the right file; there's no background extraction. That's a feature for people who distrust automatic memory and a dealbreaker for people who don't want to think about it.
- It caps volume in exchange for legibility. Grep-plus-router over markdown has a ceiling; the topic split, thin router, and preserve-don't-delete rules push it far out, but they don't remove it. For one long-lived codebase that's the right trade every time. "Remember everything across my whole org/life" is a different product — don't reach for this there.
CLAUDE.mdis a strict mirror by design. The byte-for-byte rule buys zero drift between the contract and what Claude reads, at the cost of putting assistant-specific shims in that file. They don't go there: per-assistant behavior belongs in agent config (.claude/settings, skills) or a section ofAGENTS.mdthat applies to everyone — not a divergingCLAUDE.md.- Agent-specific glue differs. Claude has the SessionStart hook under
.claude/; Codex uses repo-scoped skills under.agents/skills/plus the sharedAGENTS.mdcontract. Both stay inside the project.
- One concern per file. Behavior, workflow, live state, transfer state, durable fact, plan, and history never mix.
- Live state is local. Worktree-aware task records do not merge or compete across branches; tracked resume packets exist only for deliberate transfer.
- The router stays thin.
PROJECT_MEMORY.mdindexes; it is not the encyclopedia. - Reviews are self-limiting. When the automated check and the fixed rubric pass, the system is healthy — don't reopen the whole design every session.
- Preserve, don't delete. Move stale facts and completed evidence to
docs/repo_history/instead of leaving them in current memory or dropping them.
This repository is the canonical public distribution of the reusable system. It is not a compatibility snapshot or an announcement for a separate, newer implementation. Changes to the seven-layer model, its bundled skills, or its enforcement belong here in the same maintenance cycle as the production lesson that motivated them.
The root CI validates the complete shipped template—including both agent skill
surfaces, current Markdown hygiene, task-state self-tests, plan lifecycle,
structured history, and scope enforcement—on every push and pull request. A
clean clone of main is therefore the supported current version; consumers can
compare or recopy the template without guessing which private revision is
newer.
MIT — see LICENSE.