A research report skill with a fact-check before writing
Research written by one agent blends what it found with what it assumed. Here primary sources, figures, counterpoints and open questions are gathered separately, and a verifier keeps a claim only when it names a source that says it and every figure has a date.
7 steps in 4 stages · 7 subagents · a kit of 12 files
Free. Built in your browser; nothing is uploaded. No API key, no account.
How it runs
Steps in the same stage start at the same time. Each subagent gets only what its incoming edges carry, and a step in a fresh context never sees the reasoning of the steps before it. A gate decides whether the work passes, and a failed gate re-runs the work it checks.
- Stage 1
Split · Planner
sonnet · Read
- Stage 24 in parallel
Primary Sources · Worker
haiku · Read, WebSearch, WebFetch
Figures & Data · Worker
haiku · Read, WebSearch, WebFetch
Counterpoints · Worker
haiku · Read, WebSearch, WebFetch
Open Questions · Worker
haiku · Read, WebSearch, WebFetch
- Stage 3
VERIFIER (Fresh Context) · Checker
opus · Read, Grep, Glob · fresh context
Gate: Every kept claim names a source that says it, and every figure has a date.
- Stage 4
Final answer · Writer
sonnet · Read
Use it when
- You need a report you can cite, not a summary.
- The topic has numbers that go out of date.
- You want the counterarguments, not only the case for.
Change these first
- Ask the workers to read your own documents as well as the web.
- Change the final answer's format to the report your readers expect.
Need a workflow built for your team? Fixed-price packages
What you get
An orchestrating SKILL.md, one Claude Code subagent per step with its own tools and model, and the same skill as a Cursor rule. Your agent runs it with its own subagents; nothing on this site calls a model.
- .claude/skills/research-report/SKILL.mdorchestrator
- .claude/skills/research-report/graph.skill.mdgraph source
- .claude/agents/research-report-splitter.mdsubagent
- .claude/agents/research-report-sources.mdsubagent
- .claude/agents/research-report-figures.mdsubagent
- .claude/agents/research-report-counterpoints.mdsubagent
- .skill-graph/.gitignoresupport
- .cursor/rules/research-report.mdcCursor rule
- README.mdreadme
- + 3 more subagents
---
name: research-report
description: "Researches a question from four angles in parallel, fact-checks every claim against its source in a fresh context, and writes a sourced report."
---
# research-report
Researches a question from four angles in parallel, fact-checks every claim against its source in a fresh context, and writes a sourced report.
This skill is a graph: each step has its own prompt and context, and only the payloads named below move between steps.
## Plan
Run the stages in order. Every model step runs in its own subagent; do not do a step's work yourself. Only command steps and approvals are yours to run. Steps in the same stage do not depend on each other: start them together, in one message with several subagent calls.
The input (`goal`) is the user's goal: their request, word for word, handed on under `### goal`. Ask for it if the user has not given one.
### Stage 1 · 1 step
- **SPLIT** (`splitter`) → subagent `research-report-splitter`
- Hand it: the user's goal.
- Expect: `subtask_json` (json).
- Contract check (`subtask_json`): save its output to `.skill-graph/research-report/splitter.json`, then run `node -e "const fs=require('fs');const d=JSON.parse(fs.readFileSync(process.argv[1],'utf8'));const k=['sources','figures','counterpoints','open_questions'];const ok=d!==null&&typeof d==='object'&&Array.isArray(d)===false;const m=ok?k.filter((x)=>Object.hasOwn(d,x)===false):k;if(m.length>0){console.error('Missing keys: '+m.join(', '));process.exit(1)}" .skill-graph/research-report/splitter.json` with Bash. Exit code 0 passes.
- If a contract check fails: re-run this step once with its previous answer and the error output. If it fails again, stop and show the user the output.
### Stage 2 · 4 steps in parallel
- **Primary Sources** (`sources`) → subagent `research-report-sources`
- Hand it: only the `sources` value from the JSON of SPLIT (`splitter`).
- Expect: `research_findings` (markdown).
- **Figures & Data** (`figures`) → subagent `research-report-figures`
- Hand it: only the `figures` value from the JSON of SPLIT (`splitter`).
- Expect: `research_findings` (markdown).
- **Counterpoints** (`counterpoints`) → subagent `research-report-counterpoints`
- Hand it: only the `counterpoints` value from the JSON of SPLIT (`splitter`).
- Expect: `research_findings` (markdown).
- **Open Questions** (`open_questions`) → subagent `research-report-open-questions`
- Hand it: only the `open_questions` value from the JSON of SPLIT (`splitter`).
- Expect: `research_findings` (markdown).
… 37 more lines in the kitOr let Claude adapt it
Add Skill Graph to Claude Code as a connector, then ask. Claude loads this starter, changes it for your project, checks it and hands you the kit.
Set up the connectorAsk Claude
Use Skill Graph: get the research-report template, adapt it to this project, validate it until it has no errors, and compile it for Claude Code.The graph file
The whole workflow is one Markdown file, graph.skill.md: the steps and edges in the frontmatter, one prompt per step below it. The guide explains every field.
graph.skill.md · 150 lines
---
name: research-report
description: Researches a question from four angles in parallel, fact-checks every claim against its source in a fresh context, and writes a sourced report.
spec: skill-graph/1
topology:
nodes:
- id: goal
type: input
label: RESEARCH QUESTION
- id: splitter
type: orchestrator
label: SPLIT
model: sonnet
tools:
- Read
- id: sources
type: worker
label: Primary Sources
model: haiku
tools:
- Read
- WebSearch
- WebFetch
- id: figures
type: worker
label: Figures & Data
model: haiku
tools:
- Read
- WebSearch
- WebFetch
- id: counterpoints
type: worker
label: Counterpoints
model: haiku
tools:
- Read
- WebSearch
- WebFetch
- id: open_questions
type: worker
label: Open Questions
model: haiku
tools:
- Read
- WebSearch
- WebFetch
- id: verifier
type: verifier
label: VERIFIER (Fresh Context)
isolated_context: true
model: opus
tools:
- Read
- Grep
- Glob
gate: Every kept claim names a source that says it, and every figure has a date.
on_fail: retry
max_retries: 1
- id: merger
type: synthesizer
label: FINAL ANSWER
model: sonnet
tools:
- Read
edges:
- from: goal
to: splitter
- from: splitter
to:
- sources
- figures
- counterpoints
- open_questions
contract: subtask_json
- from:
- sources
- figures
- counterpoints
- open_questions
to: verifier
contract: research_findings
- from: verifier
to: merger
contract: checked_findings
contracts:
subtask_json:
format: json
description: One key per worker id; each value is that worker's task.
research_findings:
format: markdown
description: A numbered list of claims; every claim has its source, a short quote and a confidence of high, medium or low.
checked_findings:
format: markdown
description: Only the claims a cited source supports, then the rejected claims with the reason.
---
# Node Prompts & Edge Contracts
## @node: splitter
You are the research lead. Turn the question into four research tasks, one per angle, each saying what to look for and how recent it must be.
Input:
{{goal}}
Output strictly as JSON with one key per worker: `sources`, `figures`, `counterpoints`, `open_questions`. Each value is the exact task for that worker.
## @node: sources
Find ONLY primary sources: official documents, papers, datasets and first-hand accounts, with what each one says.
Your task: {{splitter.sources}}
Return findings as a numbered list. For each one give the source (title and URL), what it says in a short quote, and a confidence of high, medium or low.
## @node: figures
Collect ONLY figures: statistics, dates, prices and measurements, each with where it comes from and how recent it is.
Your task: {{splitter.figures}}
Return findings as a numbered list. For each one give the source (title and URL), what it says in a short quote, and a confidence of high, medium or low.
## @node: counterpoints
Look ONLY for disagreement: credible sources that contradict the obvious answer, and the reasons they give.
Your task: {{splitter.counterpoints}}
Return findings as a numbered list. For each one give the source (title and URL), what it says in a short quote, and a confidence of high, medium or low.
## @node: open_questions
Find ONLY what is unknown or disputed: gaps in the evidence and claims that nobody has checked.
Your task: {{splitter.open_questions}}
Return findings as a numbered list. For each one give the source (title and URL), what it says in a short quote, and a confidence of high, medium or low.
## @node: verifier
You are a fact-checker. Check each claim against the source it cites; reject claims whose source does not say it or is not named.
You only see the raw worker outputs below, not their reasoning. Drop any claim without concrete evidence, merge duplicates, and mark what you rejected and why.
Original input: {{goal}}
Worker outputs:
{{workers.output}}
## @node: merger
Write the research report in Markdown: the answer in two sentences, Key findings with their sources, Where sources disagree, Open questions, and a list of Sources.
{{verifier.output}}
More starter graphs
- PR reviewSecurity, performance, architecture and test coverage reviewers
- Bug triageStack trace, logs, recent commits and a minimal repro
- RFC discoveryFeasibility, prior art, interface design and failure modes
- Ship a changeEdit until tests pass, review the diff in parallel, approve once before the PR
- Review each fileOne fresh-context review per changed file, however many there are, then merge and gate
- Parallel auditResearch, compare, check and find gaps, then verify in a fresh context
- Write an articleAngle, outline, facts and reader in parallel, then an editor check before the draft
- Analyze a datasetProfile, clean, analyze and sanity-check a table in parallel, then re-check every figure
- Design an appUsers, features, screens and data model in parallel, then a product review before the spec