# How Headroom Learns from Failed Sessions and Writes Corrections to AGENTS.md

> Learn how Headroom analyzes failed sessions, generates LLM recommendations, and automatically writes corrections to AGENTS.md for improved agent performance.

- Repository: [Tejas Chopra/headroom](https://github.com/chopratejas/headroom)
- Tags: deep-dive
- Published: 2026-06-06

---

**Headroom learns from failed sessions by digesting tool-call logs into a compact summary, analyzing them with an LLM to generate structured recommendations, and writing those corrections to a marked block in [`AGENTS.md`](https://github.com/chopratejas/headroom/blob/main/AGENTS.md) using marker-delimited sections that merge with existing content.**

Headroom is an open-source CLI tool that wraps AI coding agents like Codex and Claude to optimize token usage and session efficiency. When sessions fail or generate wasted tokens, the `headroom learn` command transforms those failures into persistent institutional knowledge. This article explains exactly how Headroom learns from failed sessions and writes corrections to [`AGENTS.md`](https://github.com/chopratejas/headroom/blob/main/AGENTS.md), referencing the actual Python implementation in the `chopratejas/headroom` repository.

## The Three-Step Learning Pipeline

When you execute `headroom learn --agent codex`, the system executes three distinct phases defined in [`headroom/learn/analyzer.py`](https://github.com/chopratejas/headroom/blob/main/headroom/learn/analyzer.py) and [`headroom/learn/writer.py`](https://github.com/chopratejas/headroom/blob/main/headroom/learn/writer.py).

### Step 1: Digesting Session Failures

First, [`headroom/learn/registry.py`](https://github.com/chopratejas/headroom/blob/main/headroom/learn/registry.py) creates a **SessionDigest** that serializes all tool-call events from the previous run. The digest retains failed tool executions along with their error messages and truncated output, creating a compact representation of what went wrong without storing entire log files.

This digest captures the specific patterns that caused token waste, such as incorrect Python environment usage or attempts to edit files that should be ignored.

### Step 2: LLM Analysis and Recommendation Generation

The **learn analyzer** in [`headroom/learn/analyzer.py`](https://github.com/chopratejas/headroom/blob/main/headroom/learn/analyzer.py) feeds the digest to an LLM via the `_call_llm` method (or `_call_cli_llm` for CLI-only backends). The system embeds the digest into a system prompt (lines 15-34) that instructs the model to *"identify patterns that would prevent token waste"* and return a strict JSON payload containing two keys:

- `context_file_rules` – Stable project facts belonging in [`AGENTS.md`](https://github.com/chopratejas/headroom/blob/main/AGENTS.md)
- `memory_file_rules` – Session-specific preferences

The raw LLM response is processed by `_strip_fenced_json` to remove markdown fences, then parsed into Python dictionaries. Each JSON entry becomes a `Recommendation` object defined in [`headroom/learn/models.py`](https://github.com/chopratejas/headroom/blob/main/headroom/learn/models.py), containing `target`, `section`, `content`, and `estimated_tokens_saved` fields.

### Step 3: Persisting Structured Corrections

Finally, the **writer** module ([`headroom/learn/writer.py`](https://github.com/chopratejas/headroom/blob/main/headroom/learn/writer.py)) persists these recommendations. The `CodexWriter` class handles the specific logic for writing to [`AGENTS.md`](https://github.com/chopratejas/headroom/blob/main/AGENTS.md), while the generic writer utilities manage marker-block merging.

## How the Writer Merges Corrections into AGENTS.md

The writer uses marker-delimited blocks to ensure non-destructive updates. As defined in [`headroom/learn/writer.py`](https://github.com/chopratejas/headroom/blob/main/headroom/learn/writer.py), the system looks for these HTML-style comments:

```python
_MARKER_START = "<!-- headroom:learn:start -->"
_MARKER_END   = "<!-- headroom:learn:end -->"
_MARKER_PATTERN = re.compile(
    re.escape(_MARKER_START) + r".*?" + re.escape(_MARKER_END),
    re.DOTALL,
)

```

### The Merging Algorithm

When `CodexWriter.write` executes (lines 34-66), it calls `_merge_into_file` which performs the following:

1. **Locate existing block**: Searches for the marker pattern in the current [`AGENTS.md`](https://github.com/chopratejas/headroom/blob/main/AGENTS.md)
2. **Parse prior recommendations**: Uses `_parse_prior_recommendations` to extract existing sections
3. **Merge strategies**: New sections overwrite existing ones with the same name, while unchanged sections persist
4. **Build new block**: The `_build_section` method constructs the replacement:

```python
def _build_section(recommendations: list[Recommendation]) -> str:
    now = datetime.now(timezone.utc).strftime("%Y-%m-%d")
    lines = [
        _MARKER_START,
        "## Headroom Learned Patterns",

        f"*Auto-generated by `headroom learn` on {now} — do not edit manually*",
        "",
    ]
    for rec in recommendations:
        lines.append(f"### {rec.section}")

        if rec.estimated_tokens_saved > 0:
            lines.append(f"*~{rec.estimated_tokens_saved:,} tokens/session saved*")
        lines.append(rec.content)
        lines.append("")
    lines.append(_MARKER_END)
    return "\n".join(lines)

```

The writer then determines the destination path—either `project.context_file` or `project.project_path / "AGENTS.md"`—and writes the merged content (unless in `dry_run` mode).

### Global and Local Persistence

When using `headroom wrap ... --learn`, the CLI module ([`headroom/cli/wrap.py`](https://github.com/chopratejas/headroom/blob/main/headroom/cli/wrap.py), lines 2729-2735) ensures corrections propagate to both locations:

```python
if learn:
    # … after the wrapping session ends

    agents_md = Path.cwd() / "AGENTS.md"
    global_agents = Path.home() / ".codex" / "AGENTS.md"
    # CodexWriter will be invoked to merge the new block into both files

```

This guarantees that learned patterns are available project-locally and globally for the Codex agent.

## Practical Usage Examples

### Running the Learner from CLI

Execute the learning pipeline after a failed or completed session:

```bash

# Learn from the most recent proxy session (Codex agent)

headroom learn --agent codex

```

The LLM analyzes the session digest and generates recommendations like *"use `uv run python` instead of `python3`"* or *"add large files under `src/` to `.gitignore`"*. Upon completion, [`AGENTS.md`](https://github.com/chopratejas/headroom/blob/main/AGENTS.md) contains the updated marker block.

### Inspecting Generated Corrections

Verify what Headroom learned by inspecting the marker block:

```bash
cat AGENTS.md | grep -A5 "<!-- headroom:learn:start -->"

```

You will see output similar to:

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

## Headroom Learned Patterns

*Auto-generated by `headroom learn` on 2026-06-06 — do not edit manually*

### Environment

*~12,000 tokens/session saved*
Use `uv run python` instead of plain `python3` for reproducible builds.

### File structure

*~8,500 tokens/session saved*
Large files are located under `src/` – add them to `.gitignore` to avoid accidental edits.

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

```

### Programmatic Access to the Writer

For custom integrations, instantiate the writer directly:

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

project = ProjectInfo(
    project_path=Path.cwd(),
    context_file=None,
    memory_file=None,
    data_path=Path.cwd() / "data"
)

recs = [
    Recommendation(
        target=RecommendationTarget.CONTEXT_FILE,
        section="Environment",
        content="Use `uv run python` instead of plain `python3`.",
        estimated_tokens_saved=12000,
    )
]

writer = CodexWriter()
result = writer.write(recs, project, dry_run=False)
print("Wrote:", result.files_written)

```

## Summary

- **Headroom** transforms failure data into actionable knowledge through a three-step pipeline: digest collection, LLM analysis, and file persistence.
- The **SessionDigest** in [`headroom/learn/registry.py`](https://github.com/chopratejas/headroom/blob/main/headroom/learn/registry.py) captures tool-call failures and errors without storing full logs.
- The **analyzer** in [`headroom/learn/analyzer.py`](https://github.com/chopratejas/headroom/blob/main/headroom/learn/analyzer.py) prompts an LLM to generate JSON recommendations tagged as `CONTEXT_FILE` or `MEMORY_FILE` rules.
- The **writer** in [`headroom/learn/writer.py`](https://github.com/chopratejas/headroom/blob/main/headroom/learn/writer.py) uses marker blocks (`<!-- headroom:learn:start/end -->`) to merge new corrections with existing [`AGENTS.md`](https://github.com/chopratejas/headroom/blob/main/AGENTS.md) content non-destructively.
- Both project-local [`./AGENTS.md`](https://github.com/chopratejas/headroom/blob/main/./AGENTS.md) and global `~/.codex/AGENTS.md` can be updated, especially when using the `--learn` flag with `headroom wrap`.

## Frequently Asked Questions

### What triggers the Headroom learning process?

The learning process triggers when you explicitly run `headroom learn --agent <name>` or when you use `headroom wrap ... --learn`. In wrap mode, the proxy automatically invokes the writer after the session ends, ensuring that any failures during the wrapped task are analyzed and persisted to [`AGENTS.md`](https://github.com/chopratejas/headroom/blob/main/AGENTS.md) immediately.

### How does Headroom prevent overwriting manual edits to AGENTS.md?

Headroom uses marker-delimited blocks (`<!-- headroom:learn:start -->` and `<!-- headroom:learn:end -->`) to isolate generated content. The writer implemented in [`headroom/learn/writer.py`](https://github.com/chopratejas/headroom/blob/main/headroom/learn/writer.py) parses existing sections within these markers and merges them with new recommendations, preserving any content outside the markers and carrying forward unchanged learned rules. The comment explicitly warns *"do not edit manually"* to guide users.

### Can I use Headroom learning with Claude instead of Codex?

Yes. The recommendation system in [`headroom/learn/models.py`](https://github.com/chopratejas/headroom/blob/main/headroom/learn/models.py) supports multiple targets via the `RecommendationTarget` enum. While Codex writes to [`AGENTS.md`](https://github.com/chopratejas/headroom/blob/main/AGENTS.md), Claude agents target [`CLAUDE.md`](https://github.com/chopratejas/headroom/blob/main/CLAUDE.md) instead. The same marker-block merging logic applies regardless of the target agent, though file paths differ according to each agent's conventions.

### What happens in dry-run mode?

When `dry_run=True` is passed to `CodexWriter.write`, the writer performs all parsing and merging operations but returns the results in a `WriteResult` object without actually writing to disk. This allows you to preview exactly what corrections would be written to [`AGENTS.md`](https://github.com/chopratejas/headroom/blob/main/AGENTS.md) before committing the changes, which is useful for CI pipelines or testing new learning configurations.