Concepts

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.

  1. Accepted ADRs and repository decisions
  2. Product memory (PRODUCT.md)
  3. Engineering standards
  4. Security and testing docs
  5. Module docs
  6. Feature plans
  7. Task files
  8. External context (MCP servers, tickets, designs)
  9. 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.md is 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.mdc has alwaysApply: true, so Cursor injects the rules into every request. It carries the same rules as AGENTS.md.
Codex and any other tool
AGENTS.md is discovered and loaded. It is always written, whichever --ai-tools you choose, so there is always a portable floor. A prompt hook wired in .codex/hooks.json looks 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, templateVersionThe Persist OS version that wrote the memory.
aiToolsWhich tools get entry points: claude, codex, cursor, generic.
docsDirRoot of the memory. Default docs.
featuresDir, modulesDir, adrDirDefaults docs/40-features, docs/30-modules, docs/adrs. Paths must stay inside the repository.
preCommitGatesCommands the pre-commit hook runs after persist doctor.
prePushGatesCommands the pre-push hook runs after persist test-gate.
testCommandThe one-shot test command for persist test-gate, or null to leave the gate off.
fenceEnabledWhether 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.