Repository memory
Six Markdown files under docs/, a decision directory, a small config file, and entry points that make each AI tool read them. The repository is the source of truth; chat history is context.
"Repository memory" is the set of files that answer why the code is the way it is: product intent, the decisions, the engineering standards, what the codebase already has and reuses, what went wrong before, and what must be protected. Persist OS creates the structure and keeps it honest. It does not fill in your architecture; the files are short templates with prompts, and every technology choice is yours to record.
What persist init writes
The memory itself is six documents and a decision directory:
docs/00-product/PRODUCT.md purpose, users, non-goals, current status
docs/20-security/SECURITY_MODEL.md auth, secrets, sensitive data, supply chain
docs/50-quality/QUALITY_GATES.md what must pass before work is done
docs/60-engineering/ENGINEERING_STANDARDS.md
docs/60-engineering/CONVENTIONS.md the canonical, reusable vocabulary
docs/60-engineering/LESSONS.md durable pitfalls, one line each
docs/adrs/README.md the decision index; ADRs live beside it
Around it, the entry points, gates, and configuration:
.persist/config.json paths, AI tools, gates, test command
.persist/hooks/pre-commit runs persist doctor + preCommitGates
.persist/hooks/pre-push runs persist test-gate + prePushGates
AGENTS.md rules and routing for every agent (always written)
CLAUDE.md Claude Code entry point; imports AGENTS.md
.claude/hooks/session-start.sh injects the memory map each Claude session
.claude/settings.json wires the SessionStart hook (only if absent)
.claude/hooks/context-prompt.sh looks up context cards per prompt
.codex/hooks.json wires the Codex prompt hook (only if absent)
.codex/hooks/context-prompt.sh looks up context cards per prompt
.cursor/rules/persist-memory.mdc always-apply Cursor rule
.github/workflows/persist.yml runs persist doctor on pull requests
.claude/skills/<skill>/SKILL.md 12 workflow skills (11 without --modules)
.claude/skills/security-review/scripts/scan-secrets.sh read-only secret scan
.agents/skills/<skill>/SKILL.md the same 12, portable layout
.agents/skills/security-review/scripts/scan-secrets.sh
--features adds docs/40-features/README.md and
--modules adds docs/30-modules/README.md. Neither is required:
persist feature create and persist module create create their
directories on demand, and the doctor never treats an absent feature or module
directory as an error.
The product file
One file holds product intent. Its four sections are the questions an agent most often has to guess at when they are not written down:
# Product: your-repository
## Purpose
Describe what this repository is building and why.
## Users
Describe who this is for and what success looks like for them.
## Non-Goals
Describe what this repository deliberately does not do.
## Current Status
Draft.
Keep product intent durable here. Do not rely on chat history as source of truth.
Source-of-truth order
When sources disagree, the higher one wins. The generated AGENTS.md tells
the agent to stop and report the conflict rather than pick.
- Accepted ADRs and repository decisions
- Product memory (
PRODUCT.md) - Engineering standards
- Security and testing docs
- Module docs
- Feature plans
- Task files
- External context (MCP servers, tickets, designs)
- Chat history
A map, not a dump
The files that load on every session stay short and point elsewhere. The doctor warns
when CLAUDE.md, AGENTS.md, and the Cursor rule together grow
past 24 KiB, because memory that becomes a wall of text stops being read. The Claude
SessionStart hook follows the same idea. It is a read-only shell script that lists the
accepted ADRs and modules and hands the agent pointers, not contents:
#!/bin/sh
# Persist OS Claude Code SessionStart hook.
# Generated by `persist init`. Injects a repository-memory map into every Claude Code session so a
# fresh agent reliably loads durable memory. Wired in .claude/settings.json. Read-only.
adrs=$(ls docs/adrs/ADR-*.md 2>/dev/null | sed 's|.*/||;s|\.md$||' | tr '\n' ' ')
modules=$(ls -d docs/30-modules/*/ 2>/dev/null | sed 's|docs/30-modules/||;s|/$||' | tr '\n' ' ')
context="Persist OS repository memory is the source of truth over chat history. Before non-trivial work, read AGENTS.md and the docs it routes to; repository rules override model preference. Accepted ADRs (docs/adrs/): ${adrs:-none yet}. Modules (docs/30-modules/): ${modules:-none yet}. Use the Persist OS CLI commands listed in AGENTS.md (persist feature/adr/module create, persist adr accept and supersede, persist doctor) yourself; do not web-search them. Run 'persist doctor' before claiming work complete."
printf '{"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"%s"}}\n' "$context"
How each tool loads it
- Claude Code
-
CLAUDE.mdis loaded automatically and contains@AGENTS.md, so the shared rules load with it. The SessionStart hook above runs at startup; it is wired in.claude/settings.json, which init writes only when no settings file exists. A prompt hook looks up context cards for every submitted prompt and injects the pointers. - Cursor
-
.cursor/rules/persist-memory.mdchasalwaysApply: true, so Cursor injects the rules into every request. It carries the same rules asAGENTS.md. - Codex and any other tool
-
AGENTS.mdis discovered and loaded. It is always written, whichever--ai-toolsyou choose, so there is always a portable floor. A prompt hook wired in.codex/hooks.jsonlooks up context cards for every submitted prompt and injects the pointers.
The rules block at the top of AGENTS.md is short and imperative: read the
memory before non-trivial work; match ceremony to scope; record substantial work with
the persist CLI; reuse what CONVENTIONS.md names; add a line to
LESSONS.md when something breaks non-obviously; never contradict an accepted
ADR, supersede it instead; run persist doctor before claiming work
complete; run the CLI yourself rather than asking the human to.
Skills
Twelve workflow skills are written to .claude/skills/ and
.agents/skills/: implement-task, the default for any code
change, write-tests, create-adr,
drift-review, completion-report,
module-memory (only with --modules),
plan-feature, security-review,
conventions-adherence, chestertons-fence,
adr-compliance, which checks a change against the accepted ADRs that
govern the files it touches, and context, which looks up the area memory
before starting and records it when done. Each is a
short SKILL.md that points at the memory it needs rather than repeating
it. security-review also ships
scripts/scan-secrets.sh, a read-only scan for common accidentally
committed secrets; the skill works with the script deleted, and init lists every
executable it writes. The 1.0 catalog of six grew back under ADR-0016: the restored
skills were rewritten in the current leaner format, and the still-retired ones point
at their replacements. persist skill create <name> adds a skill by
catalog name or as a skeleton for one of your own. The doctor warns only about the
four skills still retired left on disk after an upgrade, naming the replacement and
the command to remove each; your own custom skills stay silent.
Conventions and lessons
CONVENTIONS.md names the components, helpers, clients, types, and patterns
that already exist so the agent reuses them instead of writing a second one.
LESSONS.md holds one-line pitfalls that were painful to learn, grouped
by area under an Always section and area sections with
Applies To lists. The Always lessons load into every session; the areas
matching the task arrive with the pointers (at most two areas, three lessons each),
and a one-line index names the rest. CONVENTIONS.md still loads whole,
and the generated rules tell the agent to keep both files current itself; the human's
job is to review those edits in the pull request. The doctor warns when
CONVENTIONS.md is still a stub once the repository has real work, when
Always outgrows the few lessons every task needs, and when an area
applies to no file in the repository.
Feature memory
Features are opt-in. persist feature create <name> writes
docs/40-features/F-###-<slug>/ with PLAN.md (approach,
boundaries, acceptance criteria) and TASKS.md (tasks with scope, acceptance
and tests, plus a completion evidence section for files changed, tests run, results, and
remaining risks). When testCommand is set, TEST_PLAN.md is
added and required, so the scaffold only asks for a test plan when something enforces
it.
# Tasks: Checkout
## T1: Define Scope
Status: Todo
Scope:
- TBD
Acceptance:
- TBD
Tests:
- TBD
## Completion Evidence
Status: Pending.
Files Changed:
- TBD
Tests Run:
- TBD
Results:
- TBD
Remaining Risks:
- TBD
Module memory
Modules are also opt-in. persist module create <name> writes
docs/30-modules/<name>/ with MODULE.md (what the module
owns and does not own), TASKS.md, TEST_PLAN.md, and
DECISIONS.md. Absent features or modules are never an error; the doctor
reports zero folders and moves on.
The config file
.persist/config.json is small and yours to edit.
| Field | Meaning |
|---|---|
version, templateVersion | The Persist OS version that wrote the memory. |
aiTools | Which tools get entry points: claude, codex, cursor, generic. |
docsDir | Root of the memory. Default docs. |
featuresDir, modulesDir, adrDir | Defaults docs/40-features, docs/30-modules, docs/adrs. Paths must stay inside the repository. |
preCommitGates | Commands the pre-commit hook runs after persist doctor. |
prePushGates | Commands the pre-push hook runs after persist test-gate. |
testCommand | The one-shot test command for persist test-gate, or null to leave the gate off. |
fenceEnabled | Whether the Chesterton fence is active. Default true; set false to switch the fence check off. Nothing fence-related is generated either way except this toggle. |
A config written by an earlier release may contain a field that has since been removed. The doctor reports that as a config error with the exact line to delete, and the config-dependent checks are listed as not evaluated until it is fixed.