| name | auto |
| description | Autonomous pipeline: claim → experiment (mechanism routing folded in) → verify → iteration. Each stage is delegated to an isolated agent with its own context window and configurable model. Gates are AUTO_PROCEED-governed; defaults run end-to-end without human input. Use when user says "auto pipeline", or wants the core stages chained without confirmation. |
| argument-hint | ["research-direction (optional; falls back to task.md when omitted)"] |
| allowed-tools | Bash(*), Read, Write, Edit, Grep, Glob, WebSearch, WebFetch, Agent, AskUserQuestion, Skill, CronCreate, CronList, mcp__llm-chat__chat |
Auto Pipeline: Idea → Experiments → Verify → Review
End-to-end autonomous run for: $ARGUMENTS (when provided) or the project's task.md (when $ARGUMENTS is empty).
Each stage runs in a dedicated agent (isolated context + configurable model). The orchestrator only sees each agent's final summary and the files on disk — it uses those to fire the gates between stages.
⏰ First action — register the hourly notification timer. Before any pipeline work, if task.md opts into notifications (see the Notifications rule in Key Rules), register a recurring scheduled task with CronCreate (cron <off-minute> * * * * — one call per hour, pick a minute ≠ 0/30; prompt: /notify hourly). This fires the hourly briefing on a real wall-clock timer instead of relying on the orchestrator remembering to poll during long waits. The timer is the sole source of the hourly cadence — the orchestrator never separately polls or manually fires /notify hourly. Register it once, up front — first CronList to check an equivalent /notify hourly job is not already scheduled (e.g. on resume), and skip if so. When task.md does not opt into notifications, skip this entirely (no timer). The timer covers only the hourly cadence; the event-driven /notify touchpoints (progress / done / halted / approval-needed) are separate and stay orchestrator-initiated (see Key Rules).
Direction source resolution:
$ARGUMENTS non-empty → use it as the research direction; if task.md also exists, the claim agent treats task.md as authoritative detailed context (current behavior).
$ARGUMENTS empty + task.md exists → use task.md as the sole direction source; pass direction: "" to the claim agent so it relies entirely on task.md.
$ARGUMENTS empty + task.md absent → stop and report [direction] no input — provide $ARGUMENTS or create task.md. Do not invent a direction.
Defaults
| Flag | Default | Effect |
|---|
AUTO_PROCEED | true | When true, gates skip the UI prompt entirely and directly pick the recommended option. When false, the orchestrator calls AskUserQuestion and waits for the user. |
RESUME | false | When true, the orchestrator skips any stage whose final artifacts already exist non-empty on disk, and forwards resume: true to each invoked agent so sub-skills can do phase-level skipping too. Useful for picking up after a crash, or for re-running only the missing stages. Default false = always run every stage from scratch (and overwrite previous artifacts). |
REVIEW_LOOP | true | Run iteration after verify; set false to stop at verify. |
MODEL | none | Global model family alias applied to every stage whose <STAGE>_MODEL is unset. Accepts opus / sonnet / haiku (case-insensitive). Per-stage <STAGE>_MODEL always wins over this. When both MODEL and <STAGE>_MODEL are unset, the stage falls back to its agent frontmatter default. |
<STAGE>_MODEL | none | Per-stage model family override for any of CLAIM, EXPERIMENT, VERIFY, ITERATION. Accepts only family aliases: opus, sonnet, haiku (case-insensitive). When unset, falls back to the global MODEL flag (if set), then to the agent frontmatter default (currently claim/experiment/iteration = claude-opus-4-7, verify = claude-sonnet-4-6). Version pinning is managed only in agents/<name>.md frontmatter — to change the pinned version, edit the agent file directly (one source of truth, fully tracked in git). Pinned IDs are not accepted on the CLI because the Agent tool's model parameter schema is restricted to family aliases; passing a pinned ID at the CLI level would fail schema validation. |
DIMENSIONS | model | Verify swap axes — and therefore the variant count per picked claim, since verify runs exactly one swap per listed axis. List or comma-separated subset of {method, dataset, model}. Default model → 1 variant/picked-claim (fast, single-axis model swap). Broaden with dimensions: method,dataset,model → 3 variants/picked-claim (full stress test). Forwarded to verify agent. |
TARGET_CLAIMS | all | Which claims verify stress-tests: all (default; covers both main-experiment-supported and main-experiment-rejected claims so robustness is checked in both directions) / passed (only main-experiment-supported = claim_supported = pass) / failed (only main-experiment-rejected = claim_supported = fail) / a specific claim id. Note passed ∪ failed = all. Forwarded to verify agent. |
MAX_VERIFY_CLAIMS | 1 | Cap on how many Stage-1-admitted claims proceed into Stage 2 (swap variants). Stage 1 (main-experiment integrity audit) always audits every target claim regardless — the cap only gates Stage 2 entry. When the admitted pool exceeds the cap, /auto-verify's Phase 3 step 0 picks the top-K by importance judgment (reading each admitted claim's statement against upstream narrative like IDEA_REPORT.md / ## Rationale; row order is NOT a priority signal). Un-picked admitted claims are marked INTEGRITY_ONLY with stage2_skip_reason: max_verify_claims_cap in VERIFY_REPORT.md; user can swap-test them later via /auto-verify <id> — resume: true (Stage 1 audit is reused via RESUME). Forwarded to verify agent. At the default cap of 1 with default DIMENSIONS=model, verify launches 1 × 1 = 1 variant run per /auto pass. |
ROBUSTNESS_THRESHOLD | 0.5 | A claim PASSes verify iff robustness ≥ ROBUSTNESS_THRESHOLD. Set higher (e.g., 0.67) for stricter publication-ready verification, lower (0.33) for exploratory work. Forwarded to verify agent. |
MIN_VARIANTS_FOR_VERDICT | 1 | Minimum number of integrity-clean variants required to issue a PASS / FAIL verdict on a claim. Default 1 means a single eligible variant still yields a verdict; N_eligible < MIN_VARIANTS_FOR_VERDICT triggers ZERO_ELIGIBLE_VARIANTS (a distinct terminal state from INCONCLUSIVE — see auto-verify/SKILL.md Phase 10). Raise to 2/3 for stricter projects where you want multiple independent axes to agree before issuing PASS / FAIL. Forwarded to verify agent. |
BASE_REPO | none | GitHub repo URL to clone before implementing. |
RESEARCH_DOMAIN | auto | Project domain tag (e.g. mechanistic-interpretability, vision-encoders, rl-policy-eval). Used to gate mechanism-family routing and downstream auxiliary decisions. Default auto: orchestrator forwards auto to the experiment agent and the sub-skill infers from FINAL_PROPOSAL.md; if inference is ambiguous, silently falls back to general regardless of AUTO_PROCEED (no UI prompt). To force a specific domain, set this flag explicitly on the CLI. |
COMPACT | false | Generate compact summary artifacts: idea-stage/IDEA_CANDIDATES.md after claim, refine-logs/EXPERIMENT_LOG.md after experiment, skip per-claim verify/<claim_dir>/ROBUSTNESS.md (where <claim_dir> = <claim_id>_<short_claim> on disk; see skills/auto-verify/SKILL.md "Directory Layout"). Forwarded to claim / experiment / verify agents. |
CODE_REVIEW | true | External LLM reviewer checks experiment + verify-variant code before deployment. Set false to skip. Forwarded to experiment + verify agents. |
SANITY_FIRST | true | Run the smallest/cheapest run first to catch setup bugs before launching the full suite. Forwarded to experiment + verify agents. |
AUTO_DEPLOY | true | Auto-deploy after implementation + review; set false to pause for manual inspection. Forwarded to experiment + verify agents. |
MAX_PARALLEL_RUNS | 4 | Max concurrent /run-experiment calls dispatched within the experiment / verify stages. Becomes max_parallel: in the /experiment-queue manifest when Phase 4 of experiment routes to the queue path. Forwarded to experiment + verify agents. |
BATCH_DISPATCH | auto | Phase 4 dispatch routing rule (experiment stage). auto (default) lets /auto-experiment auto-pick /run-experiment for small milestones (≤ 5 ad-hoc runs) and /experiment-queue for large or dependency-laden ones (≥ 10 runs, depends_on, grid expansions, ≥ 3-seed × ≥ 3-config sweeps). queue forces every milestone to /experiment-queue; direct forces every milestone to /run-experiment (debug-only — emits a warning if it overrides the queue rule). Forwarded to experiment agent. |
REF_PAPER | false | Reference paper for the claim stage to summarize first. Accepts a local PDF path, an arXiv abs URL, or any paper URL. When set, claim Phase 0.5 writes idea-stage/REF_PAPER_SUMMARY.md and downstream idea generation builds on it. Forwarded to claim agent as ref_paper:. |
BEHAVIOR_SOURCE | given | Behavior stage — controls where the behavior to study comes from and whether it is validated. Three values: given (default): the behavior is already specified in the direction / task.md and assumed to hold — no ideation, no novelty check, no M0 validation; the claim stage faithfully captures it and goes straight to the mechanism. Precondition enforced by the Given-Behavior Comprehension Gate (see the claim stage): task.md must name a concrete behavior; if it names only a topic, the orchestrator asks the user to specify a behavior or switch to discovery (this gate always waits — even in full-auto). given-validation: the behavior is given the same way (faithfully captured from task.md, no mining, no ideation, no novelty) but its existence is validated first — the experiment plan opens with a hard M0 phenomenon-validation gate that the experiment stage runs before any mechanism compute. discovery: the behavior itself is mined — load /mechanism-behavior-discovery to sharpen a new candidate phenomenon and run the full ideation pipeline (research-lit → idea-creator → novelty-check → impact-check → research-review → research-refine-pipeline; final idea chosen by impact-first, novelty-second), then the experiment plan opens with the M0 gate too. The M0 four-state verdict (established/conditional proceed; not-established ends the pipeline with a negative-result report skipping verify+iteration; inconclusive re-runs M0) applies to both given-validation and discovery. Accepts given / given-validation / discovery (case-insensitive). Forwarded to the claim agent as behavior_source:. |
MECHANISM | discovery | Mechanism stage — controls who picks the mechanism method. discovery (default): the system selects the mechanism family — the experiment stage runs /mechanism-skills routing (route_only → auto-select the recommended candidate, or the family mini-prompt when AUTO_PROCEED=false) and the claim stage loads /mechanism-explore to shape the hypothesis direction + experiment plan. given: the user has specified the mechanism method/family in task.md — the claim stage captures it and the experiment stage commits it directly (Phase 1.5 Mode B, CHOSEN_FAMILY=<the method>), bypassing routing and the family mini-prompt; task.md must name a concrete mechanism method/family, else the claim stage halts (report it back). Accepts given / discovery (case-insensitive). Forwarded to the claim agent as mechanism:. Resource fidelity: the claim stage stamps resource_fidelity: strict into FINAL_PROPOSAL.md + EXPERIMENT_PLAN.md iff BEHAVIOR_SOURCE=given AND MECHANISM=given (the reproduction combination) — the experiment stage then enforces the Resource-Fidelity Harness on the main experiment (exact models/datasets at full scale — no smaller-model swap, no data subsetting, no skipped must-run runs; OOM handled by batch↓/grad-accum/sharding/offload, HALT rather than downscale; verify is exempt, its swaps are intentional). Every other combination leaves the marker unstamped, so model/dataset choice is unconstrained (cost-aware). |
MAX_ITERATIONS | 6 | Iteration agent: max total back-edge actions (variant fix / plan+script fix / claim-stage re-entry) before stopping. Counts every back-edge uniformly; new claims produced by claim-stage re-entry inherit the same budget (no fresh allocation). Forwarded to iteration agent. Legacy alias MAX_ROUNDS is accepted at the CLI for one release and silently normalized to MAX_ITERATIONS. |
MAX_CLAIM_REENTRIES | 2 | Iteration agent: sub-budget within MAX_ITERATIONS for claim-stage re-entries (action type ③ — see auto-iteration-loop/SKILL.md). Prevents the failure mode where the reviewer keeps requesting claim rewrites without anyone fixing the experiments behind them. When exhausted, the iteration loop refuses further ③ actions even if iterations remain. Forwarded to iteration agent. |
TARGET_SCORE | 6 | Iteration agent: stop early when score ≥ this AND verdict is ready/almost AND no FAIL / INCONCLUSIVE / ZERO_ELIGIBLE_VARIANTS claim remains (three-dimensional STOP rule). |
GPU_ID | auto | GPU device(s) to pin every experiment / verify-variant / iteration-round run to. auto lets each sub-skill inherit from the environment / launcher (no pinning). A single id (0) or comma-list (4,5,6,7) causes the sub-skill to pass CUDA_VISIBLE_DEVICES=<GPU_ID> as the first positional argument to /run-experiment (and sanity runs); /run-experiment is a Skill, not a shell command, so it cannot accept a shell prefix — instead it parses this positional arg and exports CUDA_VISIBLE_DEVICES itself before launching the experiment subprocess. Forwarded to experiment + verify + iteration agents (iteration's Phase-C /run-experiment dispatches inherit the same pin). |
OOM_MAX_GPUS | 4 | resource_fidelity: strict only (the BEHAVIOR_SOURCE=given + MECHANISM=given reproduction combo). Upper bound on how many free GPUs the Resource-Fidelity Harness may auto-add to a single OOM'd run before halting. On OOM the harness queries free GPUs (memory.used < 500 MiB), auto-adds them to the run and enables sharding — auto-converting a single-GPU script (model.cuda() / .to(device)) to device_map="auto" / FSDP / CPU-or-disk offload, first verified numerically equivalent on a fit-on-one-GPU proxy slice — repeating up to this cap while leaving MAX_PARALLEL_RUNS headroom for sibling runs. Only after the cap is hit (or no free GPU remains) and CPU/disk offload is exhausted does it HALT — and never by downscaling (smaller model / subset data stay forbidden, even in full-auto). Set higher to let one big run spread across more devices; auto lets it use all currently-free GPUs. Forwarded to experiment + verify + iteration agents. Ignored when the strict marker is absent (every non-reproduction combination). |
UNDERPOWER | tag | Active whenever the strict marker is absent (ignored under resource_fidelity: strict — the reproduction combo — which already forbids subsetting). Guards against an under-powered cheap run's weak/negative verdict being mistaken for a real negative. After the experiment, a claim whose main-experiment verdict is weak (not-supported / partial / null) and whose realized scale is materially below the plan (used_n shortfall, fewer seeds, or fewer grid/checkpoint points than EXPERIMENT_PLAN.md) is flagged suspected under-power. tag (default): tag the claim [suspected under-power: used_n X/Y, seeds A/B, grid P/Q] (recorded as a provisional caveat carried into the ledger + verify + iteration, so the negative is treated as provisional, not a confirmed falsification) and proceed — respecting the cost-aware design; under AUTO_PROCEED=false the experiment agent instead asks (full re-run / targeted-milestone re-run / accept demo-scale). stop: treat a suspected-under-power claim as a Round-End Decision (ended-needs-decision (experiment: suspected-under-power)) even under AUTO_PROCEED=true, so you decide whether to re-run at full scale before verify trusts it. off: disable the check. Forwarded to experiment agent. |
LEDGER_FIGURES | auto | Whether the final ledger render should call /paper-figure to produce per-claim plots and tables embedded into CLAIMS_LEDGER.md (image figures = PNG inline + PDF link; tables = Markdown inline + .tex link). Tri-state: auto (default) generates figures only for claims with at least one plottable or tabulable data source; true forces an attempt for every non-deferred claim (an unsupported claim still degrades to a skipped entry, never a halt); false disables the hook entirely and the ledger renders without a Figures bullet. Fires once per pipeline run, at the final ledger hook only (iteration:final, or verify when REVIEW_LOOP=false); intermediate hooks never re-invoke /paper-figure. See Ledger Figures hook. |
Override on the CLI, e.g. /auto "direction" — auto-proceed: false, claim-model: opus, verify-model: sonnet, dimensions: method,dataset, code-review: false. Canonical CLI form is hyphen-separated (auto-proceed, claim-model, code-review, ...). The parser also accepts underscore (auto_proceed) and uppercase env-style (AUTO_PROCEED) for compatibility — see "Argument parsing" below.
Pass each flag to the agent(s) listed in its "Forwarded to" hint as the corresponding arg in agents/<name>.md's Invocation contract (e.g., COMPACT → compact:, CODE_REVIEW → code_review:).
Argument parsing
$ARGUMENTS is shaped as "<direction>" — <key>: <value>, <key>: <value>, .... Both the direction and the option list are independently optional. The orchestrator parses it as follows:
- Direction (positional, optional) — everything before the first
— (em dash with spaces) or -- is the research direction; pass it to the claim agent as direction:. When omitted (empty $ARGUMENTS, or $ARGUMENTS starts directly with — / --), pass direction: "" and rely on task.md (see "Direction source resolution" above). If both are absent, stop and report rather than inventing a direction.
- Options (after
— or --) — comma-separated key: value pairs. Whitespace around : and , is ignored. Quoted strings keep their content.
- Key normalization — lower-case, hyphens → underscores, then upper-case for the env-style flag (
auto-proceed / auto_proceed → AUTO_PROCEED; verify-model → VERIFY_MODEL).
- Model values — for both
MODEL (global) and <STAGE>_MODEL (per-stage), the value must be one of the family aliases opus / sonnet / haiku (case-insensitive; lowercase before passing through). Anything else is a parse error — log [arg-parse] <key>: "<value>" is not a family alias (opus|sonnet|haiku) — version pinning is managed in agents/<name>.md frontmatter, not on the CLI and stop. To change the pinned version of a stage, edit that agent file directly.
- Bool values —
true / false / 1 / 0 / yes / no. Anything else is a parse error.
- Enum string values — documented set or fall back to default with a log line:
BATCH_DISPATCH accepts auto / queue / direct (case-insensitive; lowercase before passing through). On unknown value: log [arg-parse] BATCH_DISPATCH: "<value>" not in {auto, queue, direct} — falling back to default 'auto' and continue with the default.
BEHAVIOR_SOURCE accepts given / given-validation / discovery (case-insensitive; lowercase before passing through). On unknown value: log [arg-parse] BEHAVIOR_SOURCE: "<value>" not in {given, given-validation, discovery} — falling back to default 'given' and continue with the default.
MECHANISM accepts given / discovery (case-insensitive; lowercase before passing through). On unknown value: log [arg-parse] MECHANISM: "<value>" not in {given, discovery} — falling back to default 'discovery' and continue with the default.
- Other future enum flags follow the same pattern.
- Unknown keys — log
[arg-parse] unknown key: <name> — ignoring and continue. The legacy MODE flag is removed: a passed mode: is an unknown key (ignored with the log line) — use behavior-source + mechanism instead (old mode: reproduction ≡ behavior-source: given, mechanism: given; old mode: discovery is the default and implied by any non-reproduction combination).
All three of these invocations resolve to the same flags. The first (hyphen-separated) is the canonical form; the other two are accepted for compatibility — prefer the canonical form when writing new docs / examples / scripts.
# Canonical: hyphen-separated, lowercase
/auto "direction" — auto-proceed: false, claim-model: opus, verify-model: sonnet, dimensions: method,dataset
# Also accepted: underscore form
/auto "direction" -- auto_proceed: false, claim_model: opus, verify_model: sonnet, dimensions: method,dataset
# Also accepted: uppercase env-style
/auto "direction" — AUTO_PROCEED: false, CLAIM_MODEL: opus, VERIFY_MODEL: sonnet, DIMENSIONS: method,dataset
Retrieval rule
The claim agent's Phase 1 research-lit step must follow the requirements in skills/research-lit/SKILL.md, and mechanic-db-search must be executed, not skipable.
In other subagents, they may freely choose any retrieval source — WebSearch, WebFetch, MCP tools, or their own bundled scripts/ helpers. The orchestrator does not constrain the tool.
Overview
/auto (orchestrator)
│
├─ claim [claude-opus-4-7] → idea-stage/IDEA_REPORT.md
│ │ refine-logs/FINAL_PROPOSAL.md
│ │ refine-logs/EXPERIMENT_PLAN.md
│ │ (internal chain depends on BEHAVIOR_SOURCE (behavior origin + ideation + M0) and
│ │ MECHANISM (routing vs. user-given family; when MECHANISM=discovery the chain loads
│ │ /mechanism-explore to shape the hypothesis direction + experiment plan):
│ │ given — [Given-Behavior Comprehension Gate: concrete behavior in task.md? no → ask user: switch-to-discovery (default) | specify-behavior]
│ │ research-lit → faithful behavior capture → [MECHANISM=discovery: /mechanism-explore] → research-refine-pipeline (no ideation, no novelty, NO M0)
│ │ given-validation — [Given-Behavior Comprehension Gate] research-lit → faithful behavior capture → [MECHANISM=discovery: /mechanism-explore] → research-refine-pipeline (plan opens with M0 gate; no ideation, no novelty)
│ │ discovery — research-lit → /mechanism-behavior-discovery → idea-creator → novelty-check → impact-check → research-review → [MECHANISM=discovery: /mechanism-explore] → research-refine-pipeline (plan opens with M0 gate)
│ │ resource_fidelity: strict is stamped iff BEHAVIOR_SOURCE=given AND MECHANISM=given (the reproduction combo).
│ │ MECHANISM=given captures the user's mechanism method from task.md → forwarded to experiment as CHOSEN_FAMILY.)
│ └─ Claim Gate
│
├─ experiment [claude-opus-4-7]
│ ├─ Phase 1.25 (BEHAVIOR_SOURCE ∈ {given-validation, discovery} — fires on the plan's M0 marker): Phenomenon-Validation Gate M0 — run M0 first → 4-state:
│ │ established/conditional → continue ; not-established → 🛑 end pipeline (negative report, skip verify+iteration) ; inconclusive → fix & re-run M0
│ ├─ (MECHANISM=given → skip Call A + mini-prompt; read chosen_mechanism: from EXPERIMENT_PLAN.md → Call B with CHOSEN_FAMILY=that value, or MECHANISM_ROUTING=not-applicable when it is behavioral-only)
│ ├─ Call A (mode=route_only) → refine-logs/MECHANISM_ROUTING.md (2–3 candidates) [MECHANISM=discovery only]
│ ├─ 🔸 Mini-prompt: pick mechanism family (AUTO_PROCEED=true → skip prompt, pick recommended) [MECHANISM=discovery only]
│ └─ Call B (mode=build, chosen_family=X)
│ → Phase 4 dispatch fork (per-milestone routing):
│ ├─ ≥10 runs / depends_on / grid → /experiment-queue (batch scheduler with OOM retry, stale cleanup, GPU gate, phase deps)
│ └─ else → /run-experiment per run (direct dispatch)
│ → refine-logs/EXPERIMENT_RESULTS.md, refine-logs/EXPERIMENT_TRACKER.md
│ → MECHANISM_ROUTING.md with committed: true
│ └─ Experiment Gate (inside agent)
│
├─ verify [claude-sonnet-4-6]
│ ├─ Phase 2 : per-claim main-experiment integrity audit (Stage 1 gate)
│ │ — /experiment-audit + /mechanism-audit on refine-logs/
│ ├─ Phase 3–7 : pick within-family method swap (+ data/model swaps), run variants
│ ├─ Phase 8 : /result-to-claim per variant → consistent_with_main_experiment
│ ├─ Phase 9 : per-claim variant integrity audit (symmetric to Phase 2)
│ ├─ Phase 10 : compute robustness → PASS / FAIL / ZERO_ELIGIBLE_VARIANTS
│ └─ write verify/VERIFY_REPORT.md, verify/INTEGRITY_AUDIT.md, verify/<claim_dir>/ROBUSTNESS.md
│ └─ Verify Gate (AUTO_PROCEED-governed, same as Claim/Experiment gates)
│
└─ iteration [claude-opus-4-7] → review-stage/AUTO_REVIEW.md, REVIEW_STATE.json,
(skipped when REVIEW_LOOP=false) REVIEWER_MEMORY.md, AUTO_ITERATION_FINAL_REPORT.md
Pipeline
Multi-round guard (run first)
Before anything else, protect a prior round's outputs from being silently overwritten. When RESUME=false (a fresh run) AND un-archived prior-round artifacts are present at the project root — test [ -s CLAIMS_LEDGER.md ] or [ -s refine-logs/FINAL_PROPOSAL.md ] existing outside any rounds/ folder — a previous round's outputs have not been archived, and starting fresh would overwrite them. Do not proceed silently. Halt with:
[multi-round] un-archived prior-round artifacts detected (CLAIMS_LEDGER.md / refine-logs/...).
A fresh /auto would overwrite them. Choose one:
• /next-round — archive this round into rounds/round_<N>/ and draft the next task.md (recommended)
• RESUME=true — continue the existing (unfinished) round instead of starting fresh
• delete the listed artifacts manually to intentionally overwrite
Aborting.
This guard is independent of AUTO_PROCEED — it is data-protective and the remedy is a single user action, so it halts rather than auto-overwriting even in full-auto mode. It is skipped when RESUME=true (continuing is the intent) and when no such artifacts exist (a true first round). See Global Exploration Memory and the /next-round skill.
Resume-mode setup (only when RESUME=true)
Skip entirely if RESUME=false (default). When true:
Before invoking each stage's agent, check whether the stage is already complete by testing the existence and non-emptiness ([ -s <path> ]) of its stage-completion artifacts:
| Stage | Stage-completion artifacts (all must exist non-empty to count as "done") |
|---|
| claim | idea-stage/IDEA_REPORT.md, refine-logs/FINAL_PROPOSAL.md, refine-logs/EXPERIMENT_PLAN.md. |
| experiment | refine-logs/MECHANISM_ROUTING.md (with committed: true), refine-logs/EXPERIMENT_RESULTS.md, refine-logs/EXPERIMENT_TRACKER.md. Exception (phenomenon-terminated): if EXPERIMENT_RESULTS.md carries phenomenon_status: not-established (or terminal inconclusive), the stage counts as done with no MECHANISM_ROUTING.md/EXPERIMENT_TRACKER.md required — the M0 gate ended the run before mechanism work (see the experiment resume branch's case 0). |
| verify | verify/VERIFY_REPORT.md AND verify/INTEGRITY_AUDIT.md — both required, AND INTEGRITY_AUDIT.md must contain a non-empty ## Variant integrity (Phase 9) section (a file with only the ## Main-experiment integrity (Phase 2, per-claim) section is an incomplete run and must re-execute Phase 9). When Phase 2 returned fail on every target claim and Phase 9 was legitimately short-circuited, the Phase 9 section still exists, populated as [skipped — all main-experiment audits FAIL]. Per-claim main-experiment audit JSON verify/<claim_dir>/main_experiment_audit/EXPERIMENT_AUDIT.json must exist non-empty for every target claim — glob-expand verify/<claim_id>_*/main_experiment_audit/ for each. ("at least one" is too weak: the [skipped — all main-experiment audits FAIL] short-circuit asserts all main experiments failed, so if any target claim has no main-experiment audit on disk the prior run was partial — re-run, don't skip.) Do not test the bare verify/main_experiment_audit/ (the legacy flat layout). |
| iteration | review-stage/AUTO_REVIEW.md, review-stage/REVIEW_STATE.json, review-stage/AUTO_ITERATION_FINAL_REPORT.md (written by the iteration agent's Termination once status=completed; absence with status=completed means the agent crashed before report assembly and the stage must be resumed) |
If every artifact for a stage is present non-empty, the orchestrator:
- Does not invoke that stage's agent.
- Logs
[resume] stage=<name> skipped — artifacts present: <comma-separated list>.
- Reads the stage's existing artifacts as if the agent had just produced them, fires the stage's Gate normally (which under
AUTO_PROCEED=true is a no-op), and moves to the next stage.
If any artifact is missing or empty, the stage's agent is invoked, with resume: true forwarded so the agent can still apply phase-level skipping inside the skill. This way a half-done stage finishes the remaining phases instead of restarting from scratch.
Log on entry:
[resume-mode] resume=true — completed stages: <claim,experiment,...>; will resume from: <first-incomplete-stage>
Note: RESUME=true never deletes or overwrites pre-existing artifacts on its own. If you want a clean run, either set RESUME=false (default) or remove the relevant files manually.
Resolve per-agent models
Resolution order (per stage, evaluated independently for claim / experiment / verify / iteration):
- If the user passed
<STAGE>_MODEL=<alias> on the CLI (opus / sonnet / haiku only), use it. Pass the alias as-is to the Agent tool's model parameter. The Agent tool then routes to the alias's current latest version.
- Else if the user passed the global
MODEL=<alias> on the CLI, use that alias for this stage.
- Else omit the
model parameter when calling the Agent tool — the agent falls back to its frontmatter model: line, which is the only place version pinning lives.
| Agent | Frontmatter pin (source of truth, edit here to bump version) | CLI override accepted |
|---|
| claim | claude-opus-4-7 | claim-model: opus | sonnet | haiku (or global model:) |
| experiment | claude-opus-4-7 | experiment-model: opus | sonnet | haiku (or global model:) |
| verify | claude-sonnet-4-6 | verify-model: opus | sonnet | haiku (or global model:) |
| iteration | claude-opus-4-7 | iteration-model: opus | sonnet | haiku (or global model:) |
Log on entry:
[models] claim=<frontmatter-pin or alias> experiment=... verify=... iteration=... (override-source: CLI-stage | CLI-global | frontmatter per stage)
When a CLI alias is supplied (per-stage or global), the log line shows the alias rather than the frontmatter pin (since the alias is what the runtime actually used).
claim — Idea Discovery
Resume check (only when RESUME=true): if all three claim stage-completion artifacts exist non-empty, log [resume] stage=claim skipped — IDEA_REPORT.md, FINAL_PROPOSAL.md, EXPERIMENT_PLAN.md present and do not invoke the claim agent; jump straight to the Claim Gate using the existing files. Otherwise invoke the agent with resume: true forwarded so it can phase-skip internally.
Invoke the claim agent with a self-contained natural-language prompt that opens with the two injected blocks — ## HARD CONSTRAINTS (from task.md — non-negotiable) then ## NOTICE (from task.md — informational) (see the Key Rules "Injecting task.md" bullet; the claim agent receives the full union of both — every stage's items — because it authors EXPERIMENT_PLAN.md for all downstream stages) and then includes: the research direction, a pointer to task.md if it exists, the retrieval rule, AUTO_PROCEED, RESUME, and the expected output paths. The same two blocks are prepended to every other stage agent's dispatch prompt (experiment / verify / iteration) — but stage-scoped: each downstream agent gets only the items routed to it (per the targeting rules), re-injected on every stage and every resume.
↳ Global memory read (default on; no-op only in the reproduction combo BEHAVIOR_SOURCE=given + MECHANISM=given). When research_memory.json exists at the project root, pass research_memory: research_memory.json to the claim agent so the strategy skills avoid re-doing concluded work — BEHAVIOR_SOURCE=discovery avoids behaviors whose status ∈ {established, conditional, not-established} (all settled; inconclusive stays a retry candidate); BEHAVIOR_SOURCE ∈ {given, given-validation} avoids, for the matched behavior, any (mechanism direction, family) whose headline + claims[].conclusion prose reads as stable positive or stable negative per Rule 1 (mixed / deferred / integrity-broken / under-power all stay retry candidates). An explicit pin in task.md (behavior / direction / family) overrides this avoidance (Rule 2). Before dispatching the claim agent, the orchestrator checks for a settled-pin conflict here (it already holds both task.md and research_memory.json): scan task.md for the pinned behavior (the given behavior) and any mechanism direction: / family: line, and — for direction/family pins — read the matched behavior's mechanisms[] entries (headline + claims[].conclusion) to decide whether the pinned pair is already settled per Rule 1. On a hit, resolution respects AUTO_PROCEED and keys on the retry-settled marker in task.md (see Rule 2): when task.md carries retry-settled: true, resolve to honor-pin (the user explicitly authorized re-doing settled work); otherwise — AUTO_PROCEED=true resolves to pick-fresh silently (log [settled-pin] <pinned item> already settled in round <N>, no retry-settled marker — picking a fresh untried <level>), AUTO_PROCEED=false calls AskUserQuestion (honor-pin vs pick-fresh, recommended pick-fresh) and blocks. Pass the resolved decision into the claim agent's prompt as pin_resolution: honor-pin | pick-fresh. When no conflict is detected (pin present but not settled, or no pin), pass no pin_resolution — the claim agent then honors any pin as written (Rule 2). (The claim agent re-checks settled status as a backstop against a missed semantic match — see agents/claim.md step 0.5.) No flag governs the read (on by default); pass research_memory: false only in the reproduction combo (BEHAVIOR_SOURCE=given + MECHANISM=given) or when the file is absent (round 1). See Global Exploration Memory.
↳ Given-Behavior Comprehension Gate (BEHAVIOR_SOURCE ∈ {given, given-validation} only; fires independent of AUTO_PROCEED — always waits for the user, like the multi-round guard; note the settled-pin gate does NOT, it respects AUTO_PROCEED). Both given and given-validation take the behavior as written in task.md / the inline direction (they differ only in whether M0 later validates it) — so before dispatching the claim agent, the orchestrator (it already holds task.md) checks that a concrete behavior is actually present. A concrete behavior names a specific, falsifiable model-observable output pattern, ideally with its triggering condition — e.g. "the model rates first-person I believe X as less likely true than the matched third-person assertion" — as opposed to a bare topic / research direction — e.g. "explore the mechanics of LLM beliefs". An explicit Rule-2 pinned behavior in task.md always counts as concrete.
-
PASS (a concrete behavior is present) → proceed with the requested behavior_source as-is; pass no extra field.
-
FAIL (only a topic / direction, no concrete behavior) → halt and AskUserQuestion (independent of AUTO_PROCEED), two options:
- switch to discovery (recommended) → re-dispatch the claim stage with
behavior_source: discovery, so the phenomenon is mined (/mechanism-behavior-discovery) and then hard-gated by M0 before any mechanism work.
This gate always waits for the user's answer — there is no timeout, no auto-abort, and no silent auto-fallback even under AUTO_PROCEED=true (AskUserQuestion blocks indefinitely; a vague given direction is a decision only the user can make).
- specify the behavior → the user supplies the concrete phenomenon as free text; inject it into the claim agent's prompt as
given_behavior: "<text>" and proceed with the requested behavior_source.
Rationale: running given / given-validation on a vague direction would make the claim stage silently invent a behavior — this gate forces either a user-pinned/clarified behavior or the M0-protected discovery path. The claim agent re-checks this as a backstop (see agents/claim.md step 0.6), so standalone /auto-claim callers are covered too. Ignored when BEHAVIOR_SOURCE=discovery (discovery mines + M0-validates the phenomenon by design).
Expected artifacts: idea-stage/IDEA_REPORT.md, refine-logs/FINAL_PROPOSAL.md, refine-logs/EXPERIMENT_PLAN.md.
↳ Claim Ledger update: after the Claim Gate fires, seed the ledger from refine-logs/EXPERIMENT_PLAN.md — one row per claim id with statement, origin, and the planned data (set provenance, source, available_n, and the planned used_n), models, method; final_status = "planned". Set the global header (project, direction, models). See Claim Ledger.
🚦 Claim Gate: Echo the agent's top-3 idea list, then branch on AUTO_PROCEED before any UI call:
AUTO_PROCEED=true (default) → do NOT call AskUserQuestion. Directly accept the top-ranked idea from IDEA_REPORT.md and proceed to the experiment routing call. Log AUTO_PROCEED: accepted top idea — [title] (no prompt shown).
AUTO_PROCEED=false → call AskUserQuestion with the four choices (approve / switch / re-run / stop) and block:
- approve → continue to the experiment stage with the top idea.
- switch → re-invoke the claim agent with
chosen_idea: <N> so it rewrites refine-logs/FINAL_PROPOSAL.md and refine-logs/EXPERIMENT_PLAN.md for idea #N (leaving IDEA_REPORT.md intact), then re-open the gate.
- re-run → re-invoke the claim agent with the same arguments plus the user's free-form feedback.
- stop → halt the pipeline.
If IDEA_REPORT.md is missing, empty, or only a placeholder, re-invoke the agent with corrective context before opening the gate.
experiment — Mechanism Routing + Build + Deploy
Resume check (only when RESUME=true). Note on committed flag semantics: Mode A (route_only) writes the routing candidates with committed: false; Mode B (build) flips it to committed: true once its Phase 0 has locked in chosen_family. The exception is a routing: not-applicable (behavioral-only) proposal — Mode A writes committed: true immediately with no mechanism family, because such a proposal is by definition "committed" to having none. So committed:true means either "Mode B has at least started" or "Mode A wrote the not-applicable stub"; the two are disambiguated by checking for a routing: not-applicable line in the file.
Pre-branch parse step (mandatory for cases 1 / 2 below): before classifying, extract from refine-logs/MECHANISM_ROUTING.md:
committed=$(grep -E '^committed:' refine-logs/MECHANISM_ROUTING.md | tail -1 | awk '{print $2}')
not_applicable=$(grep -cE '^routing:[[:space:]]*not-applicable' refine-logs/MECHANISM_ROUTING.md)
chosen_family=$(grep -E '^chosen_family:' refine-logs/MECHANISM_ROUTING.md | tail -1 | sed 's/^chosen_family:[[:space:]]*//')
Then derive the value to forward to Mode B's chosen_family: arg:
- If
not_applicable > 0 → chosen_family_for_mode_b = "not-applicable" (overrides whatever the chosen_family: line says; not-applicable is the canonical sentinel Mode B's invocation contract accepts).
- Else if
chosen_family is non-empty and not literal none → use it verbatim.
- Else (
committed:true but no usable chosen_family and no routing: not-applicable marker) → the file is malformed; fall back to case 3 (re-run both calls) rather than invoking Mode B with a missing required arg. Log [resume] MECHANISM_ROUTING.md committed:true but chosen_family missing — falling back to fresh routing.
Branch as follows (using committed and chosen_family_for_mode_b from the parse step):
-
Phenomenon-terminated run (check first, before the committed cases). If refine-logs/EXPERIMENT_RESULTS.md exists non-empty AND its top metadata has phenomenon_status: not-established (or a terminal phenomenon_status: inconclusive), the experiment stage's Phase 1.25 M0 gate already ended the pipeline on a prior run — MECHANISM_ROUTING.md may be absent or uncommitted, which is expected (the run stopped before mechanism routing). Do not re-run the experiment agent. Log [resume] stage=experiment skipped — phenomenon_status=<not-established|inconclusive>, pipeline already ended at M0, then re-apply the phenomenon early exit: skip the verify and iteration stages, and finalize state into the Ledger — set claims_ledger.json's pipeline_status = ended-phenomenon-not-established (or ended-phenomenon-inconclusive), populate journey_summary / open_items[] per the Terminal-state writeback rule, set each affected claim's final_status to ✗ phenomenon not established / phenomenon validation inconclusive — fix M0, and re-render CLAIMS_LEDGER.md. This branch takes precedence over cases 1–4 below — a terminal phenomenon_status is checked before the committed-flag parse, since on a clean negative finding there is intentionally no committed routing.
-
committed: true AND EXPERIMENT_RESULTS.md + EXPERIMENT_TRACKER.md exist non-empty → log [resume] stage=experiment skipped — routing committed, results present and skip all call(s).
-
committed: true but no (or empty) results files → either build previously started, locked the routing, then crashed before producing results, or the proposal is routing: not-applicable and build has never been invoked. Either way: invoke the experiment agent in build-only mode with chosen_family: <chosen_family_for_mode_b> and resume: true. Phase 0 will either resume from where it crashed (chosen_family was a real family) or take the no-mechanism path (chosen_family was not-applicable). This holds regardless of AUTO_PROCEED — the routing decision is already persisted in the file, so there is nothing to mini-prompt about.
-
MECHANISM_ROUTING.md exists with committed: false (a previous Mode A finished but build never reached its commit step — only reachable under the legacy AUTO_PROCEED=false two-call flow) → re-run per the Call-count rule below. Under AUTO_PROCEED=true this means a single combined call (Phase 0 will overwrite the stale candidates as needed); under AUTO_PROCEED=false it means re-running the route_only call, re-prompting the user, then the build call.
-
No MECHANISM_ROUTING.md at all → run per the Call-count rule below from scratch.
In all cases forward resume: true to whichever calls are invoked.
MECHANISM=given short-circuit (both AUTO_PROCEED modes). When MECHANISM=given, the user specified the mechanism method/family in task.md and the claim stage stamped it as chosen_mechanism: in the top metadata of refine-logs/EXPERIMENT_PLAN.md (and FINAL_PROPOSAL.md). The orchestrator reads that value:
chosen_mechanism=$(grep -E '^chosen_mechanism:' refine-logs/EXPERIMENT_PLAN.md | tail -1 | sed 's/^chosen_mechanism:[[:space:]]*//')
Then branch on the value, in both AUTO_PROCEED modes (no routing call, no family mini-prompt — the mechanism is already user-decided):
- A real method/family → forward
CHOSEN_FAMILY=<chosen_mechanism> to the experiment agent and use the build path directly (Phase 1.5 Mode B). This is the first-class form of the family-pin short-circuit below; both resolve to the same Mode-B direct commit.
not-applicable (a behavioral-only reproduction — the user declared no mechanism claim) → forward CHOSEN_FAMILY=not-applicable to the experiment agent's build call — the same behavioral-only sentinel the post-routing build call already accepts (see "Special case — behavioral-only proposal" below). The experiment writes the routing: not-applicable stub and runs no mechanism milestone.
- Marker absent (
MECHANISM=given but nothing stamped — a malformed claim output) → fall back to the normal routing branch below and log [mechanism] MECHANISM=given but no chosen_mechanism stamped — falling back to routing.
Family pin short-circuit (Rule 2, both AUTO_PROCEED modes). (Applies when MECHANISM=discovery but task.md still pins a family:.) If task.md pins a family: and the orchestrator's settled-pin gate (claim-stage setup) resolved it to honor-pin (or there was no conflict), forward CHOSEN_FAMILY=<pinned family> to the experiment agent and use the build path directly (Phase 1.5 Mode B) — this bypasses both the combined-call auto-select and the AUTO_PROCEED=false mini-prompt, since the family is already user-chosen. (A pick-fresh resolution leaves CHOSEN_FAMILY unset, falling through to the normal branch below, where the avoid-set excludes the settled family.)
Call count (branch on AUTO_PROCEED, when neither the MECHANISM=given short-circuit nor a family pin short-circuit applies — i.e. MECHANISM=discovery with no pin):
AUTO_PROCEED=true (default) → one combined call. Invoke the experiment agent once with CHOSEN_FAMILY unset; the agent's Phase 0 Step 4 auto-selects the [recommended] candidate and proceeds straight into build. No orchestrator-side mini-prompt.
AUTO_PROCEED=false → two calls: route_only → mini-prompt → build (sections below). The human picks the family.
Routing call (mode: route_only) — only when AUTO_PROCEED=false
Invoke the experiment agent. It reads FINAL_PROPOSAL.md + EXPERIMENT_PLAN.md, runs /mechanism-skills, and writes refine-logs/MECHANISM_ROUTING.md with 2–3 candidates (recommended one marked, committed: false).
Special case — behavioral-only proposal: if the agent's notes say routing: not-applicable, skip the mini-prompt and go straight to the build call with chosen_family: not-applicable.
🔸 Mini-prompt — Family selection (only when AUTO_PROCEED=false)
Call AskUserQuestion and wait for the user to pick:
question: "Which mechanism family should the experiment commit to?"
header: "Mech family"
options: [candidate 1 (recommended), candidate 2, candidate 3]
ℹ️ Implementation note: AskUserQuestion blocks until the user responds — there is no timer mechanism behind it, and that is intentional under the binary AUTO_PROCEED model: AUTO_PROCEED=true skips this prompt entirely via the Combined call above (full auto, no human needed); AUTO_PROCEED=false waits indefinitely for the human (human-in-the-loop, waiting is the desired behavior). There is no third "auto-proceed after timeout" mode — the two are mutually exclusive by design.
This is a single-question selection, not a full gate.
Build call (mode: build) — only when AUTO_PROCEED=false
Before invoking Mode B, extract chosen_idea_title from the claim agent's return message (cached from the Claim Gate). The claim agent's output contract (see agents/claim.md's "Output contract") guarantees a line of the shape **Recommended:** #<n> — <title> — parse the title from that line:
chosen_idea_title=$(printf '%s\n' "$claim_agent_return" \
| grep -E '^\*\*Recommended:\*\*' \
| head -1 \
| sed -E 's/^\*\*Recommended:\*\*[[:space:]]*#[0-9]+[[:space:]]*—[[:space:]]*//')
If the parse yields an empty string (claim agent's return is malformed, or the switch <N> branch produced a re-run that re-wrote the report), fall back to reading the title from idea-stage/IDEA_REPORT.md — find the ### 🏆 Idea <CHOSEN_IDEA>: heading (or ### Idea <CHOSEN_IDEA>: for non-top-ranked) and take the title after the colon. If both sources fail, log [build-call] chosen_idea_title unresolved — passing "n/a" and forward chosen_idea_title: n/a (Mode B's contract says this field is for logging only, so it does not gate behavior).
Invoke the experiment agent with the chosen family and chosen_idea_title. It commits the routing, implements code, runs cross-model code review, runs sanity, deploys the full suite, and collects results.
Expected artifacts: refine-logs/EXPERIMENT_RESULTS.md, refine-logs/EXPERIMENT_TRACKER.md, MECHANISM_ROUTING.md with committed: true.
↳ Claim Ledger update: after the Experiment Gate returns, fill each claim's actual data (overwrite used_n with the amount actually used in the runs; correct available_n/provenance/source and set subset_note if the realized data differs from plan), models, method, and the main_experiment block (verdict, key_stats, headline) from refine-logs/EXPERIMENT_RESULTS.md (+ MECHANISM_ROUTING.md for the method/family). When a claim carries suspected_under_power: true (Power-Fidelity check, UNDERPOWER=tag), append [suspected under-power: used_n X/Y, seeds A/B, grid P/Q] to its caveats[] and mark its main_experiment.verdict provisional (e.g. not-supported [provisional — suspected under-power]) so verify + iteration treat the negative as provisional, not a confirmed falsification. See Claim Ledger.
🚦 Experiment Gate (inside the agent): After code review and sanity pass, before launching the full suite, the agent decides on AUTO_PROCEED × AUTO_DEPLOY — same binary-binary matrix as the Verify Gate:
AUTO_PROCEED=true (default) → the agent proceeds without UI prompt regardless of AUTO_DEPLOY. Log on return: AUTO_PROCEED: deployed [N] experiments, ~[X] GPU-hours.
AUTO_PROCEED=false AND AUTO_DEPLOY=true → the agent proceeds without UI prompt; AUTO_DEPLOY=true acts as standing approval for the deploy step.
AUTO_PROCEED=false AND AUTO_DEPLOY=false → the agent calls AskUserQuestion with the deploy plan (approve / narrow scope / abort) and blocks indefinitely until the user answers. This is the intended human-in-the-loop behavior — no timeout.
🚦 Plan-reconciliation conflict → Round-End Decision. When the experiment agent returns reconciliation_status: escalate (its Phase 1.5 Step 7 found that the committed submethod cannot satisfy a method_sensitive field — e.g. the planned metric or sites — without changing the plan's scientific intent), it stops before building, since this stage may not rewrite the claim-authored EXPERIMENT_PLAN.md. Because the fix would have to alter scientific intent, the orchestrator does not auto-rewrite the plan (that risks silently changing the question the claim asserts — safety-first). Instead it writes a Round-End Decision (ended-needs-decision (experiment: plan-reconciliation-conflict), see Round-End Decision) naming the conflicting milestone field and pointing at MECHANISM_ROUTING.md's ## Plan reconciliation, then stops. Repair is the plan owner's job: the user fixes the conflicting milestone field in EXPERIMENT_PLAN.md (or picks a submethod that fits, or re-scopes the claim) and re-runs. A plain re-bind (no conflict) is not a stop — the agent absorbs it in routing and proceeds normally.
🛑 Phenomenon-Validation early exit (BEHAVIOR_SOURCE ∈ {given-validation, discovery} runs). When the experiment agent returns Phenomenon status: not-established (or a terminal inconclusive), the experiment stage's Phase 1.25 gate already stopped before any mechanism milestone (see /auto-experiment Phase 1.25). The orchestrator then ends the pipeline early:
- Skip the verify and iteration stages entirely — there is no established phenomenon to stress-test or iterate on.
- Update the Claim Ledger: set the affected claim(s)'
final_status to ✗ phenomenon not established (for not-established) or phenomenon validation inconclusive — fix M0 (for terminal inconclusive), pulling the M0 evidence from refine-logs/EXPERIMENT_RESULTS.md (phenomenon_status metadata). Treat this as the run's final ledger hook, so the maintenance protocol's step (6) fires the Ledger Figures hook here (a negative finding still gets a clean record).
- Set
claims_ledger.json's pipeline_status = ended-phenomenon-not-established (or ended-phenomenon-inconclusive) per the Terminal-state writeback rule, populate journey_summary (experiment: negative headline + M0 evidence) and open_items[] (any surfaced caveats — under-power, unmatched distributions, missing cross-check tools, etc.), and re-render CLAIMS_LEDGER.md. The M0 result is framed as a valid negative finding, not an error.
- This is governed by
AUTO_PROCEED at the experiment-stage gate (Phase 1.1): under AUTO_PROCEED=false the agent already presented verdict-appropriate options (terminate — accept & write report / re-run M0 — adjust the test/plan first) before returning; the orchestrator only sees not-established (or terminal inconclusive) once that choice resolved to terminate. established / conditional returns proceed to verify normally (conditional having restricted mechanism analysis to the holding conditions at runtime — the plan file is not rewritten by the experiment stage).
🚦 Power-Fidelity Gate (strict marker absent — every non-reproduction combination; UNDERPOWER != off). Cost-aware runs use (often reduced) scale, so a not-supported / partial / null main-experiment verdict can be an under-power artifact (too little data/seeds/grid to detect a real effect) rather than a genuine negative — and a false negative propagating to verify can get a true claim wrongly "falsified". After the experiment results land, the experiment agent flags any claim whose verdict is weak and whose realized scale is materially below EXPERIMENT_PLAN.md (used_n shortfall, fewer seeds, or fewer grid/checkpoint points) as suspected_under_power, with the X/Y figures. The orchestrator then, per UNDERPOWER:
tag (default) → tag the claim [suspected under-power: used_n X/Y, seeds A/B, grid P/Q] as a provisional caveat (carried into the ledger caveats[], and forwarded to verify + iteration so the negative is treated as provisional, not a confirmed falsification) and proceed to verify (respecting discovery's cost-aware design). This is the AUTO_PROCEED=true behavior; under AUTO_PROCEED=false the experiment agent instead AskUserQuestions (full re-run at plan scale / targeted re-run of the suspect milestone / accept demo-scale & proceed) and blocks.
stop → treat it as a Round-End Decision (ended-needs-decision (experiment: suspected-under-power), see Round-End Decision) even under AUTO_PROCEED=true, with detail = which claims/milestones look under-powered and by how much, remedy = re-run at plan scale (or accept & re-run with UNDERPOWER=tag). For users who never want a suspected artifact to silently reach verify.
off → no check (full cost-aware behavior).
verify — Claim Verification
Resume check (only when RESUME=true): skip verify only if all of these hold — verify/VERIFY_REPORT.md non-empty, verify/INTEGRITY_AUDIT.md non-empty with a populated ## Variant integrity (Phase 9) section (which may be [skipped — all main-experiment audits FAIL] for the legitimate short-circuit), and every target claim has a non-empty verify/<claim_id>_*/main_experiment_audit/EXPERIMENT_AUDIT.json (glob-expand the per-claim directory for each; the legacy flat verify/main_experiment_audit/ path no longer applies). If any target claim is missing its main-experiment audit, the prior run was partial (it never audited that claim) — do not trust an [skipped — all main-experiment audits FAIL] marker; re-run verify. Log [resume] stage=verify skipped — Phase 2 + Phase 9 both finalized. Otherwise invoke the agent with resume: true forwarded so partially-completed variants and partially-done integrity audit can be reused. Guards against a common failure mode: Phase 2 finishes and writes INTEGRITY_AUDIT.md with only the main-experiment section, then the process dies during Phase 9 — without the Phase-9-section check, resume would wrongly think verify is done.
Invoke the verify agent. It runs /auto-verify, whose full state machine — target selection (Stage 1 audits every target claim), the mandatory Phase 2 per-claim main-experiment integrity gate, the Phase 3 step 0 Stage-2 pick (top-K by importance judgment from the Stage-1-admitted pool; K = MAX_VERIFY_CLAIMS), within-family method swaps along DIMENSIONS on the picked set, the robustness = #pass / N_eligible formula, the mandatory Phase 9 per-claim variant integrity gate, and final state assignment — is owned by skills/auto-verify/SKILL.md (single source of truth). Do not re-derive that logic here; this orchestrator only consumes the outputs below.
What the orchestrator acts on (everything else is skill-internal):
- Five per-claim terminal states, read from
verify/VERIFY_REPORT.md (trust-the-files): PASS / FAIL / INCONCLUSIVE (Phase 2 main-experiment integrity FAIL — variants never ran) / ZERO_ELIGIBLE_VARIANTS (variants ran but all failed Phase 9 integrity) / INTEGRITY_ONLY (Phase 2 pass/warn but Stage 2 intentionally skipped; per-claim stage2_skip_reason ∈ {swap_variants_false, max_verify_claims_cap}). All five are valid; INCONCLUSIVE and ZERO_ELIGIBLE_VARIANTS route to different iteration surfaces (fix main experiment vs. fix variants); INTEGRITY_ONLY is a no-op-with-upgrade-suggestion bucket. These map to the five iteration context buckets below and to the ledger verify.verdict.
- Two integrity verdicts for logging / the Verify Gate: main-experiment integrity (Phase 2) and variant integrity (Phase 9), each
PASS|WARN|FAIL.
- All-integrity-broken outcome (see Key Rules → "All-integrity-broken"): Phase 2 main-experiment integrity FAIL on every target claim, or Phase 9 variant integrity FAIL on every claim with no recoverable verdict. Never a crash halt and never relabeled PASS — with
REVIEW_LOOP=true (default) the broken claims route into iteration (verify-inconclusive / verify-zero-eligible); with REVIEW_LOOP=false it becomes a Round-End Decision (ended-needs-decision (verify: all-main-experiments-integrity-broken | all-variants-integrity-broken)).
Expected artifacts: verify/VERIFY_REPORT.md, verify/INTEGRITY_AUDIT.md (Phase 2 main-experiment section + Phase 9 variant section, in one file), verify/STAGE2_PICK.json (Phase 3 step 0 record of who was picked for Stage 2), verify/<claim_dir>/main_experiment_audit/{EXPERIMENT,MECHANISM}_AUDIT.{md,json} per target claim (Stage 1 audits all), verify/<claim_dir>/variant_audit/{EXPERIMENT,MECHANISM}_AUDIT.{md,json} per picked claim only, and verify/<claim_dir>/ROBUSTNESS.md per claim (always written — verbose body when COMPACT=false, minimal verdict header when COMPACT=true; the per-claim verdict is the resume protocol's source of truth). <claim_dir> = <claim_id>_<short_claim> on disk (see skills/auto-verify/SKILL.md "Directory Layout"). Resume checks that need to match a per-claim path must glob-expand verify/<claim_id>_*/, not test the bare verify/<claim_id>/.
↳ Claim Ledger update: after the Verify Gate returns, fill each claim's verify block — robustness, per-axis axes.{method,dataset,model}, integrity, verdict, and (when verdict = integrity_only) stage2_skip_reason — from verify/VERIFY_REPORT.md + verify/<claim_dir>/ROBUSTNESS.md + verify/INTEGRITY_AUDIT.md, and set final_status to mirror verify.verdict. Claims marked INTEGRITY_ONLY with stage2_skip_reason: max_verify_claims_cap get final_status: "audit passed, swap-test deferred (max_verify_claims cap)"; those with stage2_skip_reason: swap_variants_false get final_status: "audit passed, swap-test skipped (audit-only mode)". See Claim Ledger. When REVIEW_LOOP=false this hook is also the final ledger hook of the run, so the maintenance protocol's step (6) fires the Ledger Figures hook here; with REVIEW_LOOP=true step (6) is deferred to iteration:final.
🚦 Verify Gate (inside the agent): Before launching variants, the verify agent prints the target-claim summary + projected GPU-hours and decides on AUTO_PROCEED — same binary semantics as Claim Gate / Experiment Gate:
AUTO_PROCEED=true (default) → the agent proceeds without UI prompt regardless of AUTO_DEPLOY. Log on return: AUTO_PROCEED: verified [N] claim(s) across [DIMENSIONS] — [N_pass] PASS / [N_fail] FAIL / [N_inconclusive] INCONCLUSIVE / [N_zev] ZERO_ELIGIBLE_VARIANTS / [N_integ_only] INTEGRITY_ONLY; main-experiment-integrity: <PASS|WARN|FAIL>, variant-integrity: <PASS|WARN|FAIL>.
AUTO_PROCEED=false AND AUTO_DEPLOY=true → the agent proceeds without UI prompt; AUTO_DEPLOY=true acts as standing approval for the deploy step.
AUTO_PROCEED=false AND AUTO_DEPLOY=false → the agent calls AskUserQuestion with the deploy plan (approve / narrow scope / abort) and blocks indefinitely until the user answers. This is the intended human-in-the-loop behavior — no timeout.
iteration — Auto Review Loop
Skip this stage if REVIEW_LOOP=false.
Resume check (only when RESUME=true): read review-stage/REVIEW_STATE.json (schema: iterations_consumed, claim_reentries_consumed, status, last_verdict, last_score, pending_upstream_calls — see auto-iteration-loop/SKILL.md "State Persistence"). Branch:
status == "completed" AND last_verdict ∈ {ready, almost} AND last_score >= TARGET_SCORE → log [resume] stage=iteration skipped — completed at score=<last_score>, verdict=<last_verdict> (final report at review-stage/AUTO_ITERATION_FINAL_REPORT.md) and do not invoke. Confirm AUTO_ITERATION_FINAL_REPORT.md exists non-empty; if missing, re-invoke the iteration agent with resume: true so it can run Termination's final-report assembly.
status == "completed" AND iterations_consumed >= MAX_ITERATIONS (or claim_reentries_consumed >= MAX_CLAIM_REENTRIES AND only ③ paths remain) → log [resume] stage=iteration skipped — budget exhausted (iterations=<n>, claim_reentries=<m>) and do not invoke. Confirm AUTO_ITERATION_FINAL_REPORT.md exists non-empty; if missing, re-invoke the iteration agent with resume: true so it can run Termination's final-report assembly.
status == "awaiting_upstream" → this is the back-edge handoff. See the "Back-edge handoff" subsection below.
- Otherwise → invoke the iteration agent with
resume: true. The agent inherits iterations_consumed and claim_reentries_consumed from the state file; the orchestrator does NOT reset them.
Invoke the iteration agent. Up to MAX_ITERATIONS cycles of: external LLM review → routed per-claim fixes (① variant-only / ② main-experiment-script / ③ claim-stage re-entry; the iteration agent may also record ⓪ narrative-only entries that change paper text without dispatching any back-edge) → deploy → re-review. Stops early when score ≥ TARGET_SCORE AND verdict ∈ {ready, almost} AND no claim is still FAIL / INCONCLUSIVE / ZERO_ELIGIBLE_VARIANTS (three-dimensional STOP rule). Each back-edge action ①/②/③ consumes 1 iteration uniformly; ③ additionally consumes 1 from MAX_CLAIM_REENTRIES; ⓪ does not consume budget and never escalates to the orchestrator.
Verify-result context handling — the orchestrator's job here is extraction + forwarding only, not routing. Parse VERIFY_REPORT.md for each claim's terminal state and forward five context lists to the iteration agent. The per-bucket fix routing (① / ② / ③ / ⓪, two-phase FAIL handling, the no-action-with-upgrade-suggestion contract for INTEGRITY_ONLY) is owned by skills/auto-iteration-loop/SKILL.md (single source of truth) — do not re-specify it here.
| Forward as | Claims with state |
|---|
verify-passed | PASS |
verify-failed | FAIL |
verify-inconclusive | INCONCLUSIVE |
verify-zero-eligible | ZERO_ELIGIBLE_VARIANTS |
verify-integrity-only | INTEGRITY_ONLY (both stage2_skip_reason values collapse here; iteration reads the per-claim field to pick the right upgrade command) |
Verify itself never picks among these; iteration's reviewer does (per the iteration skill's routing contract).
Back-edge handoff (status awaiting_upstream)
When the iteration agent returns with status: awaiting_upstream and a non-empty pending_upstream_calls list, that means Phase C of an iteration chose action type ③ via the full-path (not lightweight) route. The orchestrator must:
- Execute each call in
pending_upstream_calls in order. Each entry has the shape {skill: "auto-claim" | "auto-experiment" | "auto-verify", args: {…}}. Use the standard agent invocation (claim agent for auto-claim, etc.).
- After all queued calls complete and their artifacts are on disk, re-invoke the iteration agent with
resume: true. The agent inherits iterations_consumed and claim_reentries_consumed from the state file — the orchestrator does NOT reset them. The Phase C of the iteration that queued the upstream calls already incremented both counters before returning, so when the iteration loop resumes it starts a fresh iteration (Phase A → E).
- If any queued call does not complete cleanly, do NOT re-invoke the iteration agent — propagate the queued call's terminal state (the upstream skill already exhausted its own recovery, so retrying it here is pointless):
- Queued call returned
ended-needs-decision (a clean, fixable stop — e.g. the re-done /auto-experiment hit a plan-reconciliation conflict, produced no result, or a scorer collapse) → the pipeline takes the Round-End Decision path: set pipeline_status = ended-needs-decision (iteration-upstream: <underlying qualifier>), and copy the underlying call's detail / remedy into this run's Round-End Decision Record + claims_ledger.json round_end. Not a crash halt.
- Queued call genuinely crashed / halted (e.g. strict-OOM, an API error, sanity exhausted) → set
claims_ledger.json's pipeline_status = halted-at-iteration-upstream per the Terminal-state writeback rule, record the failure detail as an entry in open_items[], and re-render CLAIMS_LEDGER.md.
Lightweight type-③ rewrites do NOT go through this handoff — the iteration agent runs /auto-experiment and /auto-verify inline within the same iteration and never sets awaiting_upstream. The orchestrator only sees awaiting_upstream when the iteration agent explicitly handed off.
Expected artifacts: review-stage/AUTO_REVIEW.md, review-stage/REVIEW_STATE.json, review-stage/REVIEWER_MEMORY.md (from iteration 2+), and review-stage/AUTO_ITERATION_FINAL_REPORT.md (written by Termination once status = completed).
↳ Claim Ledger update (per round + at iteration end): after each iteration round's AUTO_REVIEW.md round section is written — and once more at iteration termination — merge that round's per-claim evidence into the ledger and overwrite each affected claim's final_status (see Claim Ledger). This is the step that records how a verify-PASS claim is narrowed or falsified across rounds; it is the ledger's single most important hook. The termination write is the final ledger hook of the run (when REVIEW_LOOP=true), so the maintenance protocol's step (6) fires the Ledger Figures hook immediately after — per-round writes never trigger it.
Claim Ledger (global living report)
A single claim-centric report maintained incrementally across all four stages, so the user can read one file and, per claim, see: what the claim is, what data and which model(s) it ran on, the experimental method, the main-experiment result, the verify verdict, and whether iteration later narrowed or falsified it — ending in one combined final_status. It is also the single canonical terminal record of the whole run: the top-level pipeline_status / journey_summary / open_items[] / round_end fields carry what a former separate pipeline report would have; there is no second report file to keep in sync.
Owner & contention rule. The orchestrator is the sole writer. It does not require any stage agent to change its return contract — at each hook it reads that stage's already-written artifacts on disk and extracts the per-claim fields. (Trust-the-summary-verify-the-files still applies: extract from the files, not the agent's prose.)
Stage-artifact ownership. Every stage document (EXPERIMENT_RESULTS.md, EXPERIMENT_TRACKER.md, MECHANISM_ROUTING.md, VERIFY_REPORT.md, ROBUSTNESS.md, AUTO_REVIEW.md, …) is owned by the skill that produces it and written only by its stage agent; the orchestrator writes only the ledger. A re-task re-runs the owning skill, which supersedes the rejected result in place (never a second, conflicting narrative); a stage that cannot produce compliant docs takes a Round-End Decision (see Key Rules).
Two files (both at project root):
claims_ledger.json — machine-readable single source of truth. Always English, stable schema, merge key = id. Survives RESUME (rebuildable from artifacts).
CLAIMS_LEDGER.md — human view, rendered from the JSON every time (so the file is always self-consistent even after a crash). Report-style prose follows task.md's language (per the Output-language rule); claim ids, file paths, and numbers stay as-is.
Maintenance protocol (idempotent re-render). At each hook the orchestrator: (1) reads claims_ledger.json if present, else starts {}; (2) merges only the current stage's fields into each claim by id (never deletes another stage's fields); (3) bumps updated_after_stage; (4) writes the JSON; (5) fully re-renders CLAIMS_LEDGER.md from the JSON. New claims produced by an iteration claim-stage re-entry are appended as new rows.
Step (6) [final-only]. When the current hook is the final ledger hook of the run (iteration:final, or verify when REVIEW_LOOP=false) AND LEDGER_FIGURES != false, fire the Ledger Figures hook, merge the returned per-claim entries into figures[], and re-run step (5) so the Markdown picks up the new image links and inline tables. The presence of figures/INDEX.md at the project root is the witness that step (6) has already fired for this run — on resume, an existing INDEX.md means skip; otherwise run. Non-final hooks never trigger step (6), so /paper-figure is invoked at most once per pipeline run.
Step (7) [final-only]. For every terminal state that produced a behavior/mechanism outcome — completed, truncated-at-verify (REVIEW_LOOP=false), ended-phenomenon-not-established, ended-phenomenon-inconclusive — the final ledger hook also updates the cross-round Global Exploration Memory by appending this run's behavior + mechanism outcome to research_memory.json. See Global Exploration Memory for the schema and the write procedure. Idempotent per round number, so a resume that re-reaches the final hook overwrites its own entry rather than appending a duplicate. ended-needs-decision exits produce no scientific outcome and therefore invoke no step (7).
JSON schema (claims_ledger.json):
{
"project": "<chosen idea title>",
"direction": "<research direction or task.md one-liner>",
"date_range": {"start": "", "end": ""},
"models": {"claim": "", "experiment": "", "verify": "", "iteration": ""},
"pipeline_status": "running | completed | halted-at-<stage> | ended-needs-decision | truncated-at-verify | ended-phenomenon-not-established | ended-phenomenon-inconclusive",
"round_end": {"qualifier": "", "detail": "", "remedy": "", "see": "", "partial_artifacts": [], "next_round_options": []},
"journey_summary": {
"claim": "<one-line: X ideas → top idea [title] (impact/novelty/feasibility)>",
"mechanism_strategy": "<directions from EXPERIMENT_PLAN.md mechanism_strategy; n/a when MECHANISM=given>",
"mechanism_routing": "<family=<name>, submethod=<name> (or `not-applicable`)>",
"experiment": "<[N] runs, [X] GPU-hours, headline positive|negative|inconclusive>",
"verify": "<[N] claim(s): [Np] PASS / [Nf] FAIL / [Ni] INCONCLUSIVE / [Nz] ZEV / [No] INTEGRITY_ONLY (cap=[N_cap], swap_off=[N_swap_off]); integrity[Phase2/Phase9]; or `skipped — <reason>`>",
"iteration": "<[N]/MAX_ITERATIONS iterations, claim-reentries=[R]/MAX_CLAIM_REENTRIES, score X/10 verdict Y, termination=<reason>; or `skipped — <reason>`>",
"figures": "<[F] across [C] claims; [J] judgment-skipped; [S] render-skipped, [E] errored; or `disabled — LEDGER_FIGURES=false` / `not-run — <reason>`>"
},
"open_items": [
"<free-form one-line entry — one per unresolved item; the orchestrator populates from: verify INTEGRITY_ONLY claims (stage2_skip_reason: max_verify_claims_cap — swap-test deferred), iteration Section-8 unresolved claims, claim-reentry refusals, halted-stage diagnostics, figure render/batch issues, and cross-stage caveats (under-power, missing cross-check tools, unmatched distributions, prepared-but-not-deployed code, etc.). Omit the field entirely (or use []) when nothing is open.>"
],
"iteration_summary": {"score": null, "verdict": "", "rounds_consumed": 0, "max_rounds": 0},
"updated_after_stage": "claim | experiment | verify | iteration:round-<N> | iteration:final",
"claims": [
{
"id": "C1",
"statement": "",
"origin": "",
"data": {"provenance": "existing | adapted | constructed", "source": "", "available_n": "", "used_n": "", "subset_note": ""},
"models": [],
"method": "",
"main_experiment": {"verdict": "", "key_stats": "", "headline": ""},
"verify": {
"robustness": null,
"axes": {"method": "pass|fail|excluded|n/a", "dataset": "pass|fail|excluded|n/a", "model": "pass|fail|excluded|n/a"},
"integrity": "PASS|WARN|FAIL",
"verdict": "PASS|FAIL|INCONCLUSIVE|ZERO_ELIGIBLE_VARIANTS|INTEGRITY_ONLY|n/a",
"stage2_skip_reason": "swap_variants_false|max_verify_claims_cap|null"
},
"iteration": {"final_reviewer_status": "", "changed": [], "falsified": [], "narrowed_to": ""},
"final_status": "",
"caveats": [],
"artifacts": [],
"figures": []
}
]
}
figures field rule. Default []. Populated only at the final ledger hook by the Ledger Figures hook; intermediate hooks leave it untouched. Each entry mirrors one row of figures/<claim_id>/INDEX.json and carries one of two artifact shapes depending on type:
{
"id": "c1_robustness",
"type": "bar",
"caption": "Robustness of C1 across method/dataset/model swaps.",
"png": "figures/C1/c1_robustness.png",
"pdf": "figures/C1/c1_robustness.pdf",
"md": null,
"tex": null,
"source_data": "verify/C1_<short>/ROBUSTNESS.md",
"status": "ok"
}
{
"id": "c1v2_k_sensitivity",
"type": "table",
"caption": "C1_v2 K-sensitivity sweep — necessity is knife-edge in K.",
"png": null,
"pdf": null,
"md": "figures/C1_v2/c1v2_k_sensitivity.md",
"tex": "figures/C1_v2/c1v2_k_sensitivity.tex",
"source_data": "runs/iteration_round_3/M0_K_sensitivity/summary.json",
"status": "ok"
}
The four artifact slots (png, pdf, md, tex) are all optional. Image figures fill png + pdf and leave md + tex null; tables fill md + tex and leave png + pdf null. The render template (below) dispatches on type to decide which slots to read. Only entries with status: ok are rendered into CLAIMS_LEDGER.md. error / skipped entries are preserved in the JSON (for debugging and re-render decisions) but suppressed in the human view — their tallies surface as entries in the Ledger's open_items[] instead.
data field rule. provenance records whether the dataset is an existing one used as-is (existing), an existing one transformed/relabeled/filtered (adapted), or built from scratch / synthetic (constructed). source names the dataset (or, for constructed, the construction method). available_n is the total size of the source pool; used_n is the amount actually used in this project's experiments — these differ whenever the project subsets the data, in which case subset_note states how/why. used_n is seeded with the planned count at the claim hook and overwritten with the realized count at the experiment hook.
final_status rule (the truth column). After verify, set it to mirror verify.verdict. For INTEGRITY_ONLY claims, append the stage2_skip_reason context (e.g., ⚪ integrity_only (swap-test deferred — max_verify_claims cap)). After iteration, overwrite it with the combined truth — e.g. ✓ holds, ⚠ configuration-specific (not general), ✗ falsified — <one-line why>. When verify says PASS but iteration falsifies/narrows, the overwrite is what surfaces the discrepancy in one place.