Research Scan
Type to search documentation.

Architecture overview

The real pipeline, stage by stage, and who owns each stage — the agent under a rubric, or the deterministic CLI.

A scan is twelve steps. Some are judgements made by a model under a written rubric; the rest are deterministic stages the CLI performs. Knowing which is which is most of understanding the system.

The research-scan pipeline, stage by stageTwelve steps in order: init (CLI), plan (agent), retrieve (CLI), screen (agent), expand (CLI), screen again on the expansion items (agent), coverage (CLI), an optional gap round, shortlist (CLI), rerank (agent), verify (CLI) and emit (CLI). Agent stages run under written rubrics; CLI stages are deterministic.init[C] CLIplan[A] agentretrieve[C] CLIscreen[A] 0–3expand[C] CLIscreen[A] expansioncoverage[C] CLIgap roundat most onceshortlist[C] CLIrerank[A] agentverify[C] every DOIemit[C] CLI[A] agent stage, under a written rubric[C] deterministic CLI stageconditional — runs only when coverage is thin
Every stage is idempotent and re-runnable: change an input file, re-run from the stage it affects. The gap round only adds — round one's papers and scores are never discarded.

The main repo renders the same chain in one line:

brief ─▶ [plan queries] ─▶ retrieve ─▶ [screen 0–3] ─▶ expand ─▶ [screen] ─▶ coverage
─▶ [gap round, if coverage is thin] ─▶ shortlist ─▶ [rerank] ─▶ verify ─▶ emit

Bracketed steps are the agent’s; the rest are CLI stages.

The stages

StageOwnerWhat it does
initCLICreates the run directory and brief.md, and prints the RunInfo the planning agent must respect. --profile belongs here and is recorded in the manifest.
planAgentWrites queries.json — 6–8 queries and 3–6 sub-criteria, derived from the brief’s purpose.
retrieveCLIQueries every routed source, then dedups, filters, caps and writes the screening batches.
screenAgentScores every candidate 0–3 against the sub-criteria.
expandCLIWalks the citation graph out from every paper screening kept.
screenAgentScores the expansion items the same way.
coverageCLICounts how well each sub-criterion is covered, so the gap round has something to aim at.
gap roundBothAt most once. The agent writes queries against the thinnest criteria; retrieve --round 2 and expand --round 2 add what they find.
shortlistCLIChecks that every candidate was screened, then orders and cuts for the reranker.
rerankAgentScores the shortlist per criterion and overall, and writes the argument for each paper.
verifyCLIChecks every ranked paper against the live record and records what did not match.
emitCLIApplies the selection rules and renders the deliverable.

There is no screen or rerank command in the CLI. Those stages belong to the agent, and the CLI has no faculty for performing them.

Every stage is idempotent and re-runnable: change queries.json or pass a flag and re-run from the stage it affects.

Sources and routing

SourceUsed forStatus
OpenAlexPrimary search, metadata, retraction flag, graph fallbackImplemented. Requires a key.
Semantic ScholarSearch, and citation-graph expansion over references, citations and recommendationsImplemented. Key optional, throttled without one.
arXivExtra source for cs, and for general when a query is a method queryImplemented.
PubMedRouted for biomedRouted but not built yet. A biomed scan runs on OpenAlex + Semantic Scholar and records unavailable: true in manifest.json.
CrossrefDOI verification only; never a retrieval sourceImplemented. Verification degrades to OpenAlex if it is unreachable.

--domain picks the routing: behavioral and general use OpenAlex and Semantic Scholar; cs adds arXiv; biomed adds PubMed. --sources overrides the map outright.

That PubMed row is worth reading twice. A routed source that is not built is recorded as unavailable in the manifest rather than dropped quietly — the same rule that governs every other kind of loss in the pipeline.

The gap round

The gap round runs at most once, and on standard only when coverage reports that criteria are unevenly covered or that a query came back nearly empty. Otherwise it is skipped and the report says so. On quick it never runs; on deep it always does.

The mechanism: coverage counts, per sub-criterion, how many papers screening kept. The agent writes one or two queries against the criteria that came back thinnest. retrieve --round 2 and expand --round 2 add what they find.

A second round adds; it never subtracts. Round one’s papers and their scores are never discarded — the pool only grows. Coverage takes one snapshot per round, so what the gap round recovered is visible as a delta.

What bounds a run

Two dials, both recorded in the manifest.

Purposebuild, research or orient — decides which sub-criteria the plan derives, what screening counts as relevant, and what why_it_matters has to argue.

Profilequick, standard or deep — sets per-query depth, the pool cap, the out-of-window total and whether the gap round runs.

ProfilePer queryPool capOut-of-window totalGap round
quick2025012never
standard (default)4045020when coverage is uneven
deep40450 × sources ÷ 230always

An explicit flag still overrides the profile: --per-query 60 means 60 whatever the profile says. The out-of-window total is a budget for the whole run, not a per-stage allowance — caps are totals, not allowances per stage.

Where the time goes

Wall clock tracks the candidate pool, because screening is the long pole — it is the agent reading the pool.

PoolScreening batchesWall clock
~675 candidates — cs, 3 sources at the default cap~2737 min
~250 candidates — 2 sources at the default cap~1022 min
~120 candidates — --max-candidates 120519 min

OpenAlex bills roughly $0.008 either way; pool size costs almost nothing in API money. Agent tokens are the real cost, and they do not fall as fast as the pool does: the 120-candidate run still cost about $5.70 in frontier-model tokens and ran only three minutes quicker than the 250-candidate one, because reranking and fixed stage overhead dominate once screening is small.

--max-candidates is the right dial for a runaway cs pool, not a way to make a scan cheap.

Read on

Last updated Aug 21, 2026