evals/evals.json
{
"skill_name": "recursive-decomposition",
"corpus": "evals/files/mortgage-doc-rag",
"evals": [
{
"id": 1,
"name": "pdf-corpus",
"kind": "decompose",
"optional": true,
"prompt": "The PDF corpus is the git submodule at skills/recursive-decomposition/evals/files/mortgage-doc-rag. Size it first. Do not load any PDF binary into the main context. How many PDFs are there, and what is the total byte size? Name the 10 largest by byte size. Convert at most three of those with anydoc (`npx -y @firecrawl/anydoc FILE -o .firecrawl/FILE.md`); if anydoc exits 3, use `firecrawl parse`. Extract each title from the markdown with grep or a bounded read. Recursion depth 1.",
"expected_output": "pdf_count at least 131, largest list includes urar_form_1004_epa_scan.pdf, depth 1, no PDF dumped into context.",
"files": ["evals/files/mortgage-doc-rag"],
"gold": "evals/gold/pdf-corpus.json",
"assertions": [
"result.json is valid JSON",
"decomposed is true",
"depth is 1",
"subagents_spawned_subagents is false",
"pdf_count is at least 131",
"largest list includes urar_form_1004_epa_scan.pdf"
]
}
]
}
evals/gold/pdf-corpus.json
{
"submodule": "skills/recursive-decomposition/evals/files/mortgage-doc-rag",
"source": "https://github.com/tccao/mortgage-doc-rag",
"license": "MIT",
"min_pdfs": 131,
"min_bytes": 60000000,
"largest_suffix": "urar_form_1004_epa_scan.pdf"
}
evals/README.md
# Evals
Large PDFs live in a git submodule. They are not copied into this repository.
Corpus: [tccao/mortgage-doc-rag](https://github.com/tccao/mortgage-doc-rag) (MIT). 131 public-domain mortgage PDFs, about 63 MB.
```bash
git submodule update --init --depth 1 skills/recursive-decomposition/evals/files/mortgage-doc-rag
```
Layout follows [Evaluating skills](https://agentskills.io/skill-creation/evaluating-skills). Runs go under `evals-workspace/` at the repository root (gitignored).
## Check (no agent)
```bash
bash .github/scripts/eval-skill.sh check
```
Validates trigger queries and, if the submodule is present, PDF count, byte floor, and the largest filename. CI does not clone the submodule. Missing PDFs print `SKIP`, not `ERROR`.
## Agent runs (with vs without the skill)
Same prompt twice, clean context:
```text
evals-workspace/iteration-1/eval-pdf-corpus/with_skill/outputs/result.json
evals-workspace/iteration-1/eval-pdf-corpus/without_skill/outputs/result.json
```
```json
{
"decomposed": true,
"depth": 1,
"subagents_spawned_subagents": false,
"pdf_count": 131,
"largest": [{"path": "data/degraded/appraisal/urar_form_1004_epa_scan.pdf", "bytes": 1639534}]
}
```
```bash
bash .github/scripts/eval-skill.sh score evals-workspace/iteration-1
```
Trigger queries live in `trigger-queries.json`. Score those by whether the agent loaded this skill.
## Firecrawl document skills
| Skill | What it does |
|-------|----------------|
| [firecrawl/anydoc@convert-documents-to-markdown](https://skills.sh/firecrawl/anydoc/convert-documents-to-markdown) | Local `npx -y @firecrawl/anydoc FILE -o out.md`. Exit 3 means OCR; then `--ocr hosted`. |
| [firecrawl/cli@firecrawl-parse](https://skills.sh/firecrawl/cli/firecrawl-parse) | Cloud `firecrawl parse`. 50 MB cap, about 1 credit per page. |
Prefer anydoc. Many files in this corpus are scans; `pdftotext` on the whole tree extracts nothing useful. Size first, then parse only what the question needs.
Live pre/post (same prompt, 2026-09-11): size-first 0.02 s. anydoc on two digital PDFs 1.64 s, titles recovered. The largest file is a 4-page scan: anydoc exit 3, `firecrawl parse` 15.45 s, title Uniform Residential Appraisal Report. Naive `pdftotext` on all 131 files: 1.65 s and 0 bytes; on that scan, 4 bytes and no title.
evals/trigger-queries.json
{
"skill_name": "recursive-decomposition",
"queries": [
{
"id": "t01",
"should_trigger": true,
"query": "Analyze error handling patterns across this entire codebase"
},
{
"id": "t02",
"should_trigger": true,
"query": "Find all TODO comments in the project and categorize by priority"
},
{
"id": "t03",
"should_trigger": true,
"query": "What features are planned across all our PRD documents?"
},
{
"id": "t04",
"should_trigger": true,
"query": "Summarize the key decisions from all meeting notes in docs/"
},
{
"id": "t05",
"should_trigger": true,
"query": "Find security issues across all TypeScript files"
},
{
"id": "t06",
"should_trigger": true,
"query": "hey can you aggregate every API error type in src/"
},
{
"id": "t07",
"should_trigger": true,
"query": "Which Q2 features depend on Q1 auth? Check every PRD."
},
{
"id": "t08",
"should_trigger": true,
"query": "codebase-wide catch/throw report please"
},
{
"id": "t09",
"should_trigger": true,
"query": "Process every markdown file under docs/product and list shipped vs planned"
},
{
"id": "t10",
"should_trigger": true,
"query": "I need a map of HACK comments in all files"
},
{
"id": "t21",
"should_trigger": true,
"query": "Summarize every PDF in this folder without swallowing them whole"
},
{
"id": "t22",
"should_trigger": true,
"query": "Extract the conclusions from all the papers under papers-we-love"
},
{
"id": "t11",
"should_trigger": false,
"query": "What does wrapError do in src/utils/wrap.ts?"
},
{
"id": "t12",
"should_trigger": false,
"query": "Find the definition of ApiError"
},
{
"id": "t13",
"should_trigger": false,
"query": "Read src/api/auth.ts and explain login"
},
{
"id": "t14",
"should_trigger": false,
"query": "What is the value of MAGIC_NEEDLE?"
},
{
"id": "t15",
"should_trigger": false,
"query": "Write a fibonacci function"
},
{
"id": "t16",
"should_trigger": false,
"query": "Commit the current diff"
},
{
"id": "t17",
"should_trigger": false,
"query": "How do I install this skill?"
},
{
"id": "t18",
"should_trigger": false,
"query": "Fix the typo on line 3 of README.md"
},
{
"id": "t19",
"should_trigger": false,
"query": "Explain what a TODO comment is"
},
{
"id": "t20",
"should_trigger": false,
"query": "Rename wrapError to wrapErr in src/utils/wrap.ts"
},
{
"id": "t23",
"should_trigger": false,
"query": "Convert this one-page receipt.pdf to markdown"
},
{
"id": "t24",
"should_trigger": false,
"query": "What is the MIME type of a PDF file?"
}
]
}
references/codebase-analysis.md
# Example: error handling across a codebase
Tool calls below use Claude Code names (`Glob`, `Grep`, `Task`); map them to your agent's file search, content search and sub-agent tools.
Walkthrough of the protocol on a made-up tree. File counts are the scenario, not a paper result.
## Task
"Analyze all error handling patterns in this codebase and report where they agree, where they diverge, and what to change."
## 1. Filter
```text
Glob("**/*.ts") → 450 files
Glob("**/*.tsx") → 120 files
total: 570 files
Grep("catch|throw|Error|exception", type="ts") → 89 files
Grep("try.*catch|\.catch\\(", type="ts") → 67 files
union: 102 files with error handling
```
Do not read the 570. Grep first.
## 2. Partition
```text
src/api/* 23 files batch A
src/services/* 31 files batch B
src/components/* 28 files batch C
src/utils/* 12 files batch D
other 8 files batch E
```
Five to ten files per batch is the skill default. These batches are larger; keep them only if each still fits a sub-agent window, or split again.
## 3. One sub-agent per batch
```text
Task(subagent_type="Explore", prompt="""
Analyze error handling in src/api/*.
For each file with error handling:
1. Identify error handling patterns used
2. Note any error types defined or caught
3. Check for consistent error propagation
4. Flag any unhandled promise rejections
Return structured findings.
""")
```
Launch A through E in parallel. Each brief must include the files, the question, and the output schema. Do not reuse the same query on overlapping files.
## 4. Merge
```text
A: HTTP errors, custom ApiError
B: service-level try/catch, logging
C: UI error boundaries, toasts
D: wrappers, validation errors
E: mixed, a few strays
```
## 5. Write the report from the merge
```text
API: ApiError, HttpError, ValidationError
service: ServiceError, DatabaseError
UI: error boundaries, user-facing messages
utils: generic wrappers
HTTP errors always carry a status code
DatabaseError drops the original error
add error codes the client can switch on
```
## 6. Spot-check
Re-read a handful of cited lines, not the 102 files:
```text
Is ApiError used throughout src/api/?
Does DatabaseError keep the stack?
Do error boundaries cover every route component?
```
If two batches disagree, re-read the conflict.
## Report shape
```markdown
# Error handling
102 files, four error families, three consistency bugs.
## Types
### API (src/api/)
- ApiError: HTTP errors
- ValidationError: bad requests
- AuthenticationError: auth failures
### Service (src/services/)
...
## What already agrees
1. API routes wrap handlers in try/catch
2. Errors include a request ID
## What does not
1. Some services swallow errors with no log
2. Database errors lose the original stack
## Changes
1. Error-code enum
2. Error boundary on the remaining routes
```
| | |
|-|-|
| Files after grep | 102 |
| Sub-agents | 5 |
| Tokens | spread across the five, not one 150k window |
references/cost-analysis.md
# Cost and when to decompose
When to read directly, when to split the work, and what the paper actually measured. Paper figures: Zhang, Kraska, Khattab, Recursive Language Models, arXiv:2512.24601 (v3).
30k and 50k below are harness caps, not a claim that a bigger window removes rot. Density first. In the paper, OOLONG-Pairs is 32k tokens (arXiv:2512.24601, Table 1).
## Decision
| Situation | Approach |
|-----------|----------|
| One file, one function, or a single needle | Read directly |
| Linear aggregate or list-everything, and completeness matters | Decompose |
| Pairwise, quadratic, or multi-hop across scattered sources | Decompose, even under 30k tokens |
| 10+ files or 50k+ tokens | Decompose |
| Under 30k tokens and a localised answer | Read directly |
Skip decomposition when the answer is one needle, when latency matters more than completeness, or when coordination would cost more than a direct read.
Recursion depth is 1. Sub-agents must not launch sub-agents. Write the batch count, run one parallel wave, merge, then another wave if batches remain.
## What you pay for
Direct: one call, input tokens times the price. Quality falls as the window fills (context rot).
Decomposed: sum of sub-call tokens plus the parent coordinating them. Latency is the slowest batch plus the merge, if batches run in parallel. Quality holds if each batch stays small and the merge is checked.
## Paper costs (Table 1, Figure 11)
BrowseComp-Plus, 6 to 11M tokens, GPT-5:
- RLM(depth=1): 91.3% at $0.99 average ($1.22 std)
- compaction: 70.5% at $0.57 average
- GPT-5 base: hits the context limit (0.0*)
- linear extrapolation of GPT-5-mini ingesting that input: $1.50 to $2.75
RLM is not cheaper than compaction on that row. It is cheaper than stuffing 6 to 11M tokens into the model, and the paper reports it outperforms compaction and retrieval by over 29%.
OOLONG (131k tokens): RLM(GPT-5, depth=1) 56.0 versus GPT-5 44.0 (+28.4%). RLM(Qwen3-Coder, depth=1) 48.0 versus 36.0 (+33.3%).
OOLONG-Pairs (32k tokens): GPT-5 F1 0.1 versus RLM 58.0 at depth=1 (76.0 at depth=3).
Median RLM cost is comparable or lower than the base model (Figure 11, 50th percentile). The average can sit higher because of long outlier trajectories. The 95th percentile of runtime is dominated by sequential sub-LLM calls. The paper does not publish a 99th-percentile multiplier.
## Parallel batches
If batches are independent:
```text
serial: T = t1 + t2 + ... + tn
parallel: T = max(t1, t2, ..., tn) + merge
```
Toy timing, not a paper result: ten batches at 30 seconds each, 10-second merge. Serial 300 seconds, parallel about 40.
## Variance
RLM trajectories are long-tailed (paper, Observation 4 and Figure 11). Common causes: too many sub-calls, the same span processed twice, deep recursion, clumsy chunks.
Caps that help: a sub-call budget, disjoint partitions, a depth limit, a running token count.
## Cut cost before you recurse
Filter first. A 1000-file tree that greps down to 20 files is a cheaper problem. The 10x in that sketch is arithmetic on the example, not a measured speedup.
For aggregates, sample a slice. If the distribution is obvious, stop. If it is not, process the rest.
For search, stop when the answer is found, then verify. Do not finish the remaining batches out of habit.
If the same tree will be queried again, cache per-chunk notes and drop them when the source changes.
## Tool choice
| Job | Approach |
|-----|----------|
| One file by name | glob or find, then read |
| One function definition | grep, then read that range |
| One module | read it and follow imports |
| About 5 related files | read them |
| A pattern across the tree | grep, then decompose the hits |
| Aggregate across 50+ files | disjoint batches, parallel sub-agents |
| Multi-hop across scattered sources | this skill |
Read directly on the first four rows. The last three are why the skill exists.
references/document-aggregation.md
# Example: features across many PRDs
Tool calls below use Claude Code names (`Glob`, `Grep`, `Task`); map them to your agent's file search, content search and sub-agent tools.
Walkthrough of the protocol on a made-up docs tree. File counts and feature counts are the scenario, not a paper result.
## Task
"What features are planned across all our PRD documents? Build one roadmap."
## 1. Find the documents
```text
Glob("**/PRD*.md") → 12 files
Glob("**/prd-*.md") → 5 files
Glob("docs/product/*.md") → 8 files
dedupe: 18 unique files
```
Size them before reading. Here that is about 85k tokens, so decompose.
## 2. Group, then drop dead weight
A header scan, not a full read:
```text
Q1 4 docs ~20k tokens
Q2 5 docs ~25k tokens
Q3 4 docs ~18k tokens
technical 3 docs ~12k tokens
archived 2 docs exclude
```
Sixteen active documents, about 75k tokens.
## 3. Extraction schema
Every sub-agent fills the same shape:
```text
{
"document": "filename",
"product_area": "string",
"features": [
{
"name": "string",
"description": "string",
"priority": "P0|P1|P2",
"status": "planned|in-progress|shipped",
"target_quarter": "Q1|Q2|Q3|Q4"
}
],
"dependencies": ["feature_name"],
"stakeholders": ["team_name"]
}
```
Without a schema the merge is a pile of prose.
## 4. One agent per group
```text
Agent 1 (Q1 PRDs):
Task(subagent_type="Explore", prompt="""
Read each PRD in docs/product/q1/:
- PRD-auth-improvements.md
- PRD-dashboard-v2.md
- PRD-mobile-notifications.md
- PRD-api-versioning.md
Extract features using this schema: [schema]
Return structured JSON for each document.
""")
Agent 2: Q2 documents, same schema
Agent 3: Q3 documents, same schema
Agent 4: technical PRDs, same schema
```
Name the files in the brief. "The Q1 folder" is not a brief.
## 5. Dedupe
```text
Q1 12 features
Q2 15
Q3 11
technical 8
raw total 46
"Dark mode" in three PRDs → one row
"API v2" in two PRDs → one row
after dedupe: 38
```
## 6. Dependencies
```text
Dashboard v2 → API v2
Mobile notifications → Auth improvements
Reporting → Dashboard v2, Data pipeline
```
A directed graph is enough. Do not invent a critical path the files do not state.
## 7. Roadmap from the merge
```text
# Feature roadmap (16 PRDs)
## Q1
P0 Auth improvements (PRD-auth-improvements.md)
OAuth2, SSO. In progress.
P0 API versioning (PRD-api-versioning.md)
v2, deprecation timeline. Planned.
P1 Dashboard v2 (PRD-dashboard-v2.md)
depends on API v2.
## Q2
...
```
## 8. Spot-check three rows
```text
PRD-auth-improvements.md
OAuth2 listed as P0
Q1 target
PRD-dashboard-v2.md
depends on API v2
four sub-features extracted
API v2 → Dashboard v2 still holds in both files
```
Three is the skill default. If a row fails, re-read that document and rebuild that slice of the merge.
## Output shape
```markdown
# Feature roadmap
16 PRDs, 38 features, 12 cross-document dependencies.
| Feature | Priority | Quarter | Status | Dependencies |
|---------|----------|---------|--------|--------------|
| Auth improvements | P0 | Q1 | In progress | |
| API v2 | P0 | Q1 | Planned | |
| Dashboard v2 | P1 | Q1 | Planned | API v2 |
| Mobile notifications | P1 | Q2 | Planned | Auth |
Unresolved dependencies: 3
Conflicting timelines: 2
Missing stakeholder: 1
```
| | |
|-|-|
| Documents | 16 |
| Features after dedupe | 38 |
| Sub-agents | 4, in parallel |
| Tokens | ~75k, spread across the four |
| Verification reads | 3 |
references/rlm-strategies.md
# RLM decomposition strategies
Strategies taken from Recursive Language Models (Zhang, Kraska, Khattab, arXiv:2512.24601). Numbers below are from that paper, v3.
## Context rot
Quality falls as the prompt gets longer. The paper (citing Hong et al., 2025) calls this context rot:
- retrieval misses
- details dropped in long documents
- invented links between distant passages
- weaker reasoning over a large evidence set
The fix in the paper is to keep the active window small and reach the rest of the prompt through tools: peek, slice, recurse.
## What models actually do
Section 5 of the paper describes how current models behave as RLMs: probe the input, then split the work into sub-calls. The patterns below are the ones this skill turns into a protocol.
### Filter with code before you read
Narrow the set with a pattern, then open what remains.
```text
pattern = (database|connection|auth)
keep files whose content matches pattern
read only those files
```
In an agent session: grep before read, glob to cut the file set, then chain filters (type, then keyword, then meaning).
### Chunk
Uniform: split a 1000-line file into ten 100-line windows, merge with overlap.
By meaning: one function, class, or section per chunk. Prefer a complete unit over equal sizes.
By keyword: all error-handling in one batch, all API definitions in another, each with a prompt that matches that batch.
### Recurse
This skill uses one level only. Sub-agents answer; they do not spawn sub-agents.
```text
main agent
├── sub-agent (chunk A)
├── sub-agent (chunk B)
└── sub-agent (chunk C)
then merge
```
The paper's OOLONG-Pairs numbers move with recursion depth (GPT-5: 58.0% F1 at depth=1, 76.0% at depth=3). Extra depth is not free: Qwen3-Coder-480B-A35B often makes syntax errors, and those errors spread into sub-calls, so depth 2 and 3 can score worse than depth 1 on that model (arXiv:2512.24601, Table 1 and Figure 4b). This skill stays at depth 1.
### Verify by reading less
1. Write an answer from the large pass.
2. Pull the cited locations.
3. Re-read only those locations.
4. Check the answer against that small window.
5. If it disagrees, re-read the disagreement, not the whole corpus.
### Build long output in pieces
Generate each section on its own, store the pieces, then stitch. That is how an RLM writes past the model's output limit (paper, section 5).
## Size limits
These match the skill body. They are session limits, not paper results.
- Files over 2,000 lines or 50 KB: read by line range, never in one shot.
- PDFs over 100 pages or 30 MB: metadata or a split, not a full ingest.
- Stay under about 30k tokens in the active window when you can.
## Task shape
| Shape | Typical job | Approach |
|-------|-------------|----------|
| O(1) | one needle | filter until the set is small, then read |
| O(n) | count, list, summarise every item | map-reduce over disjoint batches |
| O(n²) | pairwise relations | blocked pairs, sample first if the full grid is too large |
| O(log n) | search in ordered or nested data | divide and conquer |
Figure 1 in the paper scales S-NIAH, OOLONG, and OOLONG-Pairs from 2^13 to 2^20 tokens. GPT-5 drops faster on the linear and quadratic tasks. Past 2^14 tokens the RLM beats GPT-5 on those plots.
## Model differences (from the paper)
GPT-5 as an RLM: fewer syntax errors in trajectories, more stable as depth increases (Table 1).
Qwen3-Coder-480B-A35B as an RLM: more syntax errors even on correct runs (Figure 4b). Extra recursion depth can hurt. Prefer coarser batches and a hard depth cap with this class of model.
In-context decomposition examples in the system prompt change the first split and the final score on OOLONG, even when the example is from another task (Figure 4a). Put a concrete split in the sub-agent brief.
## Failure modes
Infinite recursion: this skill is depth 1; sub-agents must not launch sub-agents.
The same span processed twice: partition once into disjoint batches; deduplicate before merge.
Sub-agent missing the question or the schema: the brief is the whole context that agent gets.
Merged answer contradicts the files: spot-check cited lines; re-read the conflict.
## Numbers from Table 1 (GPT-5 unless noted)
| Task | Without RLM | RLM (depth=1) |
|------|-------------|----------------|
| BrowseComp-Plus, 6 to 11M tokens | compaction 70.5%; GPT-5 base hits the context limit | 91.3% at $0.99 average |
| OOLONG | GPT-5 44.0 | 56.0 (+28.4% vs base; Qwen3-Coder +33.3%) |
| OOLONG-Pairs | F1 0.1 | F1 58.0 (76.0 at depth=3) |
A linear extrapolation of GPT-5-mini ingesting 6 to 11M tokens is $1.50 to $2.75. Median RLM cost is comparable or lower than the base model; the average can rise on long outlier trajectories.
## Skip decomposition when
- the job is one file, one function, or a single needle
- the answer sits in one obvious range
- latency matters more than completeness
- coordination would cost more than a direct read
SKILL.md
---
name: recursive-decomposition
description: "Decompose dense codebase-wide, multi-document, PDF, and aggregation work even when the input fits the context window, following Recursive Language Models (Zhang, Kraska, Khattab, 2025). Use when the user asks to analyse all files, a whole repo, all docs, large PDFs, or to aggregate or multi-hop across scattered sources. Skip one file, one function, a single needle, or a one-page PDF conversion. Triggers: long context, context rot, large codebase, many files, all files, big document, multi-document, PDF, aggregate, summarize everything, codebase-wide, multi-hop, recursive, sub-agents, map-reduce."
license: MIT
metadata:
author: massimodeluisa
version: "1.2.0"
paper: https://arxiv.org/abs/2512.24601
---
# Recursive Decomposition
Long inputs rot. Details get missed, distant parts get glued together by guesswork, and the reasoning drifts. The RLM paper calls it context rot. Do not load the whole input into the window. Treat it as an environment you query: size it, narrow it, split it, hand independent parts to sub-agents, verify on a small window, synthesise in code. Based on [Recursive Language Models](https://arxiv.org/abs/2512.24601) (Zhang, Kraska, Khattab, 2025).
## How to use
- `/recursive-decomposition`: apply the protocol below to the current task.
- `/recursive-decomposition <path or question>`: size that input first, then run the protocol on it.
## When it applies
Fit is not the test. Dense work can rot inside a million-token window. In the paper, OOLONG-Pairs is 32k tokens and GPT-5 scores 0.1% F1; RLM(depth=1) reaches 58.0% (arXiv:2512.24601, Table 1). 30k and 50k below are harness caps.
| Situation | Approach |
|-----------|----------|
| One file, one function, or a single needle | Read directly |
| Linear aggregate or list-everything, and completeness matters | Decompose (this skill) |
| Pairwise, quadratic, or multi-hop across scattered sources | Decompose, even under 30k tokens |
| 10+ files or 50k+ tokens | Decompose |
| Under 30k tokens and a localised answer | Read directly |
## Protocol
1. **Size the input** before reading anything: count files (glob, `find`), lines (`wc -l`), bytes (`ls -lh`), pages for PDFs.
2. **Filter** the search space with searches (content search, file patterns, keywords, file types) before opening any file. Chain filters: file type, then keyword, then meaning.
3. **Chunk** what remains: natural units (functions, classes, sections), line ranges, or keyword partitions. Batches of 5 to 10 files.
4. **Recurse** at depth 1: one sub-agent per independent batch, self-contained brief (files, question, output schema); run one parallel wave. Write the batch count first. If more batches remain, run the next wave after the merge. Sub-agents answer; they do not spawn sub-agents.
5. **Verify** the synthesised answer on a smaller window: extract the minimal evidence and re-check it; settle disagreements with a targeted re-read.
6. **Synthesise programmatically**: aggregate the structured results, deduplicate, categorise, then write the answer with file and line references.
## Rules
- MUST size the input before reading it
- MUST search before reading a directory; NEVER list a tree recursively as a substitute for search
- MUST read large files by line range: over 2,000 lines or 50 KB never in one read; PDFs over 100 pages or 30 MB by metadata or split
- MUST convert PDFs and Office files to markdown before reasoning. Prefer local anydoc (`npx -y @firecrawl/anydoc FILE -o .firecrawl/FILE.md`) from the Firecrawl convert-documents-to-markdown skill. Use cloud `firecrawl parse` for OCR (`--ocr hosted` / `firecrawl parse`), `-Q`, or `-S`. Never dump the binary into context. Cloud parse caps at 50 MB and about 1 credit per PDF page; split first if larger.
- NEVER load more than 5 files into the main context without a written batch plan
- MUST give every sub-agent its own context: the files, the question, the output schema
- MUST keep recursion depth at 1: sub-agents answer; they MUST NOT launch sub-agents
- MUST write the batch count before launch; one parallel wave, then merge; further waves only after that merge
- MUST spot-check the synthesised result against the sources before answering
- SHOULD read definitions first (`grep -n "function"`) and bodies later; tables of contents and abstracts before full text
- NEVER run the same query over the same content in several sub-agents; partition once into disjoint batches
- NEVER raise depth because the merge looks thin; re-read the disagreement instead
## Tools, agent-agnostic
| Need | Use |
|------|-----|
| Find files | the file search or glob tool, or `find` |
| Find content | the content search or grep tool, never a full read |
| Size | `wc -l`, `ls -lh`, page count (`pdfinfo` if present) |
| Read | the file reader with an offset and a limit, or `sed -n 'START,ENDp'` |
| PDF or Office file | anydoc first (`npx -y @firecrawl/anydoc FILE -o .firecrawl/out.md`), then grep the markdown. Cloud `firecrawl parse` for OCR, `-Q`, or `-S`. Else `pdfinfo` and a split. Never a full binary read |
| Delegate | the sub-agent or task tool, one brief per batch |
| Aggregate | a scratch file or structured notes, then one final pass |
Tool names differ between agents (Claude Code, Codex, Cursor, Gemini CLI); map the row to your agent's equivalent.
## Patterns
### Codebase analysis
"Find all error handling patterns." Glob the source files, grep `catch|throw|Error|except`, batch the matches by module (5 to 10 files), one sub-agent per batch with a fixed report schema, merge into a categorised summary with file references. Worked example: [references/codebase-analysis.md](references/codebase-analysis.md).
### Multi-document question answering
"What features are planned across all PRDs?" Glob the documents, size them, define an extraction schema (name, priority, status, quarter), one sub-agent per document group, deduplicate and categorise, spot-check three entries against the sources. Worked example: [references/document-aggregation.md](references/document-aggregation.md).
### Aggregation
"Summarise all TODO comments." Grep `TODO|FIXME|HACK`, group by module, extract context and priority per group, produce a prioritised list.
### Large PDFs
Glob `*.pdf`, size bytes and page count (`pdfinfo`). Over 100 pages or 30 MB: do not ingest. Convert with anydoc to `.firecrawl/` (Firecrawl skill `convert-documents-to-markdown`), then grep and chunk the markdown. If anydoc exits 3 (scanned pages), rerun with `--ocr hosted` or `firecrawl parse` (Firecrawl skill `firecrawl-parse`, 50 MB / ~1 credit per page). Batch by document, depth 1, spot-check titles against PDF metadata.
A one-page convert-to-markdown job is not this skill. Hand it to anydoc or `firecrawl parse` alone.
### Long output
Split the output into sections, generate each independently, store intermediate results in a file, stitch them with a coherence pass.
## Cost and quality
Decomposition spends coordination tokens. In the paper (arXiv:2512.24601, Table 1 and Figure 1), RLM(GPT-5, depth=1) scored 91.3% on BrowseComp-Plus (6 to 11M tokens) at $0.99 average, versus 70.5% for compaction. On OOLONG it beat GPT-5 by 28.4%. On OOLONG-Pairs it reached 58.0% F1 against a 0.1% base. Figure 1 scales inputs from 2^13 to 2^20 tokens. Median cost stays comparable to the base model; averages can rise on outlier trajectories. Thresholds: [references/cost-analysis.md](references/cost-analysis.md).
## Anti-patterns
| Anti-pattern | Fix |
|--------------|-----|
| Reading everything first "to get context" | Size, filter, then read by range |
| Dumping a PDF binary into the window | Parse to markdown, then grep and chunk |
| Decomposing a single needle or one-file lookup | Read directly |
| Sub-agents without the question or the schema | Self-contained briefs |
| Sub-agents that spawn sub-agents | Depth 1: they answer, they do not delegate |
| Trusting the merged answer | Spot-check on a small window |
| Re-querying the same content in several sub-agents | Partition once, disjoint batches |
## References
- [references/rlm-strategies.md](references/rlm-strategies.md): decomposition strategies from the paper
- [references/cost-analysis.md](references/cost-analysis.md): when to decompose, break-even thresholds
- [references/codebase-analysis.md](references/codebase-analysis.md): worked example, error handling across a codebase
- [references/document-aggregation.md](references/document-aggregation.md): worked example, feature extraction across PRDs
- Paper: [Recursive Language Models](https://arxiv.org/abs/2512.24601), Zhang, Kraska, Khattab, arXiv:2512.24601