| name | generate-hypothesis |
| description | Proposer stage — produce ONE question worth answering plus the decisive test that would falsify it. Generate 5 mutually-distinct candidates grounded in the provided literature corpus and data description, each with surprise/risk/parent + nearest-priors, feasibility probes, and adversarial kill-tests, then pick the winner and write its pre-registered research arc to hypothesis.md. Topic-agnostic: the domain is set by the run-context topic brief. Carries the inputs, method, output contract, and constraints. |
generate-hypothesis
The Proposer stage. Turn the field + the available data into ONE question worth answering — a hypothesis PLUS the decisive test that would falsify it (estimand or comparison, identifying variation, key variables). Taste lives here. You GENERATE the question + write hypothesis.md; there is no self-review step in this isolated build — do NOT score or accept your own output. All outputs live under <session>/workspace/stage-frame/hypothesis/ (use the absolute session root from the run context).
The topic is NOT hardcoded — read it from the topic brief named in the run context. It tells you the field, what counts as a contribution there, the unit of analysis, and the methodological conventions. Everything below is domain-agnostic scaffolding; the brief + the corpus + the data description supply the substance.
Inputs (read-only — all named in the run context; closed corpus, no web)
- The topic brief — the field, the contribution bar, conventions, and any user seed / directions-to-avoid.
- The papers corpus dir — the closed literature the
literature-specialist reads. Have it write the session working synthesis to workspace/stage-frame/literature/review.md (with a ## Sources heading listing every [slug] it cites).
- The data description dir — metadata + data structure (schema, codebook, variable list, coverage) only; NO data bytes. The
data-specialist reads it and writes workspace/stage-frame/data/review.md with a ## Sources heading and a numbered [data:<id>] variable/field inventory (its §4).
- The prior-run idea memory —
<sessions>/papers/novelty/*.idea.md, the research questions of prior runs of THIS proposer (cross-run novelty; may be empty on a first run). A candidate must not silently re-run a question a prior run already explored — frame an explicit, distinguishable extension or pick a different arc.
- (if present in the run context) a user research-question seed and/or directions to AVOID. Seed handling: when a seed is given, all 5 candidates must be distinct OPERATIONALIZATIONS of that question (different identification × outcome × sample); set
parent=user:<slug>.
- (if you arrived on a return edge — not used in this isolated build) ignore.
Orchestration (depth-1 sub-agents via Task)
literature-specialist — situate the field + find the nearest untested prior (grounds every candidate so generation does not collapse onto 2-3 anchor papers) AND sweep the prior-run idea records so no candidate re-runs an already-explored question.
data-specialist — what the records can NEWLY support, read from the data description (the cheap variable/coverage pre-filter; do NOT claim a full feasibility verdict — there is no data-bytes access here).
hypothesis-generator — the per-lens candidate producers.
If Task is unavailable, run the parts as strictly-separated sequential passes and write DEGRADED: no-subagents at the top of selection.md.
PART A — Candidates → workspace/stage-frame/hypothesis/candidates.md
- 5 GENERATOR sub-agents — spawn the
hypothesis-generator agent five times in PARALLEL (subagent_type=hypothesis-generator), one per lens: mechanism / identification / data-first / contrarian / open (seed-anchored when a seed is given). Each is FED the literature-specialist's synthesis + workspace/stage-frame/literature/review.md (solo re-derivation collapses onto 2-3 anchor papers) and returns 1-2 candidates as JSON {rq, surprise{level 0-3, reasoning naming the specific prior it contradicts}, risk{epistemic, computational, scientific_integrity}, parent, cited_fields[], nearest_priors[]}.
nearest_priors: the 3 closest papers, each {slug, found, untested}; every slug MUST appear in review.md ## Sources (otherwise it is treated as fabricated).
- Merge into exactly 5 mutually-distinct candidates. Mutual distinctness is MECHANICAL: ≥3 axes differ among mechanism / outcome / identification / sample / treatment — not wording variants. Every cited field/variable must exist in
workspace/stage-frame/data/review.md §4 (a ghost variable is a defect). Quantitative magnitudes cite the PRIMARY [slug]; absence claims say "no prior found in our corpus pass".
Candidate quality bar
- Anchored in the topic brief's unit of analysis; framed in the field's terms; non-trivially novel against the corpus pass.
- Each candidate carries
surprise (expected information gain, naming the specific prior it contradicts), risk, parent, and nearest_priors (3 × {slug, found, untested}; slugs must exist in review.md ## Sources).
PART A2 — Feasibility probes
A lightweight read-only data-specialist probe per candidate (variable existence + coverage/structure red-lines ONLY, from the data description). Returns {candidate_id, feasible, blockers[]} → record in candidates.md. (This is a cheap pre-filter so a dead-on-arrival candidate never wins; a full feasibility grade would need data access this isolated stage does not have.)
PART A3 — Kill-tests (per candidate, read-only)
One read-only adversarial probe per candidate. Pose the candidate as a falsifiable claim; answer from the corpus AND the prior-run idea records: strongest evidence FOR it being already answered / AGAINST / what remains unsettled → {candidate_id, already_answered: yes|partial|no, evidence[]} → record in candidates.md. A candidate a prior run already explored counts as already_answered: yes exactly as a corpus hit does (cite the [<stem>]). already_answered: yes ⇒ regenerate from the same lens avoiding that territory BEFORE you pick. The FOR/AGAINST/unsettled text doubles as the winner's novelty-justification draft.
PART B — Pick the winner + write hypothesis.md
You select the winner from the kill-tested candidates (most surprising × identifiable × feasible × novel). Record the choice + rationale in workspace/stage-frame/hypothesis/selection.md (+ any DEGRADED: note) and a machine-readable workspace/stage-frame/hypothesis/score.json = {"winner":"<candidate_id>","scores":{"surprise":N,"identifiability":N,"feasibility":N,"novelty":N},"rationale":"..."}. Then write the winner to workspace/stage-frame/hypothesis/hypothesis.md:
- First content line is
# <one-line research question> (a leading YAML frontmatter block is allowed — the gate strips it — but nothing else may precede the title).
- Carries the full surprise/risk/parent contract and the decisive test (the estimand or comparison, the identifying variation, the key variables — what result would falsify it).
- MUST contain a
## Research arc section: the FIRST-ORDER headline question, the SECOND-ORDER extensions (extend / defend / boundary), and a ROBUSTNESS-SKETCH (≥6 perturbations the locked model must survive — e.g. leave-one-group-out / alt outcome / alt clustering / placebo timing / window shift / alt fixed effects / winsorization). Each arc question names the gap or prior [key] it engages.
Gate-checked outputs (the postcondition greps these — exact paths)
workspace/stage-frame/hypothesis/hypothesis.md — opens with # <one-line research question> as its first content line, carries ## Research arc.
workspace/stage-frame/hypothesis/candidates.md — the 5 candidates + feasibility-probe + kill-test results.
workspace/stage-frame/hypothesis/selection.md + score.json — why the winner won.
Constraints
- Numbers are MEASURED or CITED, never invented — every numeric magnitude cites
[slug]/[data:<id>] on the same line; absence claims say "no prior found".
- Do NOT self-review or write any accept/reject verdict — this isolated stage stops at a written hypothesis.md.
- Write ONLY under the session's
workspace/stage-frame/hypothesis/ (+ the literature/ and data/ review files); never write outside the session, and never write into the read-only inputs.