Research (QRSPI Step 3)
SkillAI & modelsUse when questions.md is approved and the QRSPI pipeline needs objective codebase and web research, dispatches parallel specialist subagents per question, collates per-question findings into research/summary.md
Then ask your AI: use the Research (QRSPI Step 3) skill
Details
Instructions available. Your AI can read the instructions. Execution depends on the setup they require.
Account requirements not reviewed. Check the skill instructions before use; Ahel provides instructions and does not run this skill.
No other account needed.
Add it as a step in a pipeline you build on Ahel, or connect Ahel to Claude, ChatGPT, Cursor, Claude Code or Codex and ask your AI to use it.
What this skill tells your AI
The instructions your AI receives, as published by dfrysinger/qrspi-plus in skills/research/SKILL.md and read by Ahel’s review.
PRECONDITION: Invoke qrspi:using-qrspi skill to ensure global pipeline rules are in context. (Idempotent on session re-entry. Subagents are exempt — SUBAGENT-STOP in using-qrspi handles that.)
Announce at start: "I'm using the QRSPI Research skill to investigate the research questions."
Overview
Objective exploration driven by the research questions. Gathers facts, not opinions. Each question gets a focused specialist agent with the right tools for its research type. Research isolation is structural — no research subagent ever sees goals.md.
Artifact Gating
Required inputs:
questions.mdwithstatus: approved
If questions.md doesn't exist or isn't approved, refuse to run and tell the user to complete the Questions step first.
Read config.md from the artifact directory to determine whether second-model reviews are enabled. If config.md doesn't exist, default to second_reviewer: false.
Execution Model
Parallel specialist subagents (ISOLATED — structurally enforced). One subagent per question (or per small group of related questions). A collation subagent assembles the per-question ## Summary blocks into _collated.md at the end; the orchestrator then renames _collated.md to summary.md via a single mv Bash call.
CRITICAL: goals.md is deliberately withheld from ALL research subagents. This is enforced structurally — subagent prompts contain only the question(s) assigned to them. goals.md is never passed to any research subagent, including the collation subagent. This prevents confirmation bias.
Process
Dispatch
- Parse
questions.md— extract each numbered question with its research type tag - Group related questions (e.g., two questions about the same subsystem) to avoid redundant exploration
- Dispatch specialist subagents based on research type tags:
| Research Type | Agent Tools | Focus |
|---|---|---|
[codebase] | File read, grep, glob | Read code, trace logic flows, map architecture. Report file:line references. |
[web] | Web search, web fetch | Search competitors, libraries, best practices, docs. Report URLs and sources. |
[hybrid] | All tools | Compare local implementation against external standards/alternatives. |
- Independent questions run in parallel subagents (use concurrent dispatches in a single orchestrator response)
- Each subagent writes its own report directly to
{ABS_RESEARCH_DIR}/q{NN}-{type}.mdusing theWritetool. The orchestrator passes the absolute path into the subagent prompt; subagents do NOT return findings as text. Theq*.mdfilename pattern is intentionally outside the Claude Code 2.1.x subagent-guardrail blocklist (filenames whose stem starts (case-insensitive) withreport,summary,findings, oranalysis), so direct write succeeds.
Per-Researcher Subagent
!cat skills/_shared/evergreen-output-rule.md
Inputs: Only the assigned question(s) from questions.md. NO goals.md. NO raw feedback/research-round-*.md files (raw feedback may carry user goals/intent — forwarding it to a research subagent breaks the research-isolation invariant). The orchestrator also passes the absolute output path ({ABS_RESEARCH_DIR}/q{NN}-{type}.md) and, for grouped questions, the full set of question IDs the report should cover. On re-dispatch via Rejection path 2, the orchestrator passes a sanitized defect summary it authors itself from the user's feedback — defect-only bullet points (e.g., "missed the auth module", "TL;DR is missing", "broken file:line citation"). Goal-bearing or intent-bearing language is stripped before the summary reaches the subagent.
Dispatch — for each question (or grouped set of related questions), dispatch(subagent_type: qrspi-research-specialist, model: sonnet) in parallel via concurrent dispatches in a single orchestrator response. The agent body (loaded by the runtime) carries the full research-agent rules, output-format template, and contract; the dispatch prompt carries only the parameters below.
Dispatch parameters (per specialist):
question_body: wrapped body of the assignedresearch/q*.mdquestion(s) between<<<UNTRUSTED-ARTIFACT-START id=question>>>and<<<UNTRUSTED-ARTIFACT-END id=question>>>markers; for grouped questions, all assigned question texts concatenated within the wrapperoutput_path: absolute path the specialist writes its report to (<ABS_RESEARCH_DIR>/q{NN}-{type}.md)question_ids: comma-separated numeric IDs the report should cover (e.g.,3or3,7)defect_summary(re-dispatch via Rejection path 2 only): orchestrator-authored sanitized defect summary; goal-bearing/intent-bearing language stripped
Direct-write contract (unambiguous default). Per-researcher subagents write their q*.md report directly to output_path via the Write tool. They do not return report content as text. Text-return is not used anywhere in the research pipeline — the collation subagent also direct-writes (to the research/_collated.md staging filename, which the orchestrator then renames to research/summary.md). The staging-filename pattern exists precisely to avoid text-return through main chat. If the orchestrator ever omits output_path from a per-researcher dispatch, that is a dispatch defect — fix the orchestrator, do not fall back to text-return.
Summary-last authoring order. The per-question report template places the structured summary block (TL;DR / key findings / surprises / caveats) at the top of the file — that is the consumer-facing reading order. Authoring order is the inverse: investigate first, draft the full report body, then author the summary block last, then place it at the top of the file. Do not generate the summary from intent before the body is complete.
Research-isolation invariant — the specialist dispatch carries NO companion_goals, NO other-question content, and NO feedback/research-round-*.md files. This is structurally enforced — the agent body refuses goals.md / cross-question content if it ever appears in the dispatch prompt. Research isolation prevents confirmation bias.
Citation-Density Post-Validation Hook (trusted-model re-run path)
Every qrspi-research-specialist dispatch is observed at the dispatch boundary by a post-output citation-density validator. The validator runs AFTER the specialist's q*.md report is written and BEFORE the report is read by the collation subagent or any downstream consumer. The floor that the validator measures against is the validators.citation_density_floor: key in config.md (default 0.05 — see the schema documentation in skills/using-qrspi/SKILL.md for the key's full semantics, the citation-counting rule, and the rationale for the default value).
Above-floor result. The report proceeds unchanged. The collation subagent reads it on the same schedule as any other specialist output. No re-run, no diagnostic, no telemetry rerun-count increment.
Below-floor result (first occurrence). The dispatch re-runs the specialist EXACTLY ONCE on the trusted model (the role's trusted_path: route from config.md's model_routing: table, OR the trusted-tier default — see skills/_shared/model-routing-defaults.md). The re-run carries identical question_body and question_ids parameters as the original dispatch — only the (provider, model) pair changes. The orchestrator increments the per-task citation_density_rerun_count field in this task's telemetry record (see skills/implement/SKILL.md § Per-Task Telemetry Emission).
Below-floor result (re-run also below floor). The validator emits a loud diagnostic naming the below-floor density value, exits non-zero (propagating the failure signal to the Implement orchestrator), and does NOT silently forward the below-floor output to downstream consumers. The non-zero exit is observably distinct from the success path — it is NOT a zero-exit-with-empty-body. The Implement orchestrator treats the non-zero exit as a specialist-dispatch failure and may retry on a different topic angle, escalate to opus, or proceed with degraded output per its BLOCKED escape hatch; the validator never silently degrades.
Implement's consumer-side wrap (telemetry rerun-count increment, non-zero-exit handling, BLOCKED escape) consumes this hook; pointer in skills/implement/SKILL.md § Specialist citation-density wrap.
Collation Subagent (verbatim extraction, not synthesis)
After all per-question research completes, dispatch a lightweight collation subagent whose ONLY job is to extract the ## Summary block from each q*.md file verbatim and assemble them into the staging file research/_collated.md (which the orchestrator subsequently renames to research/summary.md via a single mv Bash call). This is mechanical extraction — not synthesis, not re-prose. Each per-question report already carries a structured TL;DR / Key findings / Surprises / Caveats block at its head (see Per-Researcher Subagent template above); the assembled output is just those blocks stitched together in question order, plus a short Cross-References section.
Why a collation subagent (not orchestrator-direct, not synthesis):
- Context hygiene. The collation subagent reads all per-question
q*.mdfiles into ITS context, then exits. Main chat never loads the full report bodies. If main chat did the collation directly, allq*.mdcontents would persist in main chat's conversation history and slow every downstream stage — Design proposes better architecture on a lean context. - No re-synthesis. Verbatim extraction is bounded mechanical work; re-prosing the per-question reports into one risks interpretive spin not present in the originals. The per-question
## Summaryblocks are the canonical at-a-glance summary by contract. - Guardrail-compatible direct write via staging filename.
summary.mdmatches the Claude Code 2.1.x subagent-guardrail blocklist (filenames whose stem starts (case-insensitive) withreport,summary,findings, oranalysis), so the subagent cannot Write to it directly. To avoid text-return (which would route the assembled content through main chat's context, defeating the hygiene goal), the subagent instead writes to a staging filename outside the blocklist —research/_collated.md— and the orchestrator then renames it toresearch/summary.mdwith a singlemvBash call. Themvadds only the command string and a tiny confirmation to main chat's context, not the file body. The public artifact name (summary.md) is unchanged, preserving all downstream references in other QRSPI skills.
Inputs to the collation subagent: All research/q*.md files. NO goals.md. NO questions.md. NO raw feedback/research-round-*.md files (raw feedback may carry user goals/intent — forwarding it breaks research isolation). On re-dispatch via Rejection path 1, the orchestrator passes a sanitized defect summary it authors itself from the user's feedback — bullet points covering collation-output defects in either dimension collation owns: extraction fidelity (e.g., "Q5 TL;DR was misquoted in the prior _collated.md") OR Cross-References authoring (e.g., "missing link between Q3 and Q7 findings"). Goal/intent-bearing language is stripped. The verbatim-extraction contract still binds — extraction-fidelity defects are fixed by re-extracting per the Procedure, NOT by paraphrasing.
Dispatch — dispatch(subagent_type: qrspi-research-collator, model: sonnet). The agent body (loaded by the runtime) carries the verbatim-extraction rules, the procedure, the output-file shape, and the contract-violation list. The dispatch prompt carries only the parameters below.
Dispatch parameters:
qfile_paths: list of absolute paths toresearch/q*.mdfiles (passed as paths, not bodies — the collator Reads each file itself; this is required by the staging-filename + verbatim-extraction contract and keeps research bodies out of main chat's context)output_path: absolute path to the staging file (<ABS_RESEARCH_DIR>/_collated.md) — NOTsummary.md(the Claude Code 2.1.x subagent-guardrail blockssummary.mddirect write; the orchestrator renames_collated.md→summary.mdafter the subagent returns, per the staging-rename pattern documented above)defect_summary(re-dispatch via Rejection path 1 only): orchestrator-authored sanitized defect summary scoped to either dimension collation owns (extraction fidelity OR Cross-References authoring); goal-bearing/intent-bearing language stripped
Research-isolation invariant — the collator dispatch carries NO companion_goals and NO companion_questions. NO raw feedback/research-round-*.md files. The agent body refuses any of those if they appear in the dispatch prompt.
Orchestrator handling: When the collation subagent returns confirmation, run a single Bash call to rename the staging file to its final name: mv {ABS_RESEARCH_DIR}/_collated.md {ABS_RESEARCH_DIR}/summary.md. If the subagent returned a contract-violation report instead of writing _collated.md, re-dispatch the offending researcher (per the specialist dispatch above) with the orchestrator-authored sanitized defect summary, then re-dispatch collation. Isolation-violation handling is separate — see § Isolation-Violation Orchestrator Handling below.
Isolation-Violation Orchestrator Handling
All three research subagents (specialist, collator, reviewer) run a Pre-Flight Isolation Check on their incoming dispatch prompts (see the ## Pre-Flight Isolation Check (FAIL-LOUD) section in each agent body). If a goals-content or cross-question pattern is detected, the subagent does NOT write its expected output — it returns a single-line text response with the load-bearing prefix RESEARCH-ISOLATION-VIOLATION: followed by the pattern name and short evidence (≤80 chars).
Orchestrator detection: when any research subagent returns text instead of writing its expected file, inspect the first line for the prefix RESEARCH-ISOLATION-VIOLATION:.
Orchestrator response (fail-loud, not retry-with-same-leak):
- STOP the affected research dispatch — do NOT silently re-run with the same prompt (that produces an infinite refusal loop).
- Identify which dispatch parameter carried the leak. The violation message names the pattern:
field-name-leakage⇒ a forbidden parameter name (companion_goals,companion_questions, etc.) was attached;filename-leakage⇒ agoals.md/questions.mdpayload was wrapped into the prompt;goals-heading-leakage/goal-framing-triplet⇒ goals body content was smuggled intoquestion_body,companion_qfiles, ordefect_summary;cross-question-leakage⇒ q*.md payloads from outside the assignedquestion_idsreached the specialist;questions-compendium-leakage⇒questions.mdreached collator/reviewer;sanitization-bypass⇒ the orchestrator-authoreddefect_summarystill carried goal/intent prose. - Repair the dispatch:
- Field-name / filename / heading / triplet leakage: remove the offending parameter or strip the offending wrapped block; re-emit the dispatch.
- Cross-question leakage: re-emit the specialist dispatch with
question_bodycontaining only the assigned IDs. - Sanitization-bypass: re-author the
defect_summaryfrom the raw feedback, stripping goal/intent prose more aggressively. If the raw feedback is entirely goal-bearing, surface the issue to the user (per Rejection Behavior step 3 edge case) rather than re-dispatching with an empty summary.
- Re-dispatch only after the prompt has been repaired.
Why this matters: the prior prose-only "report violation in your final confirmation" instruction relied on the subagent voluntarily noticing and surfacing the leak. The Pre-Flight check is structural — refusal happens before any goals-influenced research output can be produced. Pinned by tests/unit/test-research-isolation-fail-loud.bats.
Review Round
Compaction checkpoint: pre-fanout. Reviewer dispatch reads research/summary.md + every research/q*.md file + the agent-embedded reviewer protocol; saturated context produces shallow findings. See using-qrspi ## Compaction Checkpoints for the iron-rule contract.
Surface a todo: title Recommend /compact (pre-fanout) — research, description pre-fanout: reviewer dispatch reads research/summary.md + all q*.md files. User decides whether to /compact..
Apply the Standard Review Loop from using-qrspi/SKILL.md. Research has no scope-reviewer per canonical artifact-tree topology — only the quality reviewer runs (one Claude dispatch + one Codex dispatch when second_reviewer: true).
Dispatch the round through dispatch-agent's high-level entry. Run scripts/dispatch-agent.sh --step research --round ${ROUND} --artifact-dir <ABS_ARTIFACT_DIR> (plus the per-skill --output-dir/--artifact/--agents flags below). High-level mode invokes scripts/review-prep.sh to emit <ABS_ARTIFACT_DIR>/reviews/research/round-${ROUND}.diff and threads diff_file_path: into the reviewer prompt; the orchestrator runs no git diff Bash redirect of its own. Reviewer diff narrowing targets research/summary.md (the canonical review artifact); per-question research/q*.md files are read by reviewers as companion data via Read calls, not diff-narrowed. When the artifact directory is not inside a git repository, review-prep skips diff emission and diff_file_path: is omitted. For round 01 pass --base-ref <base-branch>; on round >= 2 review-prep auto-narrows by reading reviews/research/round-$((ROUND-1))-commit.txt (named diagnostics anchor-file-missing: / sha-format-invalid: halt before the SHA reaches git diff). Scope-tag narrowing (when active) reaches the reviewer as scope_hint: wrapped between <<<UNTRUSTED-SCOPE-HINT-START id=scope_hint>>> / <<<UNTRUSTED-SCOPE-HINT-END id=scope_hint>>> markers per the reviewer-protocol Reviewer Dispatch Contract.
The round's reviewers dispatch through the universal dispatch chain (scripts/dispatch-agent.sh → subagent fan-out → scripts/await-round.sh). Set the per-skill dispatch parameters below, then include the shared reviewer-dispatch prose. Include the *-codex peer tags in REVIEW_AGENTS only when second_reviewer: true; otherwise list only the *-claude tags.
REVIEW_STEP="research"
REVIEW_ROUND="${ROUND}" # current review round (NN)
REVIEW_OUTPUT_DIR="<ABS_ARTIFACT_DIR>/reviews/research/round-${ROUND}/"
REVIEW_ARTIFACT="research.md"
REVIEW_AGENTS="quality-claude=qrspi-research-reviewer,quality-codex=qrspi-research-reviewer"
!cat skills/_shared/reviewer-dispatch-prose.md
Rejection Behavior
Because Research involves multiple subagents, rejection has two paths depending on user feedback. In both cases:
- The orchestrator writes the user's raw feedback to
feedback/research-round-{NN}.md(see using-qrspi Feedback File Format) — this is the durable record. The raw feedback file is NEVER passed to a research subagent — that would break research isolation, since user feedback can carry goals or design intent. - The orchestrator reads the feedback and authors a sanitized defect summary for subagent consumption: a bullet list of defects only, with all goal-bearing or intent-bearing language stripped. Each bullet states a defect ("X is missing", "Y is malformed", "Z citation is broken") — never a goal ("we need to X" / "for our auth refactor").
- The orchestrator passes the defect summary (not the raw feedback file) to the re-dispatched subagent(s) per the path below. Edge case: if after stripping goal/intent language the defect summary is empty (the user's feedback was entirely goal-bearing with no concrete defect cited), do NOT re-dispatch with an empty summary — surface the issue back to the user and ask them to reformulate their feedback as concrete defects.
Rejection path 1 — Collation problem ("the Cross-References miss an important link", "Q3's summary block is being misquoted in summary.md"):
- Re-dispatch the collation subagent with the existing
q*.mdfiles + the orchestrator-authored defect summary scoped to either dimension collation owns: extraction fidelity OR Cross-References authoring. The subagent re-extracts## Summaryblocks verbatim (fixing any extraction-fidelity defects by re-extracting, not paraphrasing) and re-authors Cross-References to address the cited defects.
Rejection path 2 — Underlying research problem ("Q3's findings are incomplete — the researcher missed the auth module", "Q5's summary block doesn't match the contract template"):
- Re-run only the specific researcher(s) whose findings or summary blocks were problematic, passing the orchestrator-authored defect summary scoped to those defects. Researchers re-write their
q*.mddirectly per the Per-Researcher template. - Then re-dispatch the collation subagent to rebuild
summary.mdfrom the updated per-question reports.
Ask the user which path applies when they reject.
Human Gate
Present research/summary.md to the user. Note that this is ~200 lines — much easier to review than code. Always state the review status when presenting: either "Reviews passed clean in round N" or "Reviews found issues in round N which were fixed but not re-verified."
On approval, if reviews have not passed clean, note this and ask if they'd like a review loop before finalizing. Then write status: approved in frontmatter.
Quick-Fix Auto-Approve Branch
When config.md carries pipeline: quick, the human-approval gate is skipped after any review round (initial or post-fix) that produces zero kept findings. When this branch fires, status: approved is written to research/summary.md frontmatter automatically without waiting for user input.
Verifier-gate precondition. "Zero kept findings" is satisfied only when the verifier has affirmatively confirmed the count — a vacuously-zero count from an undispatched verifier does NOT satisfy the gate and surfaces the round to the user as unverified (matching the HARD-GATE contract in skills/implement/SKILL.md). If config.md is missing or unreadable when this branch is evaluated, the auto-approve branch does NOT fire — the orchestrator surfaces a named diagnostic and falls through to the standard human-approval gate (fail-loud, not silent fallback to either pipeline mode). The gate passes when ANY of the following hold for the current round's directory (reviews/research/round-NN/):
Shortened here. Read the whole file on GitHub.
Signals
- GitHub stars
- 38
- Forks
- 4
- Last commit
- Sep 2026
- Hacker News mentions
- 20
Advanced
- Item type
- skill
- Key
research-dfrysinger- Source
- github.com/dfrysinger/qrspi-plus
github.com/dfrysinger/qrspi-plus
Related picks
Skill · mattpocock
The pick for TypeScripttypescript-providers
Skill · composiohq
The pick for TypeScriptaws-architecture-diagram
Skill · awslabs
The pick for AWSaws-health-events
Skill · aws
The pick for AWScloudflare-email-service
Skill · cloudflare
The pick for Cloudflarecloudflare-setup
Skill · peterpme
The pick for Cloudflare