---
title: Failure Learning
description: Offline failure analysis for coding agents. Analyzes past sessions, finds what went wrong, correlates with what fixed it, and writes project-level learnings.
---

`headroom learn` analyzes past coding agent sessions, finds what went wrong, correlates each failure with what eventually worked, and writes specific project-level learnings that prevent the same mistakes next session.

## Quick Start

```bash
# See recommendations for current project (dry-run, no changes)
headroom learn

# Write recommendations to CLAUDE.local.md and MEMORY.md
headroom learn --apply

# Analyze a specific project
headroom learn --project ~/my-project --apply

# Analyze all projects
headroom learn --all --apply

# Write to the team-shared CLAUDE.md instead of CLAUDE.local.md
headroom learn --apply --target CLAUDE.md
```

## Success Correlation

The core innovation. Instead of cataloging failures ("Read failed 5 times"), Headroom finds what the model did to **fix** each failure:

- **Failed**: `Read axion-formats/src/main/java/.../FirstClassEntity.java`
- **Then succeeded**: `Read axion-scala-common/src/main/scala/.../FirstClassEntity.scala`
- **Learning**: "`FirstClassEntity` is at `axion-scala-common/`, not `axion-formats/`"

This produces specific, actionable corrections -- not generic advice.

## What It Learns

### Environment Facts

Which runtime commands work vs fail.

```markdown
### Environment
- **Python**: use `uv run python` (not `python3` -- modules not available outside venv)
```

### File Path Corrections

Wrong paths the model keeps guessing, with the correct locations.

```markdown
### File Path Corrections
- `axion-common/src/.../AxionSparkConstants.scala`
  -> actually at `axion-spark-common/src/.../AxionSparkConstants.scala`
```

### Search Scope

Which directories to search in (narrow paths fail, broader ones work).

```markdown
### Search Scope
- Don't search `axion-model/` -> use `axion/` (the repo root)
```

### Command Patterns

How commands should (and should not) be run.

```markdown
### Command Patterns
- **user_prefers_manual**: User rejected gradle 18 times -- show the command, don't execute
- **python_runtime**: Use `uv run python` not `python3` (ModuleNotFoundError)
```

### Known Large Files

Files that need `offset`/`limit` with Read.

```markdown
### Known Large Files
- `proxy/server.py` (~8000 lines) -- always use offset/limit
```

## Where Learnings Go

| Pattern | Destination | Why |
|---------|-------------|-----|
| Environment, paths, search scope, commands, large files | **CLAUDE.local.md** | Personal facts (machine-specific paths), gitignored by default |
| Missing paths, retry patterns, permissions | **MEMORY.md** | May change, agent-specific |

`CLAUDE.local.md` lives in your project directory. It is the **personal** local
memory file in Claude Code's [memory convention](https://docs.claude.com/en/docs/claude-code/memory)
— meant to be gitignored — so machine-specific learnings (absolute paths,
tool-discovery byproducts) don't pollute the team-shared `CLAUDE.md`. Make sure
`CLAUDE.local.md` is listed in your `.gitignore`. Pass `--target CLAUDE.md` to
opt into the shared file instead, or `--target <path>` for any custom location.
MEMORY.md lives in `~/.claude/projects/*/memory/`.

If an older Headroom version already wrote a learned-patterns block into your
team-shared `CLAUDE.md`, the next `headroom learn --apply` moves it into
`CLAUDE.local.md` and prints a warning so you can review the diff before
committing. If `CLAUDE.md` contained nothing but the Headroom block, it is
removed entirely.

## Marker-Based Updates

Headroom manages a clearly-delimited section in each file:

```markdown
<!-- headroom:learn:start -->
## Headroom Learned Patterns
*Auto-generated by `headroom learn` -- do not edit manually*
...
<!-- headroom:learn:end -->
```

On re-run, only the content between markers is replaced. Your existing file content is preserved.

## Architecture

The system is built with an adapter pattern so it can support multiple agent systems:

- **Scanners** read tool-specific log formats (e.g., `~/.claude/projects/*.jsonl`) and produce normalized `ToolCall` sequences
- **Analyzers** work on `ToolCall` data -- same analysis logic for any agent system
- **Writers** output to tool-specific context injection mechanisms (e.g., CLAUDE.md)

To add support for a new agent (e.g., Cursor), you write a Scanner that reads its log format and a Writer that outputs to `.cursorrules`. The analyzers stay the same.

## CLI Reference

```bash
headroom learn [OPTIONS]

Options:
  --project PATH    Project directory to analyze (default: current directory)
  --all             Analyze all discovered projects
  --apply           Write recommendations (default: dry-run)
  --target TEXT     Context file to write to, Claude Code only (default:
                    CLAUDE.local.md). Relative to project root, or absolute.
  --agent TEXT      Agent to analyze (e.g. claude, codex, gemini, opencode)
  --model TEXT      LLM model to use for analysis
  --workers INT     Number of parallel workers
```

## Real-World Results

Tested on 67,583 tool calls across 23 projects:

| Metric | Value |
|--------|-------|
| Failure rate | 7.5% (5,066 failures) |
| Corrections extracted | 164 per project (avg) |
| Path corrections | 22 (axion project) |
| Search scope corrections | 24 (axion project) |
| Command patterns learned | 5 (axion project) |
