How Headroom Learns from Failed Sessions and Writes Corrections to AGENTS.md
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 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, 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 and headroom/learn/writer.py.
Step 1: Digesting Session Failures
First, 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 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 inAGENTS.mdmemory_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, containing target, section, content, and estimated_tokens_saved fields.
Step 3: Persisting Structured Corrections
Finally, the writer module (headroom/learn/writer.py) persists these recommendations. The CodexWriter class handles the specific logic for writing to 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, the system looks for these HTML-style comments:
_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:
- Locate existing block: Searches for the marker pattern in the current
AGENTS.md - Parse prior recommendations: Uses
_parse_prior_recommendationsto extract existing sections - Merge strategies: New sections overwrite existing ones with the same name, while unchanged sections persist
- Build new block: The
_build_sectionmethod constructs the replacement:
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, lines 2729-2735) ensures corrections propagate to both locations:
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:
# 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 contains the updated marker block.
Inspecting Generated Corrections
Verify what Headroom learned by inspecting the marker block:
cat AGENTS.md | grep -A5 "<!-- headroom:learn:start -->"
You will see output similar to:
<!-- 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:
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.pycaptures tool-call failures and errors without storing full logs. - The analyzer in
headroom/learn/analyzer.pyprompts an LLM to generate JSON recommendations tagged asCONTEXT_FILEorMEMORY_FILErules. - The writer in
headroom/learn/writer.pyuses marker blocks (<!-- headroom:learn:start/end -->) to merge new corrections with existingAGENTS.mdcontent non-destructively. - Both project-local
./AGENTS.mdand global~/.codex/AGENTS.mdcan be updated, especially when using the--learnflag withheadroom 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 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 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 supports multiple targets via the RecommendationTarget enum. While Codex writes to AGENTS.md, Claude agents target 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 before committing the changes, which is useful for CI pipelines or testing new learning configurations.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →