| name | research |
| description | Use 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 |
Research (QRSPI Step 3)
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.md with status: 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.
Do NOT pass goals.md to ANY research subagent, including the collation subagent.
Research isolation is structural — this is not optional, not a judgment call.
If a subagent prompt contains goals.md content, the isolation invariant is broken.
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 the Agent tool with multiple concurrent calls)
- Each subagent writes its own report directly to
{ABS_RESEARCH_DIR}/q{NN}-{type}.md using the Write tool. The orchestrator passes the absolute path into the subagent prompt; subagents do NOT return findings as text. The q*.md filename pattern is intentionally outside the Claude Code 2.1.x subagent-guardrail blocklist (filenames whose stem starts (case-insensitive) with report, summary, findings, or analysis), so direct write succeeds.
Per-Researcher Subagent
Evergreen-Output Rule
Any artifact in the QRSPI run directory governed by status: draft → approved frontmatter promotion (goals, design, structure, phasing, plan, parallelization, roadmap, future-goals, and any future artifact adopting this lifecycle) describes the current state of decisions. The reader is a downstream agent or future maintainer.
(Excludes by design: SKILL.md files — skills carry rule rationale legitimately; feedback/*.md — the designated home for dialogue exhaust; reviews/**/*.md — finding rationale; config.md — non-narrative.)
Litmus test (apply to every paragraph before write). Two filters, in order:
- Is the subject the decision (the thing being designed / planned / scoped)? → keep.
- Is the subject the document itself — its drafts, its history, the dialogue that produced it, "us"? → cut.
A sentence that only makes sense as a delta from a prior state is dialogue exhaust — strip it.
Permitted substantive content (do NOT confuse with dialogue exhaust):
- Chosen approach and its rationale (inline)
- Rejected alternatives and tradeoffs, where the artifact template asks for them (e.g., design.md's
## Trade-offs Considered — substantive content about the decision space, not about the document's history)
- Rationale embedded inline as one parenthetical when a downstream reader needs it
Named antagonist patterns — strip on sight, substitute as shown:
| Antagonist pattern | Recognize by | Replace with |
|---|
| Session / drafting notes | "Rule X drafting note," "this collapsed from 3 to 1 because…" | Nothing — delete. If a fact matters, embed inline in the decision. |
| Version-history narration | "earlier draft said X," "previously," "originally," "pre-cleanup" | Nothing — git history holds versions. |
| Inside baseball | text addressed to "us" / "the author," meta-explanation of the document's own structure ("this section is split into A and B because…") | The decision the structure expresses — without the structural explanation. |
| Compaction-loss recovery notes | "this nuance was almost lost during…" | Nothing — if the nuance is needed, the rule itself carries it. |
| Failure-modes-prevented lists | bullets that justify why a rule exists rather than state what to do | Strengthen the rule's wording; delete the justification list. |
Decision-process history (drafts, review rounds, feedback applied, compaction recovery) lives in feedback files, review findings, PR descriptions, and git history — never in the artifact.
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 Agent({ subagent_type: "qrspi-research-specialist", model: "sonnet" }) in parallel via concurrent Agent tool calls. 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 assigned research/q*.md question(s) between <<<UNTRUSTED-ARTIFACT-START id=question>>> and <<<UNTRUSTED-ARTIFACT-END id=question>>> markers; for grouped questions, all assigned question texts concatenated within the wrapper
output_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., 3 or 3,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 (G5; 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 (T01) 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 from the routing matrix in skills/implement/SKILL.md § G5 Initial Routing Matrix). 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.
The dispatch-wrapping contract that consumes this hook (when the wrap fires, how the telemetry rerun-count is incremented, what the orchestrator does with the non-zero exit) is authored in skills/implement/SKILL.md § Specialist Citation-Density Validator. This Research section documents the producer-side hook contract that Implement consumes.
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*.md files into ITS context, then exits. Main chat never loads the full report bodies. If main chat did the collation directly, all q*.md contents 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
## Summary blocks are the canonical at-a-glance summary by contract.
- Guardrail-compatible direct write via staging filename.
summary.md matches the Claude Code 2.1.x subagent-guardrail blocklist (filenames whose stem starts (case-insensitive) with report, summary, findings, or analysis), 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 to research/summary.md with a single mv Bash call. The mv adds 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 — Agent({ 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 to research/q*.md files (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) — NOT summary.md (the Claude Code 2.1.x subagent-guardrail blocks summary.md direct write; the orchestrator renames _collated.md → summary.md after 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 ⇒ a goals.md / questions.md payload was wrapped into the prompt; goals-heading-leakage / goal-framing-triplet ⇒ goals body content was smuggled into question_body, companion_qfiles, or defect_summary; cross-question-leakage ⇒ q*.md payloads from outside the assigned question_ids reached the specialist; questions-compendium-leakage ⇒ questions.md reached collator/reviewer; sanitization-bypass ⇒ the orchestrator-authored defect_summary still 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_body containing only the assigned IDs.
- Sanitization-bypass: re-author the
defect_summary from 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.
Call TaskCreate({ subject: "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. When the artifact directory is not inside a git repository, review-prep skips diff emission and diff_file_path: is omitted. When using-qrspi step 12 narrows the base ref, pass --base-ref "$(cat reviews/research/round-$((ROUND-1))-commit.txt)" so review-prep narrows against the prior round's per-round commit SHA (using-qrspi step 12 owns the SHA-format validation and the anchor-file-missing:/sha-format-invalid: halt directions 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 → Task 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}"
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"
Reviewer Dispatch (shared)
With $REVIEW_STEP, $REVIEW_ROUND, $REVIEW_OUTPUT_DIR, $REVIEW_ARTIFACT, and $REVIEW_AGENTS set by the per-skill preamble above, run:
scripts/dispatch-agent.sh --step "$REVIEW_STEP" --round "$REVIEW_ROUND" \
--output-dir "$REVIEW_OUTPUT_DIR" --artifact "$REVIEW_ARTIFACT" \
--agents "$REVIEW_AGENTS"
dispatch-agent emits M lines on stdout (one per first-party reviewer; zero lines for a third-party-only batch). Each line has the form:
MODE=first_party TAG=<tag> SUBAGENT_TYPE=<agent-name> MODEL=<resolved-model> PROMPT_FILE=<absolute-path>
For every emitted spec line, invoke the Task tool with these arguments (parse the line as space-separated KEY=VALUE pairs; values contain no spaces):
subagent_type = the SUBAGENT_TYPE value, verbatim
model = the MODEL value, verbatim
prompt = the literal string "DISPATCH_FILE=<PROMPT_FILE-value>" — a single-line env-var-style reference; the prompt argument has no other content
Invoke all M Task tool calls in parallel in one orchestrator response (one Task call per spec line). The reviewer agent body's first instruction is to Read its DISPATCH_FILE — do not pre-Read the file yourself; the dispatch context belongs in the subagent's window, not the orchestrator's.
Iron law (orchestrator-side dispatch contract): invoke the Task tool exactly once per emitted spec line, with SUBAGENT_TYPE, MODEL, and PROMPT_FILE copied verbatim. Skipping a line, deduplicating across lines, modifying any value, or substituting a different subagent_type is a contract violation. The dispatch manifest ($REVIEW_OUTPUT_DIR/.dispatch-manifest.json) records expected dispatches; the apply-fix step's "expected tag produced no output" diagnostic catches missed or mis-routed Task invocations.
Capture each Task return value to disk before draining. After each Task call returns, write the subagent's reply text (the full Task return string) to $REVIEW_OUTPUT_DIR/.dispatch/<TAG>.raw using the create tool, where <TAG> is the TAG value from the corresponding spec line. This is mandatory regardless of whether the subagent appeared to write per-finding files itself. Rationale: when a subagent cannot use the Write tool (read-only sandbox; missing allowed-tools entry; tool denial at runtime) it emits findings via the <<<FINDING-BOUNDARY>>> stdout contract instead. await-round.sh recovers those findings via a universal stdout-fallback that reads .dispatch/<TAG>.raw and pipes it through third-party-finding-splitter.sh; without the captured .raw file the fallback has nothing to work with and the round looks (incorrectly) clean.
After all Task tool calls return AND all .raw captures are written (Task tool is synchronous; first-party subagents with working Write tools have already written their per-finding files by this point), drain any third-party background dispatches and finalize the round:
scripts/await-round.sh --round-dir "$REVIEW_OUTPUT_DIR"
await-round is no-op-safe — first-party-only rounds still call it; it returns immediately after reading the manifest. It writes a small $REVIEW_OUTPUT_DIR/.round-complete.json summary and (for third-party dispatches OR any entry that produced no per-finding files but has a .dispatch/<TAG>.raw capture) materializes per-finding files via third-party-finding-splitter.sh. It does NOT echo captured subagent payloads (CD-1 #4 output-bound contract).
Then read $REVIEW_OUTPUT_DIR/.round-complete.json and the per-finding files as needed for apply-fix. The raw per-reviewer prompt content (assembled by dispatch-agent into PROMPT_FILE) never enters the orchestrator's context — only the small spec lines + the small DISPATCH_FILE references passed to Task.
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*.md files + the orchestrator-authored defect summary scoped to either dimension collation owns: extraction fidelity OR Cross-References authoring. The subagent re-extracts ## Summary blocks 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*.md directly per the Per-Researcher template.
- Then re-dispatch the collation subagent to rebuild
summary.md from 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/):
- At least one
.score.md sidecar file exists in the round directory AND every sidecar evaluates to no kept-blocker findings per the verifier's scoring rubric (see agents/qrspi-finding-verifier.md and skills/implementer-protocol/SKILL.md). A zero-byte sidecar does not constitute verifier affirmation and the gate does NOT pass. Full sidecar schema validation is the verifier's contract (see agents/qrspi-finding-verifier.md); this skill assumes well-formed sidecars. OR
- A
round-NN-verifier-disabled.md marker file is present in the round directory AND the marker conforms to the canonical schema defined in skills/implement/SKILL.md HARD-GATE (a marker failing schema validation, or whose round identifier does not match the current round, is treated as absent). OR
config.md carries verifier_enabled: false. When this condition satisfies the gate, the orchestrator MUST append an audit-log entry before writing status: approved — recording: timestamp, run slug, step name (research), and branch label (auto-approve-verifier-disabled-config). The audit entry is written to the cascade audit log if one exists, otherwise to the round directory. An attempt to auto-approve via verifier_enabled: false without successfully writing this audit entry MUST abort with a named diagnostic (fail-loud, matching the audit-write precondition philosophy in skills/implement/SKILL.md HARD-GATE). This path is a deliberate operator-level configuration, not a default; the round appears in the review log as verifier-disabled, not as a normal clean round.
When none of these hold (no sidecars with affirmative zero-kept-findings content, no valid schema-conforming marker for the current round, and verifier_enabled is absent or true), the gate does NOT fire; the review round surfaces to the user as unverified and the standard human-approval gate runs.
Post-fix round behavior. If a fix round still produces kept findings, the auto-approve branch does NOT fire. The orchestrator surfaces the remaining kept findings to the user. The branch fires only when the most recent review round — initial or post-fix — produces verifier-affirmed zero kept findings.
Specialist dispatch cap. When pipeline: quick, the count of dispatched research specialists is capped at the question_budget value from config.md. If questions.md contains more questions than question_budget, the orchestrator dispatches at most question_budget specialists (grouping lower-priority questions into existing dispatches rather than adding new ones). This cap honors the quick-fix scope-tightening contract: the question_budget field is written to config.md by the Goals skill at run creation when pipeline: quick (default value: 5). Subject to the schema-level ≤50 ceiling enforced by Goals (see the Goals skill's question_budget field validation); Research treats any value outside the validated range as a precondition violation and halts with a named diagnostic. The question_budget value is read once at run start; subsequent edits to goals.md are not re-applied to the in-flight research cap. If question_budget is absent from config.md when Research dispatches in pipeline: quick, the dispatch halts with a named diagnostic; the cap is REQUIRED for quick-fix Research — no silent default to an arbitrary value.
Full pipeline unchanged. When pipeline: full, the human-approval gate runs as before and no specialist dispatch cap applies — the branch is inert and the user must explicitly approve.
Terminal State
If the artifact directory is inside a git repository, commit the approved research/summary.md, all research/q*.md files, and the reviews/research/ directory (per-round per-reviewer files; see using-qrspi → "Commit after approval (when applicable)").
Compaction checkpoint: pre-handoff. Research approved; the next skill (typically Design) reads research/summary.md + every prior approved artifact + reviewer findings on a fresh context. See using-qrspi ## Compaction Checkpoints for the iron-rule contract.
Call TaskCreate({ subject: "Recommend /compact (pre-handoff) — research", description: "pre-handoff: next skill reads research/summary.md + prior artifacts + reviewer findings. User decides whether to /compact." }).
REQUIRED: Invoke the next skill in the config.md route after research.
Red Flags — STOP
- A research finding contains opinions ("X is better than Y", "you should use Z")
- A finding states recommendations instead of facts ("the best approach is...")
- Codebase references are vague ("somewhere in the auth module") instead of specific (
auth/middleware.ts:45-67)
- Web sources are uncited (no URLs)
- A finding answers a question that wasn't asked (scope creep from the researcher)
- The collation step paraphrases or editorializes the verbatim
## Summary blocks, or adds Cross-References that re-narrate findings rather than naming connections
- goals.md content appears in any subagent prompt
Common Rationalizations — STOP
| Rationalization | Reality |
|---|
| "The researcher needs goals for context" | No. Research isolation prevents confirmation bias. The questions provide all the context needed. |
| "This opinion is well-supported" | Opinions are for Design, not Research. Report the facts and let Design interpret. |
| "Collation can lightly rephrase for flow" | No. Collation is verbatim extraction of per-question ## Summary blocks. Any rephrasing is a contract violation; the only authored content is the short Cross-References section. |
| "One researcher can answer multiple questions" | Group related questions only. Over-consolidation reduces depth. |
| "The web research is thorough enough without URLs" | Uncited claims are unverifiable. Every web finding needs a source URL. |
Worked Example
Good research finding (objective, factual):
Q4: What Redis-based rate limiting algorithms exist?
Three common algorithms:
Fixed Window — Count requests in fixed time intervals. Simple but allows bursts at window boundaries. Used by GitHub API (https://docs.github.com/en/rest/rate-limit).
Sliding Window Log — Store timestamp of each request, count within sliding window. Precise but memory-intensive (O(n) per client). Described in https://blog.cloudflare.com/counting-things-a-lot-of-different-things/.
Token Bucket — Tokens added at fixed rate, consumed per request. Allows controlled bursts. Used by AWS API Gateway (https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-request-throttling.html). The rate-limiter-flexible npm package (https://github.com/animir/node-rate-limiter-flexible) implements this with Redis backend.
Bad research finding (opinionated):
Q4: Rate limiting algorithms
You should use the Token Bucket algorithm because it's the best approach for APIs. Fixed window is outdated and sliding window is too complex.
The bad example makes recommendations ("you should"), value judgments ("best", "outdated", "too complex"), and cites no sources.
Iron Laws — Final Reminder
The two override-critical rules for Research, restated at end:
-
Research isolation is structural — goals.md is NEVER passed to any research subagent. This includes the collation subagent. Subagent prompts contain only the assigned question(s) — or, for collation, only the per-question q*.md files — plus, on re-dispatch, an orchestrator-authored sanitized defect summary with goal/intent-bearing language stripped. Raw feedback/research-round-*.md files are NEVER passed to subagents. Goal leakage produces confirmation-bias-driven research that selects for the conclusion the goals already implied.
-
Facts only — no opinions, no recommendations, no value judgments. Codebase findings cite specific file:line references; web findings cite URLs. "Should", "best", "better than" are forbidden in research output — those interpretations belong in Design.
Behavioral directives D1-D4 apply — see using-qrspi/SKILL.md → "BEHAVIORAL-DIRECTIVES".