Skip to content

Repository files navigation

AutoR: A Recursive Research System

It proposes, tests, and tries to refute itself. The approval gate is the one thing it does not own — by default, that is you.

Python 3.10+ 8 stages, directed graph Terminal-first Human approval required Agent harness Reproducible research runs Proprietary license GitHub stars

Start here: The Framework · English Guide · 中文教程 · Full Documentation

AutoR example figure


AutoR is not a chat demo, not a generic agent framework, and not a markdown-only research toy.

It is a structured research harness over a coding-agent execution layer: the agent handles execution, the human owns the direction, and every run becomes an inspectable research artifact on disk.

docs/framework.md is the single document that describes what this system is: its implementation, its modules, what is new in it, and what it contributes. This README is the overview and the operating manual.

Contents

What AutoR is · Quick start · The stage graph · The rigor dial · Self-improvement rounds · Review · The stage contract · Execution model · Run layout · Architecture · Benchmarks · Documentation · Limits · License

What AutoR is

Most autoresearch systems optimize for autonomy. AutoR takes a different position: research is too important to hand over as a blind end-to-end loop. The goal is not to remove humans from research. The goal is to give them a stronger execution system.

AutoR runs a research project as eight stages wired into a directed graph. Six of the forward edges are guarded by artifacts on disk; thirteen backward edges let a late finding send the run back — Stage 07 can reopen the literature survey. Hypotheses are frozen and hashed when Stage 04 is approved, every one must be adjudicated at Stage 06 against a named result file that exists, and every paper claim traced at Stage 07; a supported or refuted verdict resting on a single seed is refused unless the run records why one run settles it. An adversarial reviewer attacks Stage 05's results and Stage 06's analysis, and the stage after each must answer every finding in writing or the gate refuses it. Drafts are scored and a refinement that does not improve is reverted. Every stage still stops at an approval gate, and by default that gate is you.

"Recursive" is eight mechanisms, each of them a file you can open

Move What runs Where
Propose Five proposers work from distinct lenses — mechanism, contrarian, adjacent field, null/artifact, regime — blind to each other; two statements whose Jaccard overlap reaches 0.5 collapse into one idea ideation_panel.py
Test Every baseline declares why_competent and a tuning_budget before it runs; the hypothesis set is frozen and hashed before any result exists, and a later change is legal only as a recorded amendment experimental_protocol.py
preregistration.py
Refute An adversarial pass asks why the result is wrong across ten named failure modes — confound, leakage, metric_cherry_picking, effect_within_noise, six more; a round can close as converged, refine_design, new_hypothesis or abandon validity_review.py
research_rounds.py
Critique Five seats review independently, cross-examine anonymised, then converge; a blocking objection is turned into a refusal in code against the panel's own chair, and a different model family audits the approval as a veto review_panel.py
cross_reviewer.py
Iterate Every valid draft is scored against a rubric read off disk; the champion is kept and a losing polish round is reverted before anyone reads it; a draft that loses on the weighted total but is non-dominated on the criterion vector is kept anyway rubric.py
evolution.py
pareto.py
Learn Each finished run records its route and measured fitness; a fitness comparison is keyed on the set of stages the run actually measured, so a run cannot score well by stopping early archive.py
decisions.py
Deliberate A stage that hits a genuine crux stops, names the question, and pulls in theorist / empiricist / critic / pragmatist plus an expert brief, then continues with an answer that names its own falsifier; budgeted, and measured against what the agent already believed deliberation.py
Localise A reviewer quotes the passage it objects to instead of refusing the whole stage; the revision is told to change only those spans and is diffed against them, so "preserve the correct parts" is measured rather than hoped for stage_comments.py

What a default run (--rigor standard) actually uses. Test, Refute, Iterate and Learn are on: the validity chain is unconditional at every rigor level including fast, --evolve defaults on, and the archive records every run — though it only steers under --archive-steer. Localise runs whenever a reviewer quotes a passage, which requires an agent reviewer. Propose and Deliberate need --rigor thorough; Critique's panel needs --rigor max and its cross-model veto is live on the rcb_agent.py path only. See the rigor dial for the exact mapping.

AutoR does not run itself. Manual approval is the default: approval_mode is manual unless a flag opts out. Seven of the eight moves above can only score, refuse, revert or re-order; none of them can approve a stage. The eighth, the review panel, is an approval gate, and it exists only on the runs where you hand it the gate. Recursion did not change who decides; it changed what reaches the desk. The research unit is unchanged: one reproducible run under runs/<run_id>/, isolated, resumable, with redo and rollback.

The one thing the docs will not claim

Approved stage summaries are the only free-text cross-stage memory. Every other cross-stage edge is a typed artifact with a declared reader: sixteen channels in information_flow.py each name the exact stage slugs that consume them, and the eight produced inside the walk name their producing stage as well. obligations.json and review_policy.json cross stages without touching a summary at all — both only behind an agent approval gate.

Many systems aim to generate research outputs that look ready. So the question is not

Does it look ready?

It is

Can you verify every part of it?

The answer is the validity chain — freeze at Stage 04, adjudicate at Stage 06, trace at Stage 07 (preregistration.py) — and the edge into writing stays shut until every frozen hypothesis carries a verdict (_guard_validity_chain, stage_graph.py).

The shape of the system, in counts you can re-derive

Every number below comes from a named symbol in the source. Re-derive them; that is the point of naming them.

Count Symbol Value
Stages (nodes in the walk) STAGES, src/utils.py 8
Guarded forward edges _ADVANCE_GUARDS, src/stage_graph.py 6
Backward edges REVISIT_EDGES 13
Conditional terminal edges TERMINAL_EDGES 1
Edges in the default (adaptive) graph StageGraph.adaptive() 22
Edges in --stage-graph linear StageGraph.linear() 9
Typed information channels CHANNELS, src/information_flow.py 16
validate_* functions the stage gate calls validate_stage_artifacts, src/utils.py 17
Required stage-summary headings REQUIRED_STAGE_HEADINGS 7
Rubric criteria (weighted, backend-free) CRITERIA, src/rubric.py 8
Flags on main.py / rcb_agent.py parse_args 61 / 37
Python modules / lines / tests the tree 150 / 62 k / 1826

python -m unittest discover -s tests -p "test_*.py" runs 1826 tests in ~69 s across 82 test modules, with no third-party dependency.

Quick start

Prerequisites

  • Python 3.10+
  • Claude CLI or Codex CLI on PATH for real runs
  • Local TeX tools only for --output-format latex; the default markdown output needs no TeX
  • pip install google-genai plus a key in GOOGLE_API_KEY or GEMINI_API_KEY — needed by three paths, not only the diagram one: --web-search gemini, required where the backend's own WebSearch tool is disabled (build_genai_client, src/web_search.py); the cross-model veto --cross-review auto|gemini (src/cross_reviewer.py); and --research-diagram, which also reads configs/diagram_config.yaml
  • The SDK is not a default dependency. Without it the diagram step prints Diagram generation failed: No module named 'google' and the run continues; cross-review records itself unavailable rather than agreeing

Common commands

Goal Command
Start a run (the goal is prompted for if omitted) python main.py --goal "Your research goal here"
Start with preloaded resources python main.py --goal "..." --resources paper.pdf refs.bib data.csv
Run a local smoke test without a real agent backend python main.py --fake-operator --goal "Smoke test"
Run with the automated reviewer gate python main.py --full-auto --goal "..."
Choose how much optional machinery to run python main.py --rigor thorough --goal "..."
Give the panel a researcher persona to stand in for python main.py --review-panel --persona docs/persona-example.md --goal "..."
Seat the panel across different models python main.py --review-panel --panel-models pi=opus skeptic=codex:default --goal "..."
Seat the optional Area Chair as a sixth reviewer python main.py --review-panel --panel-roles pi domain method repro skeptic reader --goal "..."
Keep the strong model for the steps that matter python main.py --effort-tiers --model opus --routine-model sonnet --goal "..."
Choose the execution backend and model python main.py --operator claude --model opus or python main.py --operator codex --model default
Choose the reviewer backend separately python main.py --full-auto --review-operator claude --review-model opus
Allow Codex-backed SSH / remote GPU execution python main.py --operator codex --codex-sandbox danger-full-access --goal "..."
Produce a LaTeX paper package instead of a markdown report python main.py --output-format latex --goal "..."
Stop once the report is written, skipping dissemination python main.py --final-stage 07_writing --goal "..."
Choose a writing venue profile python main.py --venue neurips_2025 · --venue nature · --venue jmlr
Resume the latest run python main.py --resume-run latest
Redo a stage inside the same run python main.py --resume-run 20260329_210252 --redo-stage 03
Roll back to a stage inside the same run python main.py --resume-run 20260329_210252 --rollback-stage 03
Re-enter an existing project instead of starting over python main.py --project-root ~/code/my-project --goal "..."
Seed the run from your own prior papers python main.py --paper-corpus ~/papers --goal "..."
Store runs on another disk python main.py --runs-dir /mnt/big-disk/runs --goal "..."
Raise the per-attempt ceiling for long training runs python main.py --stage-timeout 43200 --goal "..."
Give a stubborn stage more retries python main.py --max-attempts 10 --goal "..."
Let Stages 03-06 run as a repeatable round (default 1) python main.py --max-rounds 2 --goal "..."
Escalate a crux to a four-voice panel python main.py --deliberation --max-deliberations 3 --goal "..."
Widen Stage 02 with divergent proposers python main.py --ideation-panel --ideas-per-proposer 3 --goal "..."
Skip the intake stage python main.py --skip-intake --goal "..."
Add a generated method diagram to the paper python main.py --research-diagram --goal "..."
Search the web where the agent's own WebSearch is disabled python main.py --web-search gemini --goal "..."
Tag this run as one arm of a paired trial python main.py --trial t1 --capability review_panel --arm on --goal "..."
Read the paired-trial analysis and exit python main.py --trial-report
Benchmark AutoR on ResearchClawBench python rcb_agent.py --workspace <WORKSPACE>
Score a finished benchmark run with the reference judge python tools/score_rcb_run.py --workspace <WORKSPACE> --bench <BENCH>

Every flag, its default, and what is preserved on resume: docs/cli-reference.md. Stage identifiers accept 03, 3 or 03_study_design; --venue defaults to neurips_2025.

Three flags put an agent in the approval seat, not two — and a fourth removes the human without replacing them. approval_mode becomes agent for --approval-mode agent, --full-auto and --review-panel, and create_reviewer is called only when it does. --unattended on its own is the odd one: resolve_unattended returns True for all four, but with approval_mode still manual there is no reviewer to install, so the first approval menu raises UnattendedInputError rather than being decided. For a run with nobody at the terminal, pass --full-auto.

Because --rigor is resolved before resolve_unattended runs, a plain --rigor max sets review_panel = True and silently converts an interactive run into an unattended agent-gated one. Under a badge reading Human approval required, the flag that looks like more review is the flag that removes the reviewer. Three headline mechanisms — obligations, the standing review policy, the cross-model veto — also run only behind that agent gate, as do anchored comments. Manual approval is the default and remains the path for work you intend to publish.

For Codex-backed runs AutoR defaults to --codex-sandbox workspace-write. If a verified remote experiment needs SSH or external GPU access, use --codex-sandbox danger-full-access intentionally: it grants the Codex backend unrestricted local and remote execution, so it should not be the default for untrusted tasks.

# Self-improvement is on by default: navigate the graph, score every draft, keep the
# best, and record the route in ~/.autor/archive.
python main.py --goal "..."
python main.py --archive-report                   # what the archive has learned so far
python main.py --goal "..." --evolve-rounds 4     # spend more on improvement
python main.py --goal "..." --evolve-rounds 0     # measure and ratchet, no extra passes
python main.py --goal "..." --archive-steer       # let the archive pick the topology

# Opt out entirely: the strict 01-through-08 sequence, last draft wins.
python main.py --goal "..." --stage-graph linear --routing off --no-evolve --no-archive

Studio (browser UI)

A local web UI over the same Claude-backed runs: create a project, watch stages execute, approve or send feedback, read the compiled paper. It needs the Claude CLI on PATH to start a run.

python studio.py                                # http://127.0.0.1:8000/studio/
python studio.py --host 0.0.0.0 --port 8765     # bind externally, see the warning below
python studio.py --runs-dir /path/to/runs       # override runs directory

The Studio API has no authentication. It binds to 127.0.0.1 by default; anything that can reach it can start runs, approve stages, and read every file under the runs directory. For remote access prefer an SSH tunnel over --host 0.0.0.0. See SECURITY.md.

One honest limit, then the walkthrough: the Studio's lazy-resume approve path picks the next stage arithmetically — the first stage with a higher number (src/backend/studio_runner.py) — and never consults the router, so graph routing and backward moves are a CLI capability today. Page-by-page walkthrough and the full HTTP API: docs/studio.md.

How it works: the stage graph

Eight stages are the nodes; a finish node closes the walk. Stage 00 intake is not one of them — it runs before the walk starts, and _graph_entry_stage_select_stages_for_run (src/manager.py) only ever yield the eight. Solid edges advance, dotted edges go back. --stage-graph linear is the eight advance edges plus the conditional terminal — nine in all — and the guards come off with the backward ones (_advance_edges(guarded=False)): one edge out of each node leaves nothing to choose, so a guard there could only halt a run that the stage's own validation is about to fail anyway.

flowchart LR
    S1[01 Literature] --> S2[02 Hypotheses]
    S2 -->|has_hypotheses| S3[03 Design]
    S3 -->|design_artifacts| S4[04 Implementation]
    S4 -->|runnable_code| S5[05 Experiments]
    S5 -->|results_exist| S6[06 Analysis]
    S6 -->|validity_chain| S7[07 Writing]
    S7 -->|report_exists| S8[08 Dissemination]
    S8 --> Z([finish])
    S6 ==>|round abandoned| Z

    S2 -.->|the gap it rests on is not a gap| S1
    S3 -.->|a hypothesis cannot be brought to a decision| S2
    S4 -.->|not executable as specified| S3
    S5 -.->|implementation is at fault| S4
    S5 -.->|comparison cannot distinguish| S3
    S6 -.->|results insufficient to decide| S5
    S6 -.->|confound the results cannot repair| S3
    S6 -.->|evidence refutes, and points somewhere| S2
    S6 -.->|the numbers are wrong, not disappointing| S4
    S7 -.->|claim has no analysis behind it| S6
    S7 -.->|needs a result never produced| S5
    S7 -.->|the survey missed related work| S1
    S8 -.->|deliverable is not what a reader needs| S7
Loading

Six of the eight forward edges carry a guard, one per target stage (_ADVANCE_GUARDS); 01→02 and 08→finish are unguarded. Thirteen dotted edges go back (REVISIT_EDGES) — the longest is 07→01: writing it up showed the finding relates to work the survey missed. One conditional terminal (TERMINAL_EDGES, carried by both topologies) lets an abandoned round finish from Stage 06. The Stage 07 guard is the strictest: every preregistered empirical hypothesis needs a verdict and at least one figure under workspace/figures (_guard_validity_chain).

Who decides the move. AutoR decides which moves are admissible, by evaluating each edge's guard against the artifacts on disk. With --routing auto (the default, DEFAULT_ROUTING_MODE) the agent chooses among them and states a reason; --routing off always takes the graph's default. An off-menu choice — an unlisted target, or one with no stated reason — is refused, written to evolution/routing_refusals.jsonl (src/router.py), and replaced by the forward edge.

Two design calls worth naming. Blocked moves are handed to the agent with the reason they are blocked (StageGraph.moves) — the useful thing to say is not "you may go to 06" but "07 is closed because H2 has no verdict", and an agent that sees why writing is closed routes to the analysis that opens it. And a revisit whose justification repeats one already on the path is refused (repeats_a_previous_reason): going again on the same grounds is a loop, not an iteration.

A backward move is only ever a deliberate choice. The default is always the forward edge, and when a guard has closed it the default advances anyway and lets the stage's own validation — still refusing a Stage 07 that writes up unadjudicated hypotheses — be the gate it always was. A guard is a routing preference; the gate is the gate. So a refusal, a routing failure, or a run nobody is steering all come out as the linear pipeline rather than as a stall.

A stage is a node with a visit budget, not a position in a sequence: DEFAULT_MAX_VISITS = 3 (--graph-max-visits); DEFAULT_MAX_STEPS = 20 bounds the whole walk (--graph-max-steps).

The eight stages, and what you check at each

Stage Role What the human is checking
00_intake (before the walk) Align the goal, resources, constraints, target venue and success criteria. Answer the clarification questions, add the missing constraints, and narrow the project until it is executable.
01_literature_survey Build the related-work base, organize the evidence, identify the real gap. Reject shallow paper lists; require task framing, benchmarks, baselines, differences, and structured literature files with a cross-referenced sources.json/claims.json.
02_hypothesis_generation Convert the direction into typed, testable hypotheses and provisional paper claims. A - Decision rule: line on every empirical hypothesis, stating in advance what would count as support and what would count as refutation. These are the hypotheses frozen at 04 and adjudicated at 06.
03_study_design Turn the hypotheses into an executable plan, a declared protocol and a committed report plan. Datasets, metrics, ablations, budgets, failure criteria, machine-readable data artifacts, a baseline set where every entry states why_competent and its tuning_budget — and the figures the report will carry, each naming the claim it supports.
04_implementation Build the runnable code, configs, data preparation and sanity checks. This is the freeze point: approving the stage hashes the hypothesis set into workspace/notes/preregistration.json. Check the set you are freezing, and do not approve skeletons.
05_experimentation Run the planned experiments and write machine-readable results. The declared baselines and the seeds: a supported or refuted verdict off a single seed is refused unless the run states why one run settles it (MIN_SEEDS_FOR_A_VERDICT = 2).
06_analysis Interpret the results, produce figures, adjudicate every frozen hypothesis. A verdict for each one, backed by a result file the validator can find. The forward edge stays closed until then.
07_writing Produce the deliverable: a markdown report with embedded figures, or a venue-aware LaTeX package with a compiled PDF. That every claim traces, and that the report answers what the task actually asked. A confirmatory claim whose hypothesis is not in the supported set is already refused, so what is left to check is whether the exploratory ones are honestly labelled.
08_dissemination Package the run for review, release, reproduction or presentation. Readiness notes, review materials, manifests and outward-facing deliverables exist.

The rigor dial

--rigor is the single source of truth for which optional machinery a run uses. The table is generated from _LEVEL_FEATURES in src/rigor.py; an explicit --flag / --no-flag always beats the level, which is why those switches use BooleanOptionalAction with default=None.

--rigor --effort-tiers --deliberation --ideation-panel --review-panel
fast
standard (default) on
thorough on on on
max on on on on

Two consequences worth stating out loud:

  • Effort tiers are on by default. A default run therefore routes 04_implementation, 05_experimentation and 08_dissemination to a lean prompt and a single reviewer (DEFAULT_TIERS, src/effort.py). Under --rigor max the seated panel does not sit at those three gates unless you also pass --no-effort-tiers.
  • --rigor max makes the run unattended, because it implies --review-panel. See the note under Common commands.

The scientific-validity chain is not on this dial. It is unconditional at every level, fast included.

Self-improvement rounds

Every valid stage draft is measured against a rigour rubric read off disk — do the paths it names resolve, do the numbers it reports appear in a results file, did it produce artifacts during this execution, is the decision ledger four different things rather than one sentence four times. Eight weighted criteria, RUBRIC_VERSION = "2":

Criterion Weight From What it measures
grounding 3.0 01 References that resolve — every path the draft names exists on disk
numeric_fidelity 3.0 05 Reported numbers trace to a results file
reproducibility 3.0 01 The machine-readable validity chain is present and parses
contract 2.0 01 Contract compliance in substance, not just in headings
artifact_breadth 2.0 03 Artifacts produced this stage, across the classes the role calls for
quantification 2.0 04 Findings carrying numbers rather than adjectives
traceability 1.5 01 The decision ledger is four different things, not one sentence four times
commitment 1.5 01 Reports work, not intentions

min_stage exists so a criterion that cannot apply is not scored zero: Stage 01 has no experiment manifest to produce, and grading it as if it failed to produce one would make every early stage look worse than every late one — which would make the ratchet prefer late drafts for a reason unconnected to quality.

Measuring is free and always on. The rubric reads the run off disk and never calls a backend, so the property it buys costs nothing: the draft that gets promoted is the best one the run produced, not the last one. That is the half that was missing before — AutoR could iterate, but "later" was the only ordering it had, so a refinement that dropped a resolving reference was promoted on exactly the same terms as one that fixed something.

Improvement rounds are the half that costs, and they are budgeted separately from --max-attempts, which bounds a stage that is failing rather than one being improved. Two per stage by default, and a stage whose rubric has no shortfall worth acting on spends none of them — a round aimed at a criterion already at full marks produces churn, so AutoR does not buy one. --evolve-rounds 0 measures without polishing; --no-evolve restores the old behaviour entirely.

One edge of that budget is worth knowing before you resume a run. state() rehydrates the champion and the Pareto frontier from disk and nothing else (src/evolution.py), so --resume-run restarts rounds_spent and the patience counter at zero: the best draft survives the resume, the spend cap does not, and a stage resumed twice can buy the two rounds twice.

A round that scores worse is reverted, so a stage can only improve. A round that changes a hypothesis verdict is rejected outright, whatever it scored — the rubric is blind to what the run concluded, which removes the incentive, and the verdict_drift check removes the possibility. A revision a human asked for always stands, whatever it measures. The ratchet governs AutoR's own rounds, not the direction it is given.

Full mechanism, and the reasoning behind each refusal, in docs/self-improvement.md.

The archive: which moves paid, across runs

Every finished run is recorded into ~/.autor/archive — the route it took, the rubric fitness it reached, and the set of stages it actually measured (Archive.record_run, from record_into_archive in main.py). edge_payoffs compares runs that took an edge against runs that were offered it and declined (src/decisions.py), and propose_variant turns a payoff that is believable — enough observations, and a delta above min_gain — into a child variant that moves that one edge one step up or down the preference order. When no payoff is believable, propose_exploration proposes an unexplored edge instead, so an edge nobody has taken is not stranded forever.

A variant is only a reordering. It never opens a guarded edge, never adds one that was not declared, and never removes one: the guards are the correctness argument for letting an agent route at all, and the component that learns from outcomes is precisely the one that must not be able to weaken them. Promotion is as conservative — a challenger has to beat the incumbent within every comparability basis rather than on a pooled mean, because "runs that stopped early" is the cheapest composition for a topology to win on.

The archive records and proposes on every run; it steers only when you ask. The proposed variant is written down and reported, but the topology a run walks comes from the archive only under --archive-steer. Without it, resolve_graph returns the declared topology unchanged.

Paired trials

--trial ID --capability NAME --arm LABEL tags a run as one arm of a paired A/B trial in the archive; --trial-report prints the within-pair rubric difference with an exact sign-flip p-value and the smallest p-value that sample size could possibly reach (src/trials.py, src/inference.py). Below MIN_PAIRS_FOR_SIGNIFICANCE = 6 a trial is labelled underpowered rather than reported as a null.

This is the apparatus for answering "does this mechanism help?", not the answer. No paired trial has been run yet. Read docs/self-improvement.md before quoting anything from it.

Review: five kinds of critic

Only two of the five are the approval gate. The other three cannot approve anything.

Kind What it is Can it approve?
Solo reviewer (approval_agent.py) A coding agent with file tools returning one of six choices as JSON, through a parser that re-asks once and then falls back Yes — this is the gate under --full-auto
Review panel (review_panel.py) Five role-differentiated seats review blind, cross-examine anonymised peers, then a chair synthesizes Yes — this is the gate under --review-panel
Cross-model veto (cross_reviewer.py) A different model family audits an approval only No — veto only
Adversarial validity review (validity_review.py) Runs after Stages 05 and 06 are approved with the opposite instruction: "explain why this result is wrong" No — it creates debts the next stage must answer
Crux panel (deliberation.py) The executing agent raises a question; four voices answer it while arguing against themselves No — it is not a reviewer of a stage at all

The panel's teeth are mechanical. If any final-round seat carries blocking: true and the chair returned "approve", _enforce_blocking_objections() rewrites the approval into a refinement and records chair_overridden. It is enforced in code precisely because the chair is a model that can be argued out of a prompt-level rule. Blocking is read from the seat's own payload and only counts when that payload's decision token is legible, so a seat whose verdict was unreadable cannot veto.

Every panel run carries its own control arm. The chair's round-1 verdict is one model, one call, no peer input; panel_effect.json accumulates solo-vs-panel across the run and writes a verdict sentence deliberately phrased to be unflattering — "it did not earn that cost; consider dropping the panel" — when that is the truth. The same is true of the ideation panel, anchored comments, crux deliberation and effort tiers; scorecard.py reads all five ledgers at the end of every run and writes workspace/reviews/scorecard.md, keeping "could not be measured" separate from "changed nothing".

Two ledgers run underneath. review_policy.json turns every refusal into a standing rule injected into every later solo review, deduplicated on normalized text so a reviewer restating one complaint cannot manufacture the appearance of learning. obligations.json lets an approving reviewer attach a debt to a later stage — "fine, but you owe me a power analysis at design time" — which only a later reviewer may discharge; deferral is counted and shown, never silent.

Anchored comments make a refusal local. A reviewer quotes the exact passage it objects to (minimum 12 characters, and an unfindable quote is dropped as unanchored rather than sent), the revision is told to leave everything else byte-identical, and the next draft is diffed against the quotes so collateral rewriting is counted rather than assumed away.

Full seat charters, the deliberation protocol and the measurement design: docs/review-panel.md, docs/deliberation.md, docs/stage-comments.md, docs/scorecard.md.

The stage contract and what gets validated

AutoR does not consider a run successful just because it generated a plausible markdown summary.

Required stage summary shape. Seven headings, in this order — REQUIRED_STAGE_HEADINGS:

# Stage X: <name>

## Objective
## What I Did
## Key Results
## Files Produced
## Decision Ledger
## Suggestions for Refinement
## Your Options

Also required, and checked: exactly 3 numbered refinement suggestions, exactly the fixed 6 user options, concrete file paths under Files Produced, and no [In progress], [Pending], [TODO] or [TBD] placeholders.

Artifact gates. Most start by asking whether a file is there — the rows that say "valid", "resolving" or "matching" then parse it.

Stage Required non-toy output
Stage 01 A cross-referenced evidence ledger: sources.json and claims.json, where every cited source_id resolves
Stage 03+ Machine-readable data under workspace/data/, plus a report_plan.json committing to the figures and headline numbers the report will carry
Stage 05+ Machine-readable results under workspace/results/, plus a valid experiment_manifest.json
Stage 06+ Real figure files under workspace/figures/, and every planned figure's source_artifact resolving to a non-empty file
Stage 07+ (markdown) report/report.md with resolving figure references, between min_report_figures and 5 figures under report/images/, deliverables_coverage.json, citation_verification.json, self_review.json, report_review.json
Stage 07+ (latex) main.tex matching the venue, sections/*.tex, a bibliography, a compiled PDF, build_log.txt, citation_verification.json, self_review.json, layout_review.json
Stage 08+ Review and readiness assets under workspace/reviews/

Requirements are cumulative, and the stage that produces a class of artifact must produce it during that stage's execution — a re-run is not credited with the previous attempt's files. The cutoff is stage_execution_started_at feeding recent_in, and the rubric enforces the same rule independently in _fresh_artifact_kinds, so a Stage 07 draft cannot score on Stage 06's figures.

min_report_figures is a run_config.json field with no CLI flag, clamped to [1, 5]. It is 1 for an ordinary run and 3 for a ResearchClawBench run (BENCHMARK_MIN_REPORT_FIGURES).

Validity gates. The same function — validate_stage_artifacts (src/utils.py) — also runs the validators that ask whether a claim is warranted rather than whether output exists. Seventeen validate_* functions are reachable from it in all:

Fires at Validator Refuses when
01 validate_literature_evidence A claim cites a source_id that is not in sources.json
03+ validate_report_plan The plan has no task outputs, non-contiguous slots, a slot with no supported claim, or headline numbers without a quantity, unit and source
05+ validate_preregistration Nothing is frozen, an empirical hypothesis has no decision rule, or the manifest changed with no amendment on record
05+ validate_experimental_protocol No primary metric, planned_seeds < 1, or a baseline missing why_competent / tuning_budget
05+ validate_experiment_manifest The manifest does not parse into the declared shape
06+ validate_hypothesis_outcomes A frozen hypothesis has no verdict, a verdict adjudicates something unpreregistered, or a supported/refuted verdict cites an evidence path that does not exist
06+ validate_outcome_statistics A verdict has no n_seeds, an unrecognised dispersion_type, a single seed with no justification, or dispersion_type: none with two or more seeds
06+ validate_report_plan_sources A planned figure or headline number's source_artifact is missing or empty
06, 07 validate_validity_response The stage did not answer every adversarial finding from the one before it, with a status, a ≥40-character explanation, and evidence when it claims addressed
06 validate_round_decision A round closes as converged with no supported hypothesis and no negative_result: true
07+ validate_claim_provenance A manuscript claim is confirmatory on a hypothesis that is not supported, or cites no evidence file that exists
07 md validate_markdown_report The report is under 1200 characters, carries placeholder text, references an image that does not resolve, or publishes fewer than min_report_figures
07 md validate_report_plan_coverage A planned figure was never published or never referenced, or every slot was dropped
07 md validate_deliverables_coverage The report does not answer a demanding sentence of the task statement, or a coverage entry quotes something that is not verbatim in the task
07 md validate_report_review The AutoR-generated triage artifact is malformed
07 latex validate_layout_review The LaTeX build triage artifact is malformed
07+ validate_citation_verification The self-report has no status, a non-integer citation count, or an empty claim-coverage list

The code labels the split itself: "the scientific-validity chain, distinct from the artifact gates around it". A run can fail because a claim is unwarranted, not only because a file is absent. The 06→07 router edge is closed on top of that, in the adaptive topology only.

A gate that is honest about what it does not check. validate_preregistration compares the manifest's digest to the recorded source_digest; it does not recompute the digest of the frozen file itself. Editing preregistration.json in place, leaving the manifest alone, passes. The honest claim is that a manifest rewrite is detected, not that the frozen file is tamper-proof.

The complete gate, including every JSON schema that is parsed rather than merely counted, is in docs/stage-contract.md.

Execution model

Context is composed per consumer, not per availability. A stage's inbound block is built by render_inbound(ChannelContext(...), CHANNELS) from the sixteen typed channels in src/information_flow.py. Each channel declares produced_by, a consumed_by set of real stage slugs, and a rationale; test_every_narrowing_is_argued_for (tests/test_information_flow.py) fails a channel that withholds itself from a stage without saying why. Withholding has to be argued for, not just done.

Three narrowings, because the abstraction is not the point:

  • the artifact index skips Stages 00-02 — they produce no data, results or figures, so the index is empty noise there
  • the writing manifest reaches Stage 07 alone
  • the mutable Stage 02 hypotheses stop at 04_implementation, because the freeze at Stage 04's approval supersedes them. Before that edge was typed, the same H1 went into every prompt from Stage 05 on twice — one copy labelled editable, sitting next to the frozen one at exactly the stages where the freeze is the point.

dependency_edges() returns every (producer, consumer, channel key) triple, so the information topology can be printed and diffed rather than reconstructed from a pile of if statements. _record_inbound_channels writes the delivered channel keys per stage into the run log.

Honest scope: sixteen blocks are typed. Six more — obligations_context, intake_context_text, web_search_context, approved_memory, handoff_context, and the # What the Task Asks For block that build_prompt composes inline from format_deliverables_for_prompt — are still delivered by build_prompt itself rather than declared as channels, so each one's delivery rule lives there instead of next to a consumed_by set. Around them, compose_stage_template (src/prompt_fragments.py) assembles the stage's own instructions, the accepted-extension lists generated from the validators' constants rather than hand-copied, and the run-safety rules.

The assembled prompt is written to runs/<run_id>/prompt_cache/, per-stage session IDs to runs/<run_id>/operator_state/, and the selected CLI backend is invoked in live streaming mode. Alongside the prompt, AutoR installs an agent skill pack from src/skills/ into runs/<run_id>/.claude/skills/ — the operator's working directory — so the agent can pull long-form craft guidance when it needs it. Six skills ship today: citation-discipline, latex-repair, paper-writing, reproducibility-check, result-table, venue-checklist. A skill costs nothing in the prompts that do not use it.

Claude CLI invocation

First attempt for a stage:

claude --model <model> \
  --permission-mode bypassPermissions \
  --dangerously-skip-permissions \
  --session-id <stage_session_id> \
  -p @runs/<run_id>/prompt_cache/<stage>_attempt_<nn>.prompt.md \
  --output-format stream-json \
  --verbose

Continuation attempt for the same stage replaces --session-id with --resume.

_build_cli_command (src/operator.py) additionally inserts --mcp-config <run>/operator_state/mcp_config.json whenever the MCP web-search server is active, and --tools <tools> when a tool restriction is set.

Web search where the backend has none. Some deployments disable the agent's built-in WebSearch tool. --web-search gemini starts a stdlib JSON-RPC MCP stdio server (src/mcp_web_search.py) that exposes one tool, mcp__autor-search__web_search, backed by Gemini with grounded search, and passes it to the CLI via --mcp-config. assess_search_readiness() refuses to promise a capability the environment cannot deliver: a hard blocker (no key, no SDK) is reported before the run starts rather than discovered at Stage 01.

Important behaviour:

  • refinement attempts reuse the same stage conversation whenever possible
  • streamed agent output is shown live in the terminal
  • raw stream-json output is captured in logs_raw.jsonl
  • if resume fails, AutoR can fall back to a fresh session
  • if stage markdown is incomplete, AutoR can repair or normalize it locally before failing the stage
  • a backend that is unreachable is classified by src/backend_health.py and surfaces as run.backend_unavailable, so "the model was down" never reads as "the research failed"

Run layout

Every run lives entirely inside its own directory. The tree is build_run_paths (src/utils.py).

runs/<run_id>/
├── user_input.txt      memory.md             run_config.json
├── run_manifest.json   artifact_index.json   intake_context.json
├── obligations.json    review_policy.json    # both per-run; nothing crosses runs
├── report_plan_stamp.json                    # AutoR's copy, outside workspace/ on purpose
├── logs.txt            logs_raw.jsonl
├── prompt_cache/       operator_state/       handoff/        stages/
├── .claude/skills/     # the skill pack, pulled on demand by the agent
├── evolution/          # champion drafts, improvement_ledger.jsonl, summary.json,
│                       # stage_graph.json, routing_refusals.jsonl
└── workspace/
    ├── literature/  code/  data/  figures/  report/  writing/
    ├── bootstrap/   profile/
    ├── notes/       preregistration.json, hypothesis_manifest.json, experimental_protocol.json,
    │                report_plan.json, research_rounds.json, round_decision.json,
    │                deliberation_request.json
    ├── results/     experiment_manifest.json, hypothesis_outcomes.json
    ├── artifacts/   claim_provenance.json, deliverables_coverage.json, citation_verification.json,
    │                self_review.json, report_review.json | layout_review.json, compiled PDFs
    └── reviews/     validity_review_<stage>.json, validity_response_<stage>.json,
                     comment_ledger.json, deliberations.json, scorecard.md, panel/

evolution/ sits outside workspace/ on purpose, and the dataclass records the reason: it is "a record of how the run reached its answer, not part of the answer, and a benchmark export that swept it up would ship the losing drafts alongside the report". report_plan_stamp.json is outside workspace/ for the same class of reason: the agent must not be able to backdate its own declaration.

The only state AutoR writes outside a run directory is the cross-run archive at ~/.autor/archive (--archive, --no-archive).

Workspace semantics. literature/ reading notes, survey tables, benchmark notes · code/ runnable code, scripts, configs · data/ machine-readable datasets, manifests, processed splits · results/ metrics, predictions, ablations, plus the standardized experiment_manifest.json · report/ the markdown deliverable, report.md and the PNGs it embeds under images/ · writing/ LaTeX sources, sections, tables, bibliography · figures/ plots and paper figures · artifacts/ review JSON, build metadata, compiled PDFs, packaged deliverables · notes/ the frozen files of the validity chain plus supporting notes · reviews/ adversarial validity reviews, panel transcripts, self-measurement ledgers, the run scorecard.

Outside workspace/: memory.md is the approved free-text cross-stage memory; handoff/<slug>.md is the second free-text carrier, each approved summary trimmed to Objective / Key Results / Files Produced and sent only on a continuation attempt or when memory is still empty. Every other cross-stage edge is a typed channel or a JSON artifact. run_manifest.json is the lifecycle state that resume, redo and rollback read; prompt_cache/ holds the exact prompt of every attempt, repair, review, panel seat and crux voice.

Full file-by-file reference: docs/run-artifacts.md.

Architecture

flowchart LR
    P[rigor.py · effort.py<br/>policy: what machinery runs] --> M
    C[information_flow.py<br/>16 typed channels] --> M
    M[manager.py<br/>walks the stage graph] --> W[walk<br/>stage_graph · router]
    M --> G[gates<br/>utils · preregistration · experimental_protocol<br/>report_plan · deliverables · validity_review]
    M --> I[improvement<br/>rubric · evolution · pareto]
    M --> R[review<br/>approval_agent · review_panel · cross_reviewer<br/>obligations · review_policy · stage_comments]
    M --> S[self-measurement<br/>scorecard · archive · decisions · trials · inference]
    M --> X[execution<br/>operator · operator_codex · web_search · backend_health]
Loading
Module What it owns
src/manager.py Walks the stage graph until it reaches finish or nothing is open — plus the router call, the evolution controller, the freeze/amend seam, the validity review, the round close, the obligation ledger, the cross-review veto, the crux settlement and the inbound-channel record
src/utils.py Stage metadata, run paths, prompt assembly, markdown validation, the artifact gates and the validity-chain wiring
src/operator.py The Claude CLI adapter: stage session state, live streaming, resume fallback, MCP config, skill pack install
src/review_panel.py The deliberating panel; a blocking objection is enforced in code against its own chair
main.py CLI entry: 61 flags, start, resume, --redo-stage, --rollback-stage, the archive record and the reports that print and exit
src/report_plan.py Figures and headline numbers committed at Stage 03, stamped outside the workspace, enforced at 03, 06 and 07
src/rcb.py The ResearchClawBench adapter core: workspace layout, goal construction, report synthesis, figure publication, export
src/stage_graph.py Stages as nodes: six guarded forward edges, thirteen backward edges, a conditional terminal, a per-stage visit budget
src/archive.py Cross-run routes and edge payoffs keyed on a comparability basis; variant proposal, exploration and promotion
src/rubric.py The rigour score over a draft and the artifacts it names. Never calls a backend
src/web_search.py Gemini-backed search, readiness assessment, MCP config construction
src/deliberation.py The crux panel: four voices, each arguing against itself, resolved into an answer that names its own falsifier
src/ideation_panel.py Divergent Stage 02 proposers across five lenses, deduplicated into a candidate pool
src/evolution.py The champion ratchet: budgeted polish rounds, reverted when they do not improve, rejected on verdict drift
src/writing_manifest.py The Stage 07 inventory plus the AutoR-owned triage artifact for each output format
src/approval_agent.py The solo approval gate, its six-choice vocabulary and its unreadable-verdict fallback
src/preregistration.py Freeze, amend, adjudicate, trace
src/information_flow.py Sixteen typed information channels, each with declared readers and a written rationale
src/router.py The agent's choice among admissible moves; an off-menu choice is refused and logged
src/validity_review.py The adversarial pass after Stages 05 and 06, and the response gate that follows it
src/research_rounds.py Stages 03-06 as a repeatable round, bounded by --max-rounds
src/trials.py Paired A/B trials over archived runs, with an exact sign-flip p-value and its attainable floor
src/stage_comments.py Anchored review comments and the collateral-change diff
src/effort.py Routine vs deliberative tiering, and the concentration of the strong model
src/scorecard.py Reads all five self-measurement ledgers and says which features earned their cost
src/obligations.py What a later stage still owes; only a reviewer can discharge it
src/decisions.py "Was offered the edge and declined" — the control arm the archive's payoffs are computed against
src/cross_reviewer.py A second opinion from a different model family. Veto only, never an override
src/experimental_protocol.py Declared baselines, seeds and dispersion, fixed before the result exists
src/deliverables.py Did the run answer what the task statement actually demanded?
src/pareto.py Non-dominated drafts kept beside the champion, and the pair worth merging
src/review_policy.py Standing review rules learned from this run's own corrections
src/inference.py Exact permutation tests and attainable-p floors; derives the archive's min_observations rather than asserting it
src/rigor.py The one dial: which optional machinery a level turns on
src/backend_health.py Distinguishes "the backend is down" from "the research failed"
src/prompt_fragments.py Shared prompt blocks generated from the validators' own constants

Supporting modules: operator_codex.py and operator_protocol.py, intake.py, manifest.py, artifact_index.py, experiment_manifest.py, evidence_ledger.py, hypothesis_manifest.py, mcp_web_search.py, diagram_gen.py, bootstrap.py and project_bootstrap.py, platform/foundry.py, run_skills.py, terminal_ui.py, prompts/, skills/, and backend/ + frontend/ for the Studio. Runnable tools: tools/score_rcb_run.py (score a benchmark run with the reference judge) and tools/archive_sample_complexity.py (how many runs the archive needs before an edge becomes believable).

The full module map, the stage attempt loop and the extension points are in docs/architecture.md. The design rationale — what is new here and why — is in docs/framework.md.

Benchmarks

python rcb_agent.py --workspace <WORKSPACE> runs AutoR against a ResearchClawBench workspace with no human in the loop and exports the benchmark's deliverables (report/report.md, report/images/, code/, outputs/). Scoring is the benchmark's own rubric judge; tools/score_rcb_run.py defaults to the reference judge, gpt-5.1, which is what ResearchClawBench itself scores with.

Judge choice can move a score by more than the gap between the top and the bottom third of the leaderboard. On one identical artifact set Gemini 2.5 Flash scored 37.0 where Opus scored 20.8, a spread of 16.2; on another, Opus scored 52.6 where gpt-5.1 scored 46.0, a spread of 6.6. A number carrying the wrong judge is not a smaller number, it is an incomparable one. Quote the judge with every total.

Where AutoR lands, on all forty tasks

One attempt per task, Claude Opus executing and reviewing, judged by gpt-5.1. The three comparison agents were re-scored from their public runs under that same judge:

agent mean median max tasks scoring 0
Codex CLI 19.53 17.73 48.40 2
ResearchHarness (GPT-5.4) 15.40 10.85 45.10 1
ARIS Codex 15.02 12.65 46.90 2
AutoR 14.16 11.50 47.70 7

AutoR is last, below the bare Codex CLI it can be configured to run on top of. Eight of the forty runs shipped a 197-byte "incomplete run" stub, and the deficit is almost entirely those. Four caveats travel with the number and none can be dropped: it is single-attempt where the leaderboard aggregates the best score per (task, agent) pair; it is cross-model, since all three comparators run GPT-5.4; it is a gpt-5.1 number; and it is pre-repair — #180 and #181 closed the routes that produced the stubs, and a re-run is in flight.

The framework document's §6 is the full account, including the part that is worse than the mean: the two highest scores came from runs that halted at hypothesis generation, and across 133 stage visits the graph took one backward edge.

One task, before and after a targeted fix

Measured on Astronomy_000, reference judge gpt-5.1, before and after the export and figure-budget fixes in #147 / #149 / #153:

Checklist item Weight Before After
Text: data characterisation 0.2 48 38
Image: exclusion curve 0.3 0 48
Text: coupling limits 0.5 0 48
Weighted total 9.6 46.0

This is one task out of forty and must not be extrapolated. ResearchClawBench's published leaderboard numbers are means over all 40 tasks; a single-task score is not comparable to one. For what the reported systems actually score and which of their numbers reproduce, see docs/researchclawbench-landscape.md; for the adapter, its output contract and the export rules, see docs/researchclawbench.md.

Documentation

The docs/ directory is the reference documentation. This README is the overview; everything below is the detail behind it.

The Framework What AutoR is as a system: the implementation, every module and what it owns, what is new here, and what it contributes. Start here if you want the design, not the commands.
English Guide · 中文教程 Install, run your first project end to end, review each stage, and write feedback that actually improves output.
CLI Reference Every flag on main.py, rcb_agent.py and studio.py, defaults, what is preserved on resume, exit codes.
Configuration run_config.json, the venue registry, diagram setup, environment variables, hard-coded limits.
Run Artifacts The run directory, file by file, and the schema of every machine-readable artifact.
Stage Contract Exactly what a stage must produce to be accepted, as validate_stage_artifacts enforces it.
Recursive Self-Improvement The stage graph, routing, the rigour rubric and the champion ratchet, the cross-run archive — and the constraints that stop a scored loop from optimising toward a nicer answer.
Rigor Levels The one dial, what each level turns on, and how an explicit flag overrides it.
Effort Tiers Routine vs deliberative stages, tier promotion, and concentrating the strong model.
Review Panel The five seats, the independent round and the cross-examination that only runs on disagreement, blocking objections, --panel-models, --persona, and the solo baseline every panel run measures itself against.
Ideation Panel The five proposer lenses, Jaccard deduplication, scoring into a candidate pool, and the adoption measurement taken after the stage is approved.
Crux Deliberation When a stage may stop and escalate, the four voices, and the falsifier the resolution must name.
Anchored Comments Quoting a passage instead of refusing a stage, and the collateral-change diff.
Run Scorecard The five self-measurement ledgers and the end-of-run verdict on which flags earned their cost.
Backend Health Telling "the model was unreachable" apart from "the research failed".
Studio Guide & API The browser workspace and its complete HTTP API.
ResearchClawBench Running with no human in the loop: unattended execution, the benchmark adapter and its output contract, and Gemini-backed web search.
ResearchClawBench Landscape How EvoScientist, ARIS Codex and MIRA actually score on the benchmark, which reported numbers reproduce, and the baseline any result must be quoted against.
Architecture Layers, the module map, the stage walk, prompt assembly by typed channel, recovery, extension points.
Development Dev setup, tests, CI, conventions, and recipes for adding a stage, venue, or backend.
Troubleshooting Symptom-to-fix for the errors AutoR actually raises.
Contributing · Security · Code of Conduct How to land a change; the security model, the sandbox trade-offs and how to report a vulnerability; community expectations.

Showcase

runs/20260330_101222 is the full example run the docs work from. Run directories are gitignored, so what ships in the repository is the artifacts lifted out of it, under assets/.

What the run produced What it demonstrates
example_paper.pdf A compiled manuscript inside a larger research package
Executable research code The run is not a writing pipeline
Machine-readable datasets and result files Claims are backed by inspectable experiment outputs
Real figures used in the package Publication-style visuals, not placeholders
Review and dissemination materials The run continues past writing into release readiness

AGSNv2 reached 36.21 ± 1.08 on Actor, and the run preserved the full human approval trail.

Accuracy Comparison
Example accuracy figure
Ablation + Actor Results
Example ablation figure
Two-Layer Narrative
Two-layer narrative figure

Research output gallery

Four artifact-backed runs, two pages each: the framing page and an evidence page.

Output 1
A complete end-to-end AutoR run.
Output 1 page 1
Page 1
Output 1 evidence page
Evidence Page
Output 2
Do More Experts Help? A parameter-matched MoE-LoRA study.
Output 2 page 1
Page 1
Output 2 evidence page
Evidence Page
Output 3
Attention Sink Onset in Tiny Transformers A controlled factorial study.
Output 3 page 1
Page 1
Output 3 overview page
Overview Page
Output 4
HSOD: Harmonic Spectral Operator Decomposition A stability-focused time-series study.
Output 4 page 1
Page 1
Output 4 analysis page
Analysis Page

AutoR terminal UI

The shot above is the real terminal UI: colored stage panels, parsed backend event streams, display-width-aware wrapping, keyboard-selectable menus, and a Stage 00 clarification flow that asks its questions one at a time.

News

  • 2026-08-11 — Documentation rebuilt against the code: docs/framework.md added, and every count in this README re-derived from a named symbol.
  • 2026-08-08 — The report plan (src/report_plan.py) and the task-deliverables contract (src/deliverables.py): a run now commits at Stage 03 to the figures and headline numbers its report will carry, and Stage 07 is refused if the report does not answer what the task statement demanded.
  • 2026-08-06Recursive self-improvement is the default. The eight stages became a directed graph with a router that must justify its move and is refused off-menu; every valid draft is scored and held to a champion ratchet; the cross-run archive keys every fitness comparison on the stages a run actually measured. Opt out with --stage-graph linear, --routing off, --no-evolve, --no-archive.
  • 2026-08-04--rigor {fast,standard,thorough,max}: one dial over effort tiers, crux deliberation, the ideation panel and the review panel, ordered by what each costs and what evidence there is for it.
  • 2026-06-02--codex-sandbox danger-full-access for runs that intentionally need remote GPU or SSH execution. Codex still defaults to workspace-write.
  • 2026-04-20--full-auto: the manual approval gate can be replaced by a strict reviewer agent.
  • 2026-04-19 — AutoR Studio merged into main: a local browser workspace over the same run directories.

Limits

Things the mechanisms above do not close. Each is also the next thing worth building, named at the code that would have to change.

  • A crashed adversarial pass is indistinguishable from a clean result. _write_review(..., failed=True) records reviewer_failed: true (src/validity_review.py) and no production code reads that flag. Zero findings and a reviewer that never returned are the same input to validate_validity_response: nothing owed, gate open.
  • Attribution stops at the log. _record_inbound_channels writes which channels reached each stage, but RunRecord (src/archive.py) has no channel field, so "this edge helped" cannot yet become "this information helped".
  • The frozen preregistration is not checked against itself. validate_preregistration compares the manifest's digest to the recorded source_digest and never recomputes the digest of the frozen file. Editing preregistration.json in place passes; deleting the manifest silences the check entirely.
  • Standing rules and obligations never reach a panel seat. Both are injected only into the solo reviewer's prompt. Under --review-panel, no seat is shown the accumulated rules and no seat is asked whether an inherited obligation was met — and because neither the seat nor the chair prompt asks for carry_forward, a panel run essentially never creates an obligation either.
  • The cross-model veto is unreachable from main.py. --cross-review is declared and parsed there, but resolve_cross_reviewer is called only from rcb_agent.py. The feature is live on the benchmark path only.
  • The validity chain is bypassable in unattended mode, and past the skip budget it is bypassed deliberately. A stage that burns its attempts against the gate is auto-skipped, up to --max-auto-skips (default 3). Past that, _route_to_deliverable routes to the writing stage around the validity guard rather than exiting with nothing — the rule and its cost are §2.7. What the run owes instead is disclosure: every stage the route stepped over is named in the report. Whether a banner is enough is a fair thing to push on.
  • No mechanism here has evidence that it improves a research output. src/trials.py is the apparatus built to produce that evidence; not one paired trial has been run. The scorecard says when a feature did not change a decision in this run, which is a different and weaker claim.
  • Most of the recursion is opt-in or partial. --max-rounds defaults to 1, so a round that asks to go back is recorded with acted_on: false and the run continues to writing anyway; the archive steers only under --archive-steer; and REVIEWED_STAGE_NUMBERS = (5, 6), so nothing attacks Stage 07 or 08.
  • Studio does not route. Its lazy-resume approve path picks the next stage by stage number and never consults the router, so graph routing is a CLI capability today.

Intentionally out of scope: generic multi-agent orchestration, database-backed runtime state, concurrent stage execution, heavyweight platform abstractions, dashboard-first productization.

Contributing

Bug reports, feature requests, documentation fixes, and shared runs are all welcome. Setup is one clone and one command — AutoR's runtime imports nothing outside the standard library and there is no build step. Only the optional Gemini-backed paths (--web-search gemini, --research-diagram, the cross-model reviewer) need google-genai:

git clone https://github.com/tangxiangru/AutoR.git
cd AutoR
python -m unittest discover -s tests -p "test_*.py"    # 1826 tests, ~69s, no dependencies

Read CONTRIBUTING.md before opening a pull request, and docs/development.md before changing code. Security issues go through SECURITY.md, not a public issue. Contributions are assigned to the copyright holder under Section 6 of the LICENSE, and running AutoR requires written permission — see below.

License

AutoR is proprietary software. It is not open source.

Copyright © 2026 Xiangru Tang. All rights reserved. Licensed under the AutoR Proprietary License 1.0 (LicenseRef-AutoR-Proprietary-1.0) — see LICENSE for the full terms and NOTICE for the summary.

This repository is public so that AutoR's design and behaviour can be examined, cited, and discussed. Publication is not a license. No right to use, run, copy, modify, fork, or redistribute the Software is granted by its availability here.

Permitted Viewing this repository. Quoting short excerpts for academic citation, commentary, review, teaching, or news reporting, with attribution.
Requires written permission Any use at all — running AutoR, deploying it, modifying it, forking it, redistributing it, or using it to train or evaluate a model.
Not granted Any patent license. Any trademark license to the AutoR name or marks.
Contributions Assigned to the copyright holder with a relicensable right (LICENSE §6).

To request permission, open an issue or contact the copyright holder directly. Permission applies only to the specific use, party, and period stated in writing.

About

AI handles execution, humans own the direction, and every run becomes an inspectable research artifact on disk.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

804 stars

Watchers

6 watching

Forks

Releases

Packages

Contributors

Languages