# How the headroom learn Command Mines Failed Sessions and Writes Corrections to CLAUDE.md

> Discover how headroom learn scans agent conversations for failed tool calls and uses an LLM to write corrections into CLAUDE.md for improved performance.

- Repository: [Tejas Chopra/headroom](https://github.com/chopratejas/headroom)
- Tags: how-to-guide
- Published: 2026-06-05

---

**The `headroom learn` command scans your agent's conversation history for failed tool calls, uses an LLM to identify failure patterns, and writes structured corrections into a guarded marker block inside [`CLAUDE.md`](https://github.com/chopratejas/headroom/blob/main/CLAUDE.md).**

The `chopratejas/headroom` repository implements this as a multi-stage pipeline that turns raw session logs into actionable context for Claude Code. By mining errors from past interactions, the tool automatically generates recommendations that help the agent avoid repeating the same mistakes. Documentation in [`wiki/learn.md`](https://github.com/chopratejas/headroom/blob/main/wiki/learn.md) describes the full behavior and file format.

## How the CLI Orchestrates the Learning Pipeline

The entry point lives in [`headroom/cli/learn.py`](https://github.com/chopratejas/headroom/blob/main/headroom/cli/learn.py). When you invoke `headroom learn`, the **CLI** first detects the target LLM via `_detect_default_model` and loads the correct learning plugin from the registry using `auto_detect_plugins` and `get_plugin`. Each plugin maps to a specific agent such as Claude, Codex, or Gemini, and plugin registration is handled in files like [`headroom/learn/plugins/claude.py`](https://github.com/chopratejas/headroom/blob/main/headroom/learn/plugins/claude.py).

For every discovered project, the CLI delegates to the **scanner** to collect raw sessions, then passes those sessions to the **analyzer**. The analyzer returns a `ResultData` object that contains the total failure count, a failure rate, and a list of `Recommendation` instances. Finally, the CLI invokes the plugin's writer via `writer.write`. If you omit the `--apply` flag, the command runs in dry-run mode and only prints what would be persisted.

## How the Scanner Discovers Failed Sessions

Session extraction is handled by [`headroom/learn/scanner.py`](https://github.com/chopratejas/headroom/blob/main/headroom/learn/scanner.py). Each plugin implements a `scan_project` method that walks the agent-specific data directory—for Claude Code this is typically `~/.claude/projects/*.jsonl`.

The scanner parses each log into a `Session` object that captures the sequence of tool calls, their inputs, outputs, and error status. It also normalizes file paths through helpers such as `_decode_project_path` and `_greedy_path_decode`, ensuring the downstream analyzer receives a canonical representation of every project.

## How the Analyzer Synthesizes Recommendations

Pattern recognition is performed by [`headroom/learn/analyzer.py`](https://github.com/chopratejas/headroom/blob/main/headroom/learn/analyzer.py). The `SessionAnalyzer` builds a prompt that describes the project, enumerates the collected sessions, and asks the model to identify recurring failure patterns such as wrong file paths, missing imports, or repeated retries.

The LLM responds with structured suggestions that the analyzer transforms into `Recommendation` objects. Each recommendation includes a section title, body content, an optional token-saving estimate, and a `RecommendationTarget` that points either to a **context** file or a **memory** file.

## How the Writer Updates CLAUDE.md

Correction persistence is implemented in [`headroom/learn/writer.py`](https://github.com/chopratejas/headroom/blob/main/headroom/learn/writer.py). The abstract `ContextWriter` defines the contract, while concrete implementations such as `ClaudeCodeWriter`, `CodexWriter`, and `GeminiWriter` handle agent-specific file layouts.

For Claude Code, the writer creates or updates two files: **[`CLAUDE.md`](https://github.com/chopratejas/headroom/blob/main/CLAUDE.md)** for context and **[`MEMORY.md`](https://github.com/chopratejas/headroom/blob/main/MEMORY.md)** for memory. Recommendations are inserted into a marker-delimited block that the writer guards from manual edits.

### The Marker-Block Format

Inside [`CLAUDE.md`](https://github.com/chopratejas/headroom/blob/main/CLAUDE.md), the writer injects content between these exact comment markers:

```markdown
<!-- headroom:learn:start -->

## Headroom Learned Patterns

*Auto-generated by `headroom learn` on 2024-11-07 — do not edit manually*

### <section-title>

*~123 tokens/session saved*
<recommendation-body>

<!-- headroom:learn:end -->

```

This format makes it easy for both humans and the tool to locate managed sections.

### Merging Without Data Loss

If [`CLAUDE.md`](https://github.com/chopratejas/headroom/blob/main/CLAUDE.md) already contains a `headroom:learn` block, the writer extracts existing recommendations via `_parse_prior_recommendations`. It then preserves any sections that were **not** regenerated and rewrites the block using `_merge_into_file`. This merge strategy guarantees that repeated runs never overwrite previously learned advice. When `dry_run=False`, the writer creates missing parent directories with `mkdir(parents=True)` and commits the merged content to disk.

## Practical Usage Examples

Run a dry-run to preview what `headroom learn` would write without modifying any files:

```bash
headroom learn --agent claude

```

Apply the corrections permanently to [`CLAUDE.md`](https://github.com/chopratejas/headroom/blob/main/CLAUDE.md):

```bash
headroom learn --agent claude --apply

```

You can also interact with the writer directly in Python:

```python
from pathlib import Path
from headroom.learn.writer import ClaudeCodeWriter
from headroom.learn.models import Recommendation, RecommendationTarget, ProjectInfo

writer = ClaudeCodeWriter()
proj = ProjectInfo(
    project_path=Path("/home/user/my_project"),
    context_file=None,
    memory_file=None,
    data_path=Path("/home/user/.claude")
)

recs = [
    Recommendation(
        target=RecommendationTarget.CONTEXT_FILE,
        section="File-Path Corrections",
        content="Update imports to use `src/` instead of `../`.",
        estimated_tokens_saved=140
    )
]

result = writer.write(recs, proj, dry_run=False)
print(result.files_written)

```

The `result.files_written` list contains the paths that were updated, such as `~/.claude/CLAUDE.md`.

## Summary

- The `headroom learn` command in `chopratejas/headroom` is a multi-stage pipeline that converts failed agent sessions into persistent corrections.
- [`headroom/cli/learn.py`](https://github.com/chopratejas/headroom/blob/main/headroom/cli/learn.py) orchestrates plugin detection, scanning, analysis, and writing.
- [`headroom/learn/scanner.py`](https://github.com/chopratejas/headroom/blob/main/headroom/learn/scanner.py) parses agent-specific logs like `~/.claude/projects/*.jsonl` into normalized `Session` objects.
- [`headroom/learn/analyzer.py`](https://github.com/chopratejas/headroom/blob/main/headroom/learn/analyzer.py) uses an LLM to produce structured `Recommendation` objects that target either context or memory files.
- [`headroom/learn/writer.py`](https://github.com/chopratejas/headroom/blob/main/headroom/learn/writer.py) injects recommendations into a guarded `<!-- headroom:learn -->` block inside [`CLAUDE.md`](https://github.com/chopratejas/headroom/blob/main/CLAUDE.md) and merges new advice without deleting old advice.

## Frequently Asked Questions

### What file does `headroom learn` update for Claude Code?

For Claude Code, the writer updates **[`CLAUDE.md`](https://github.com/chopratejas/headroom/blob/main/CLAUDE.md)** for context recommendations and **[`MEMORY.md`](https://github.com/chopratejas/headroom/blob/main/MEMORY.md)** for memory recommendations. These files live in the agent's data directory, typically under `~/.claude`.

### Will `headroom learn` overwrite my existing CLAUDE.md content?

No. The writer in [`headroom/learn/writer.py`](https://github.com/chopratejas/headroom/blob/main/headroom/learn/writer.py) calls `_parse_prior_recommendations` and `_merge_into_file` to merge new suggestions with existing ones. Any previously learned section that is not regenerated in the current run is preserved inside the marker block.

### Which LLM powers the recommendation engine?

The analyzer in [`headroom/learn/analyzer.py`](https://github.com/chopratejas/headroom/blob/main/headroom/learn/analyzer.py) sends a structured prompt to the LLM configured for your agent. The CLI detects the default model via `_detect_default_model` and loads the corresponding plugin, so the exact model depends on your `headroom` configuration.

### How can I preview changes before writing to disk?

Omit the `--apply` flag. The default behavior is a dry-run that prints the planned updates without calling the writer with `dry_run=False`. You can also programmatically pass `dry_run=True` to the writer's `write` method to inspect the output.