Agent Brain

Enforced, persistent decision memory for AI code agent teams — that survives context compaction. Agents log decisions and outcomes, learn from past rejections, and resume their pending roadmap after a /compact instead of re-researching it. Hooks make it mandatory (not "stored and hoped for"). And code reads route through SAN compression (~80% fewer tokens, tokenizer-measured) so the context window fills slower in the first place.

Portable across agent runtimes — your knowledge isn't trapped in one tool. Agent Brain is an MCP server, so Claude Code and Codex can both drive the same brain with MCP tools, standing protocol, and lifecycle hooks — switch agents for price or capability without resetting your project's accumulated decisions and SAN. Any MCP host works (Cursor, Cline, Continue, …). See docs/adapters.md; run ./setup.sh for the easiest install.

Contents

What This Does

AI coding agents start fresh every session: no memory of past decisions, no learning from rejections, no cross-agent knowledge sharing — and they burn tokens re-reading the same source files task after task. Agent Brain fixes both:

  1. Memory — decisions, outcomes, and review feedback persist across sessions and agents:
Agent    → pre_check()    → "WARNING: similar approach was rejected last week"
Agent    → log_decision() → records what you decided and why
Agent    → does work      → PR created
Reviewer → log_outcome()  → "rejected: violates DIP (dependency inversion)"
Next time, any agent → pre_check() → sees that rejection → avoids the mistake
  1. Cheap code reading — the optional SAN protocol compresses source files to ~17-27% of their original tokens (81% saved, tokenizer-measured), and token_savings shows you exactly how much it saved, per session, in numbers and %.

Features

Feature What it does
Decision Memory Log decisions, outcomes, feedback. Persists across sessions.
Pre-Check Warnings Before starting work, see past failures in the same area.
Fuzzy Matching "Rate limiting on signup" finds "rate limiting on login" rejection.
Code Bridge Link decisions to code symbols: "Show me all decisions that touched AuthService." (Richer with the optional code-review-graph MCP server; works standalone too.)
Agent Scorecards Acceptance rate, trends, top rejection categories per agent.
Adaptive Warnings Agents with high rejection rates get stricter pre-check warnings.
Team Dashboard All agents at a glance — for project managers.
SAN Protocol Compress code to ~20% of original tokens (81% saved, measured). Full codebase fits in context.
Token Savings Tracker token_savings reports tokens saved this session / today / all-time, with %.
Enforcement Hook Code edits are blocked until the agent logs a decision — memory actually gets populated.
Survives compaction SessionStart hook re-injects the pending roadmap after /compact so the agent resumes instead of re-researching. Details
Relevance search query_decisions(query="…") ranks by topic relevance, not just recency; get_roadmap returns open work in one call.
SAN as default read path Hooks on both Read and Bash route raw code reads/dumps to get_san (per-file nudge, optional hard-block), so agents actually use SAN instead of cat/Read. A tool ladder (find→read→grep-literal→edit) is auto-installed to CLAUDE.md. Details
Records & pruning Browse every decision as dated markdown; prune old/resolved ones (dry-run first, archived not deleted). Keeps the brain lean without losing the lessons. Details
Live decisions web view A /decisions browser that streams decisions in real time as agents log them — filter by repo/area/agent/outcome, search, color-coded. Details
SAN search trace A /san view that shows how a query finds code through SAN — index → content → block → src: line range, live. Details

Quick Start

Recommended:

git clone https://github.com/sandeep84397/agent-brain.git
cd agent-brain
chmod +x setup.sh
./setup.sh

./setup.sh installs Agent Brain for every supported runtime it detects on your machine: Claude Code, Codex, or both. If you want to force one runtime:

./setup.sh --claude
./setup.sh --codex
./setup.sh --all

The setup wizard will:

  • Create a Python venv and install dependencies
  • Prompt for your repo paths (or use the template config)
  • Register the MCP server globally with Claude Code and/or Codex
  • Install the brain hooks (decision-gate, amnesia re-inject, Read/Bash→SAN routing)
  • Add the SAN tool-ladder to ~/.claude/CLAUDE.md for Claude Code or ~/.codex/hooks.json guidance for Codex — no manual edit needed (idempotent; safe to re-run)
  • Offer to customize agent names interactively
  • Run verification checks

No setup.sh? The server works standalone. Just pip install mcp networkx and register manually:

claude mcp add --transport stdio --scope user agent-brain -- python3 /path/to/server.py

The server gracefully handles a missing config.json — it starts with an empty brain.

Where things land: setup.sh installs a copy of the server and hooks to ~/.agent-brain/ with its own venv — that installed copy is what Claude Code and Codex run. The repo checkout keeps the source. CLI examples in this README use python3 brain/server.py <cmd> from the repo root after installing dependencies; against the installed copy, the equivalent is ~/.agent-brain/.venv/bin/python ~/.agent-brain/server.py <cmd>. If you edit the repo copy, re-run setup.sh and restart your agent runtime.

Existing users: upgrade to Claude + Codex

If you already installed Agent Brain before Codex support, update the checkout and re-run the same command:

git pull
./setup.sh

The installer is idempotent. It preserves ~/.agent-brain/config.json, decisions.json, decisions.journal, records, metrics, and existing Claude agent files unless you explicitly choose to overwrite templates. It refreshes the installed server and hooks under ~/.agent-brain/, keeps Claude Code wired, and adds Codex config when Codex is detected.

For each repo you want both runtimes to use:

./setup.sh --link-project=/absolute/path/to/your/project

Then restart Claude Code and Codex. In Codex, run /mcp and /hooks; trust the Agent Brain hooks when prompted. Verify with:

~/.agent-brain/.venv/bin/python ~/.agent-brain/server.py diagnose --project=/absolute/path/to/your/project

Let an AI agent upgrade it

If an AI coding agent already has terminal access to your Agent Brain checkout, you can ask it to do the upgrade for you:

Update Agent Brain for Claude and Codex compatibility in this repo, link this
project, then run diagnose and validation.

The agent should run:

git pull
./setup.sh
./setup.sh --link-project="$(pwd)"
~/.agent-brain/.venv/bin/python ~/.agent-brain/server.py diagnose --project="$(pwd)"
~/.agent-brain/.venv/bin/python ~/.agent-brain/server.py validate

The only manual step Codex may still require is hook trust: after setup, restart Codex, run /hooks, and trust the Agent Brain hooks. That trust step is intentional Codex safety behavior for non-managed command hooks.

Codex setup

When Codex is installed, ./setup.sh writes:

  • ~/.codex/config.toml[mcp_servers.agent-brain] using the installed brain venv
  • ~/.codex/hooks.json — decision gate, roadmap injection, brain-before-research nudge, and SAN Read/Bash routing

After install, restart Codex. In Codex, run /mcp to confirm agent-brain is enabled, then run /hooks and trust the Agent Brain hooks when prompted. Codex requires explicit trust for non-managed command hooks.

To link a project for all supported runtimes:

./setup.sh --link-project=/absolute/path/to/your/project

This writes Claude project MCP files and appends an idempotent Agent Brain protocol block to <project>/AGENTS.md for Codex. Restart your agent runtime in that project so the instructions and MCP config reload.

Linking a project (so subagents can use brain)

./setup.sh registers brain at the user level. For project-local guidance and subagent/project wiring, run:

./setup.sh --link-project=/absolute/path/to/your/project

This is idempotent and writes/merges:

  • <project>/.mcp.json — adds the agent-brain server entry alongside any existing entries
  • <project>/.claude/settings.local.json — sets enableAllProjectMcpServers: true and adds agent-brain to enabledMcpjsonServers
  • <project>/AGENTS.md — adds the Agent Brain protocol for Codex
  • <project>/.gitignore — appends .mcp.json, .san/.san_hashes.json, .san/_cache/

After running it, restart Claude Code and/or Codex in the project, then verify:

~/.agent-brain/.venv/bin/python ~/.agent-brain/server.py diagnose --project=/absolute/path/to/your/project

Subagents not seeing brain tools? See the 4-layer model under Verification.

How To Use It

Once set up, you don't call brain tools yourself — your agents do, automatically, as part of their normal work. Your job is just to give agents tasks and (optionally) review the memory that builds up.

The loop every agent runs

For any non-trivial task, an agent follows this cycle (enforced by the hook — see Enforcement Hook):

1. pre_check(agent, area, action)     ← "has anyone tried this before? did it fail?"
2. log_decision(agent, repo, area,    ← records the plan; unlocks code edits
                action, reasoning,
                handoff_summary?, validation?, git metadata?)
3. … writes the code …
4. log_outcome(decision_id, outcome,  ← records accepted / rejected / failed + why
               outcome_by, reason)

You just say "add rate limiting to the signup endpoint". The agent does the rest.

Worked example — across two sessions

Monday — a decision gets rejected:

You:   "Add rate limiting to /login"
Agent: pre_check(agent="karan", area="auth", action="rate limit login")
       → "No past failures in 'auth'. Proceed."
Agent: log_decision(... action="in-memory counter per IP", reasoning="simplest")
       → dec_20260609_..._a1b2c3
Agent: …writes code, opens PR…
PE:    log_outcome(dec_..._a1b2c3, outcome="rejected", outcome_by="marcus",
                   reason="in-memory won't survive multi-instance deploy; use Redis")

Friday — a different agent, a related task, a different machine/session:

You:   "Add rate limiting to the signup endpoint"
Agent: pre_check(agent="dev", area="auth", action="rate limit signup")
       → "SIMILAR REJECTIONS (1, 78% match):
          [2026-06-09] karan tried: in-memory counter per IP
          REJECTED by marcus: in-memory won't survive multi-instance deploy; use Redis"
Agent: …goes straight to a Redis-backed limiter, skips the mistake…

No human re-explained the Redis constraint. The brain carried it forward.

How it behaves

When an agent calls… What happens What you see
pre_check Searches past decisions in the same area + fuzzy-matches similar actions across all areas Agent mentions relevant past rejections before coding
log_decision Appends the decision to the journal + drops a marker file Code edits are now unblocked for ~30 min
Edit/Write without a recent log_decision PreToolUse hook blocks the edit (exit 2) Agent is forced to log a decision first, then retries
log_outcome (rejected) Records the rejection; raises that agent's rejection rate Future pre_checks surface it; repeat offenders get stricter warnings
Any tool with a repo/status Updates the live office dashboard Agent appears working/reviewing/blocked at localhost:3333

What you get out of it (outcomes)

Outcome How it helps
Mistakes aren't repeated A rejection logged once warns every agent, every future session — even on a different machine.
No re-explaining context Constraints ("use Redis", "don't bypass the auth middleware") live in the brain, not in your head.
Cross-agent learning What backend-engineer learns, frontend-engineer and QA see. Knowledge is team-wide, not per-agent.
Accountability & trends Scorecards show each agent's acceptance rate and recurring failure patterns — agent_scorecard("karan", detail=True).
Auditable history "Why did we build it this way?" → decisions_for("AuthService.login") returns every decision that touched it, with reasoning and outcome.
Enforced discipline The hook means the memory actually gets populated — agents can't silently skip logging and edit code anyway.

Inspecting the memory yourself

You rarely need to, but from the repo root (where you cloned agent-brain):

python3 brain/server.py stats        # overall health: how many decisions/agents/repos
python3 brain/server.py office       # who's working on what right now
python3 brain/server.py savings      # tokens SAN saved (last session / today / all-time)

From any agent/MCP client you can also ask in plain language — "show me the team dashboard", "what decisions touched the payment service?", "what's karan's scorecard?", "how many tokens did SAN save this session?" — and the agent picks the right tool (team_dashboard, decisions_for, agent_scorecard, token_savings).

Architecture

┌─────────────────────────────────────────────────┐
│  Your Machine (global)                          │
│                                                 │
│  ~/.agent-brain/                                │
│  ├── server.py        ← MCP server (17 tools)  │
│  ├── config.json      ← your repos + team      │
│  ├── decisions.json   ← memory snapshot         │
│  └── decisions.journal← append-only deltas      │
│                                                 │
│  ~/.claude/agents/                              │
│  ├── project-manager.md                         │
│  ├── product-owner.md                           │
│  ├── principal-engineer.md                      │
│  ├── backend-engineer.md                        │
│  ├── frontend-engineer.md                       │
│  └── qa-engineer.md                             │
│                                                 │
│  project-repo/.san/   ← SAN-compressed code     │
│  ├── _index.json                                │
│  └── src/**/*.san                               │
│                                                 │
│  dashboard/           ← 3D office + decisions   │
│  ├── server.py        (python, zero deps)       │   + SAN search (web)
│  └── static/          (Three.js + SSE)          │
└─────────────────────────────────────────────────┘

Performance & internals

The brain is built to stay fast as the decision history grows into thousands of entries:

Concern How it's handled
Reading the graph decisions.json is parsed once and held in an in-memory cache keyed on file mtime+size — repeat tool calls in a session reuse it (~0.03ms vs ~140ms re-parse). The cache self-invalidates if another session writes the file.
Writing a decision Writes are O(delta), not O(graph). decisions.json is a periodic full snapshot; decisions.journal is an append-only log of mutations. Logging an outcome on a ~4MB brain appends ~800 bytes instead of rewriting 4MB. The journal auto-compacts back into the snapshot once it passes 256KB.
SAN freshness The freshness sweep (stat every indexed file + scan the .san/ tree) is debounced to once per 60s per repo, so bursts of get_san/query_san calls don't each pay for it.
Bounded responses Every list/detail tool caps its output (row limits, per-field truncation) so one giant decision can't blow up a response. Stored text fields are capped at write time too.
Multi-session safety Each Claude Code session runs its own server process sharing ~/.agent-brain/. Writes use os.replace with pid-unique temp files to avoid cross-process rename collisions.

Files in ~/.agent-brain/: decisions.json (snapshot) + decisions.journal (deltas) are the decision memory — both are needed; don't delete one without the other. office-state.json is live dashboard state (self-pruning), san_savings.jsonl is the token-savings log. All are per-machine and git-ignored.

MCP Tools (22)

Core (every agent uses these)

Tool Purpose
pre_check Past failures before starting work + plan pointers, escalation hints, model routing, SAN coverage (pass repo=)
log_decision Record what you decided and why; optional plan_file, handoff summary, git metadata, validation, blockers, deferred work, and do-not-touch notes make future resume cheaper
log_outcome Record accepted/rejected/failed after review
log_feedback Reviewers log feedback on decisions

Query

Tool Purpose
query_decisions Filter by area/agent/repo/outcome and rank by free-text relevance (query="...") — finds decisions about a topic without knowing the exact area
get_decision Full detail + feedback for one decision
get_roadmap What's left to do — pending + roadmap/blocker-tagged work, ranked. One call to resume context after a fresh session or compaction
get_resume_context Compact repo/area digest: latest handoff, open work, validation, hygiene nudges, SAN visibility, and next action

Records & pruning (keep the brain lean)

Tool Purpose
export_records Write a human-readable audit trail to ~/.agent-brain/records/ — one markdown file per day, browsable via INDEX.md
prune_decisions Forget old, resolved decisions: archives to decisions.archive.jsonl (recoverable) and removes from the live graph. Dry-run by default; keeps rejections + roadmap (the learning)
resolve_stale_pending Mark long-abandoned pending decisions as superseded so they stop polluting get_roadmap. Dry-run by default

Code Bridge

Tool Purpose
decisions_for Decisions touching a code symbol or file (auto-detected)
code_impact Blast radius: code symbols + callers

Patterns

Tool Purpose
get_patterns Cluster recurring rejections; pass action to find similar past failures

Scorecards

Tool Purpose
agent_scorecard Stats for one/all agents; detail=True for trends + advice
team_dashboard All agents at a glance (limit caps rows)

Office Dashboard

Tool Purpose
heartbeat Report agent status (working/idle/discussing/blocked) for live dashboard
detect_stalls Find agents with open decisions but no activity for N minutes (default 5)

SAN (Structured Associative Notation)

Tool Purpose
recompile_san Refresh SAN metadata: rebuild index, clean orphans, update hashes. dry_run=True for a freshness report only. Does NOT generate content.
query_san Find code instead of grepping raw source — keyword search over index + content, far fewer tokens
get_san Read code instead of raw Read — same structure, ~5x fewer tokens (detail="full") / ~11x (detail="sig"). file_path may be relative or absolute (repo auto-detected); max_chars caps output
token_savings Tokens saved by SAN this session / today / all-time — number + %

Admin (CLI only — not exposed via MCP)

Run from the repo root. These live on the CLI (not MCP) to keep the agent-facing tool surface lean:

python3 brain/server.py validate        # full brain self-tests (117 checks)
python3 brain/server.py validate-san    # SAN subsystem self-tests
python3 brain/server.py san-index <repo> # rebuild _index.json from .san/
python3 brain/server.py stats           # overall brain health
python3 brain/server.py office [repo]    # current office state (debug)
python3 brain/server.py savings         # SAN token savings (last session / today / all-time)
python3 brain/server.py roadmap [repo]   # open-work digest (same as get_roadmap)
python3 brain/server.py records [repo]   # export the dated records dir
python3 brain/server.py prune [repo] [--before-days=N] [--apply]  # forget old decisions (dry-run default)
python3 brain/server.py resolve-stale [repo] [--apply]            # mark abandoned pending as superseded

Managing what the brain remembers

The brain only grows — it learns, it never forgets on its own. That's good for the valuable memory (what failed and why) but old, resolved decisions eventually add noise to pre_check and inflate get_roadmap. Two safe ways to keep it lean:

1. Browse the records. Every decision is exportable as dated, human-readable markdown:

python3 brain/server.py records          # writes ~/.agent-brain/records/YYYY-MM-DD.md + INDEX.md
open ~/.agent-brain/records/INDEX.md     # browse by date

Each entry shows the date, repo, area, agent, action, and outcome. The records are regenerated from the graph — deleting a day file just re-renders on the next export, so the graph stays the source of truth. To actually forget something, prune it.

2. Prune (or let the AI do it). prune_decisions is dry-run by default — it shows what would go without touching anything:

python3 brain/server.py prune --before-days=90          # preview: old, resolved decisions
python3 brain/server.py prune --before-days=90 --apply  # archive + remove for real

What it does on --apply:

  • Archives each pruned decision to ~/.agent-brain/decisions.archive.jsonl (recoverable — nothing is hard-deleted).
  • Removes it from the live graph, so pre_check/get_roadmap stop surfacing it.
  • Keeps the learning: rejections/failures and roadmap/blocker-tagged decisions are never pruned.
  • Re-exports the records dir to reflect the change.

You can also just ask an agent: "prune decisions older than 3 months, dry run first" — it calls prune_decisions(before_days=90, dry_run=True), shows you the list, and only applies after you confirm.

Is unbounded growth a problem? Disk is trivial (~28 MB/year) and reads stay sub-millisecond (mtime-cached). The real cost is relevance noise — a 2-year-old rejected approach surfacing as a "similar failure". Prune resolved/old decisions periodically; keep the rejections, which are the cheapest lesson you'll ever get.

Agent Team

The repo includes 6 agent templates. Each has the Brain Protocol baked in:

Role File Responsibility Pinned model
Project Manager project-manager.md Coordination, tracking, blockers Haiku (cheap coordination)
Product Owner product-owner.md PRDs, acceptance criteria Sonnet
Principal Engineer principal-engineer.md Architecture, SOLID, reviews Opus (review is high-leverage)
Backend Engineer backend-engineer.md API, services, data layer Sonnet
Frontend Engineer frontend-engineer.md UI, app logic, integration Sonnet
QA Engineer qa-engineer.md Test plans, validation, quality gates Sonnet

Model pins live in each template's model: frontmatter — change them to fit your budget. See Model Routing for the full strategy.

Scale by duplicating templates (e.g., backend-engineer-2.md).

Placeholders

Each template has {{ROLE_NAME}} / {{ROLE_NAME_LOWER}} placeholders:

File Placeholders
project-manager.md {{PM_NAME}}, {{PM_NAME_LOWER}}
product-owner.md {{PO_NAME}}, {{PO_NAME_LOWER}}
principal-engineer.md {{PE_NAME}}, {{PE_NAME_LOWER}}
backend-engineer.md {{BE_NAME}}, {{BE_NAME_LOWER}}
frontend-engineer.md {{FE_NAME}}, {{FE_NAME_LOWER}}
qa-engineer.md {{QA_NAME}}, {{QA_NAME_LOWER}}

setup.sh offers to replace these interactively. Or do it manually:

sed -i 's/{{BE_NAME}}/Arjun/g; s/{{BE_NAME_LOWER}}/arjun/g' ~/.claude/agents/backend-engineer.md

Already have custom agents?

If you already have agent .md files, don't overwrite them. Instead, add the Brain Protocol block to each:

# Brain Protocol
Before starting any task:
1. Call `pre_check(agent="<name>", area="<area>", action_description="<plan>")`
2. If warnings exist, adjust approach
3. Call `log_decision(agent="<name>", repo="<repo>", area="<area>", action="<plan>", reasoning="<why>", files_touched=["<paths>"])`
After feedback:
4. Call `log_outcome(decision_id="<id>", outcome="<result>", outcome_by="<who>", reason="<why>")`
NON-NEGOTIABLE.

Critical: do NOT set the frontmatter tools: field. Claude Code subagents inherit ALL tools from the parent session — including every mcp__agent-brain__* tool — only when tools: is omitted. Setting it (even with ToolSearch included) turns it into a literal allowlist that silently strips MCP tools, because mcp__* is not a valid wildcard. Reference: Claude Code subagents — Available tools.

---
name: my-agent
description: ...
model: claude-sonnet-4-6
# No `tools:` — inherits everything from the parent session, including MCP.
# To restrict tools, use `disallowedTools:` instead.
---

What if I really must restrict tools? Add ToolSearch to your tools: allowlist and bootstrap brain tools at the top of every task with ToolSearch(query="agent-brain", max_results=25). This is a fallback for the rare case where you genuinely need a tool denylist; for normal use, omit tools: entirely.

For reviewers (PE, QA), also add:

5. Call `log_feedback(agent="<name>", decision_id="<their-id>", feedback="<detail>", severity="blocker|warning|info")`

setup.sh shows this snippet if it detects existing agents (choose [m] for manual).

Model Routing (quality per cost)

Spend the expensive model where mistakes are costly to undo; spend the cheap ones where mistakes are cheap to fix. The brain supports this in three layers:

1. Per-role model pins

Each agent template pins a model in frontmatter (model: claude-sonnet-4-6). Defaults follow the phase-cost logic:

Phase Work Model Why
Plan / architecture System design, module boundaries, implementation plan Fable / Opus A wrong architecture costs days of rework; one good plan makes every later step cheaper
Scaffolding / boilerplate Project setup, DI wiring, data classes, mappers Sonnet / Haiku Pattern-matching, not reasoning — executing against the plan, not deciding
Core / complex logic Encryption flows, state machines, tricky concurrency Opus, escalate on failure Start mid-tier; escalate only when the data says so (see below)
Review Architecture + code review of cheap-model output Opus / Fable Read-heavy, write-light — high leverage per output token
Tests / docs / polish Unit tests against spec, KDoc, README Sonnet / Haiku Cheap-model territory

2. model_routing config

Declare your routing once in ~/.agent-brain/config.json:

"model_routing": {
  "plan": "fable",
  "implement": "sonnet",
  "review": "opus",
  "boilerplate": "haiku",
  "escalate": "fable"
}

Every pre_check response then ends with one line — MODEL ROUTING: plan=fable | implement=sonnet | review=opus | boilerplate=haiku | escalate=fable — so whatever agent is orchestrating spawns subagents on the right tier without you re-explaining the strategy each session. Omit the key and the line disappears.

3. Two-strikes escalation (data-driven)

Repeated failed attempts on a cheap model can cost more than one clean shot on a strong one — but you don't know which problems are "strong-model problems" until the cheap model stumbles. The brain already logs every rejection, so it applies the two-strikes rule automatically:

When the same agent has ≥2 rejected/failed decisions in the same area, pre_check returns: ESCALATION HINT: 'arjun' has 2 rejected/failed decisions in 'auth'. Two-strikes rule: do NOT retry on the same model tier — re-spawn this task on fable.

The escalation target comes from model_routing.escalate (generic wording if unset). This is per-agent — another agent entering the same area is not escalated by someone else's failures.

4. Plan files as handoff artifacts

Pay for deep thinking once, reuse it across many cheap executions. The planner writes the plan to a file and logs it:

log_decision(agent="marcus", repo="my-app", area="payments",
             action="Designed payment module architecture",
             reasoning="...", plan_file="docs/plans/payments-plan.md")

Every later pre_check in that area surfaces it:

PLAN AVAILABLE: docs/plans/payments-plan.md (by marcus, 2026-06-12).
Read it before re-deriving the approach — execute against it, don't re-plan.

The pointer stays active while the decision is pending or accepted; a rejected plan stops being advertised.

Cost mechanics that matter as much as model choice

  • Context discipline beats model choice. A Sonnet call with clean context beats an Opus call drowning in irrelevant files. SAN reads (~20% of raw cost) + pre_check (past failures only, not full history) are the brain's context discipline.
  • The orchestrator burns its own tokens. Spawning a Sonnet subagent from an Opus session still pays Opus rates for coordination. Cheapest pattern: cheap main session as orchestrator, escalate via subagents — not an expensive main session delegating down.
  • Fewer turns > cheaper tokens. For a genuinely complex task, a strong model finishing in fewer turns can land near mid-tier pricing. Don't be dogmatic — the two-strikes hint exists precisely to catch this case from real outcome data.

Rough split to aim for: ~70% of tokens on Sonnet-tier, ~25% on Opus-tier, ~5% on Fable-tier — that 5% (architecture + final review) determines whether the output is actually good.

Brain Protocol

Every agent must follow this before starting work:

1. pre_check(agent, area, action_description)
   → See past failures. Adjust approach if warnings.

2. log_decision(agent, repo, area, action, reasoning)
   → Record your plan before implementing.

3. [do the work]

4. log_outcome(decision_id, outcome, outcome_by, reason)
   → Record what happened after review.

This is enforced in every agent's .md file as NON-NEGOTIABLE.

The hooks (5)

Most "AI memory" tools are passive — they store things and hope the agent uses them. Agent Brain enforces via Claude Code hooks, so memory and SAN actually get used. setup.sh installs all five idempotently:

Hook Event Purpose
enforce_brain_protocol.py PreToolUse Edit|Write Blocks a code edit unless a decision was logged first — so the brain actually gets populated
inject_brain_context.py SessionStart startup/resume/compact Re-injects the open-work digest after compaction — the amnesia fix
remind_brain_before_research.py PreToolUse Workflow Nudges a brain check before expensive fan-out research
route_read_to_san.py PreToolUse Read Routes raw Read of SAN-covered code → get_san (per-file, escalating)
route_bash_to_san.py PreToolUse Bash Closes the shell bypass: cat/sed-dumping a SAN-covered file → get_san (leaves discovery grep alone)

All fail-open (any error → allow, never break a session) and respect BRAIN_SKIP_ENFORCE=1.

Enforcement Hook

Text in .md files is advisory — agents can skip it. The enforcement hook makes it mandatory: any Edit/Write to code files is blocked if no log_decision was called in the last 30 minutes.

How it works:

  1. log_decision() writes a marker file (~/.agent-brain/.last_decision_marker)
  2. A PreToolUse hook fires before every Edit/Write
  3. If marker is missing or stale (>30min), the hook blocks with exit code 2
  4. Claude sees the block reason and calls log_decision before retrying

Skips (no block): .md, .json, .yaml, .toml, config files, .claude/, .git/, .san/, node_modules/, build/

Custom skip patterns — extend the built-in skip list with fnmatch globs in ~/.agent-brain/config.json:

{
  "hook_skip_paths": [
    "**/docs/**",
    "**/.github/**",
    "**/CHANGELOG*",
    "**/migrations/**"
  ]
}

Patterns are matched against the absolute file path. The hook fails open: an invalid hook_skip_paths value is ignored silently rather than blocking your session.

Install (setup.sh does this automatically):

// ~/.claude/settings.json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "python3 /path/to/agent-brain/brain/hooks/enforce_brain_protocol.py",
            "timeout": 5000
          }
        ]
      }
    ]
  }
}

Fail-open: If the marker file is corrupt or the hook script errors, it allows the edit (exit 0). The hook never crashes your workflow — it only blocks when it's confident no decision was logged.

Bypass for direct edits: The hook fires on all Edit/Write — agents and user alike. To skip enforcement when you're editing directly, set BRAIN_SKIP_ENFORCE=1 in your shell before launching Claude Code, or add it to your settings.json env block:

{ "env": { "BRAIN_SKIP_ENFORCE": "1" } }

Agents spawned via the team system won't inherit this, so enforcement stays active for them.

Surviving compaction (the amnesia fix)

A long session eventually hits /compact, and the summarizer can drop the pending work and roadmap you discussed earlier — the agent then re-researches things the brain already knows. Two hooks close that gap by pushing the brain's open-work digest back into context at the exact moments memory is born or lost:

Hook Event What it does
inject_brain_context.py SessionStart (startup/resume/compact) Injects the ranked open-work digest (pending + roadmap-tagged decisions) via additionalContext. source=compact is the post-compaction re-entry — it injects a fuller digest so the roadmap survives the summary.
remind_brain_before_research.py PreToolUse matcher Workflow Soft, non-blocking nudge to check get_roadmap/query_decisions before a fan-out research workflow. Doesn't block — the push digest already put the answer in context.

This is the same digest get_roadmap returns, so push (hook) and pull (tool) never diverge. The digest is token-budgeted (top ~15 items, action truncated) — ~1k tokens once per compaction.

Tagging durable work: put roadmap or blocker in a decision's area (e.g. area="kmp-foundation/roadmap") and it floats to the top of the digest above transient pending edits. Untagged pending decisions still appear, newest first — so the fix works on an existing brain with no migration.

Optional hard research gate: the reminder is soft by default. To block Workflow until a brain read happened this session, set "research_gate": "hard" in ~/.agent-brain/config.json. A pre_check/query_decisions/get_roadmap call satisfies the gate (it writes ~/.agent-brain/.last_query_marker); BRAIN_SKIP_ENFORCE=1 bypasses it. Soft is recommended — a hard gate can false-positive when the brain genuinely has nothing on the topic.

Install (setup.sh wires every brain hook idempotently, backing up settings.json.bak):

// ~/.claude/settings.json
{
  "hooks": {
    "SessionStart": [
      { "matcher": "startup|resume|compact",
        "hooks": [{ "type": "command",
          "command": "~/.agent-brain/.venv/bin/python /path/to/agent-brain/brain/hooks/inject_brain_context.py",
          "timeout": 15000 }] }
    ],
    "PreToolUse": [
      { "matcher": "Workflow",
        "hooks": [{ "type": "command",
          "command": "~/.agent-brain/.venv/bin/python /path/to/agent-brain/brain/hooks/remind_brain_before_research.py",
          "timeout": 10000 }] }
    ]
  }
}

Making SAN the default read path

SAN is far cheaper for reading code (see Is SAN worth it?) — but agents kept defaulting to raw reads because nothing routed them to SAN, and a tool ladder isn't obvious. These changes close the gap (none touch the SAN format or quality):

The tool ladder (the standing rule agents are taught + nudged toward):

Step Goal Use Not
Find which file/symbol has X query_san / semantic_search_nodes grep -r raw source
Read/understand what a file/class does get_san(file_path="<abs>") (sig/full) cat/head/sed the file
Exact literal a precise string/line grep/rg on the file get_san (SAN drops literals)
Edit change exact bytes raw Read then Edit

How it's enforced:

Mechanism What it does
get_san accepts absolute paths Pass the exact path you got from a discovery hit — repo auto-detected, no repo-name lookup. Removes the friction tax.
route_read_to_san.py (PreToolUse Read) Raw Read of a fresh-.san code file → nudge toward get_san. Per-file dedupe (every file's first read is nudged, not just the session's first). Escalates: 1st read soft-allow; a 2nd+ raw read of the same file gets a stronger nudge, or a block under hard mode.
route_bash_to_san.py (PreToolUse Bash) Closes the shell bypass: cat/head/sed/tail dumping a fresh-.san source file → same nudge/escalation. Leaves grep/rg/awk (literal search) and discovery-grep-across-files alone — those are the right tools, not bypasses.
pre_check(repo=...) surfaces coverage Appends SAN AVAILABLE: repo 'X' has N files compiled… so the read path is in view at check-before-work