npx skills add ...
npx skills add factory-ai/factory-plugins --skill autoresearch
npx skills add factory-ai/factory-plugins --skill autoresearch
Autonomous experiment loop for optimization research. Use when the user wants to: - Optimize a metric through systematic experimentation (ML training loss, test speed, bundle size, build time, etc.) - Run an automated research loop: try an idea, measure it, keep improvements, revert regressions, repeat - Set up autoresearch for any codebase with a measurable optimization target Implements the autoresearch pattern with MAD-based confidence scoring, git branch isolation, and structured experiment logging.
Autonomous experiment loop: try ideas, keep what works, discard what doesn't, never stop.
You are running an autonomous optimization loop. Your job is to systematically improve a measurable metric by making changes, running experiments, and keeping only the improvements. You maintain structured state files so that any session — including a fresh one with no memory — can resume exactly where you left off.
If the user is asking you to do this and you are not currently in mission mode, suggest that they might want to run this inside a mission (/enter-mission) for better progress tracking, milestone validation, and multi-session continuity. Don't block on it — just mention it once during setup.
If you are already in mission mode, invoke the mission planning skills first (mission-planning and define-mission-skills) before diving into this skill's procedure. Use the mission system's planning, decomposition, and worker design to structure the autoresearch work — then combine that guidance with this skill's experiment loop procedure. This skill defines how to run experiments; the mission system defines how to plan, track, and validate them.
Before the loop starts, you need to establish the experiment.
Ask the user (or infer from context) for:
uv run train.py, pnpm test, pnpm build && du -sb dist)val_bpb, unitless, lower is better)Read the source files thoroughly. Understand the workload deeply before writing anything.
Create three files:
autoresearch.mdThe living research document. A fresh agent with no context should be able to read this file and run the loop effectively. Invest time making it excellent.
autoresearch.shBash script (set -euo pipefail) that: pre-checks fast (syntax errors in <1s), runs the benchmark, and outputs structured METRIC name=value lines to stdout. Keep the script fast.
For fast, noisy benchmarks (< 5s), run the workload multiple times inside the script and report the median. Slow workloads (ML training, large builds) don't need this.
Example:
autoresearch.checks.sh (optional)Only create this when the user's constraints require correctness validation (e.g., "tests must pass", "types must check"). Bash script (set -euo pipefail) for backpressure checks.
Initialize the experiment log:
Commit all state files:
Run the benchmark and record the baseline result:
Parse the METRIC lines from the output, then log the baseline as a keep:
This is experiment #1 — it establishes the starting point for all future comparisons.
LOOP FOREVER. Never ask "should I continue?" — the user expects autonomous work. Only stop when:
Read autoresearch.md (especially "What's Been Tried") and autoresearch.ideas.md (if it exists) to pick the next hypothesis. Think about what the data tells you. The best ideas come from deep understanding, not random variations.
Edit the files in scope. Keep changes focused — one hypothesis per experiment.
Execute the benchmark:
Capture the full output. Parse METRIC name=value lines from the output.
If the run crashes or times out, log it as a crash and revert.
If autoresearch.checks.sh exists and the benchmark passed, run it:
If checks fail, log as checks_failed and revert.
Compare the primary metric against the current best (or baseline if no keeps yet) using the helper script:
This outputs whether to keep or discard, the confidence score, and delta from baseline.
Decision rules:
keepdiscardkeep (removing code for same perf is a win)discardOn keep:
Log to JSONL first (so the entry is included in the commit):
Then commit all changes (including the JSONL entry):
On discard/crash/checks_failed:
Log to JSONL first (before reverting, so the entry is preserved):
Then revert changes, backing up state files so git clean -fd doesn't destroy them:
After every few experiments (or after significant findings), update the "What's Been Tried" section in autoresearch.md. Include:
When you discover promising but deferred optimizations, append them as bullet points to autoresearch.ideas.md. Don't let good ideas get lost. Prune stale or tried entries.
Go back to step 1.
| File | Format | Purpose |
|---|---|---|
autoresearch.jsonl | JSON Lines | Append-only experiment log. One JSON object per line. |
autoresearch.md | Markdown | Living research document. Objective, what's been tried, current best. |
autoresearch.ideas.md | Markdown | Hypothesis backlog. Bullet points of promising ideas to try. |
autoresearch.sh | Bash | Benchmark script. Outputs METRIC name=value lines. |
autoresearch.checks.sh | Bash | Optional correctness checks (tests, types, lint). |
Each line in autoresearch.jsonl is either a config header or an experiment result:
Config header (first line, or on re-init):
Experiment result:
Always record ASI with every experiment. At minimum: {"hypothesis": "what you tried"}. On discard/crash, also include rollback_reason and next_action_hint. Add any other key/value pairs that capture what you learned — dead ends, surprising findings, error details, bottlenecks.
ASI is the only structured memory that survives reverts. Without it, future iterations waste time re-discovering the same dead ends.
After 3+ experiments, the helper script computes a confidence score using Median Absolute Deviation (MAD):
| Confidence | Meaning |
|---|---|
| >= 2.0x | Improvement is likely real |
| 1.0-2.0x | Above noise but marginal |
| < 1.0x | Within noise — consider re-running to confirm |
The score is advisory — it never auto-discards. If confidence is below 1.0x, consider re-running the same experiment to confirm before keeping.
Droid sessions have finite context. To handle this gracefully:
autoresearch.md with current findings, commit state files, and stop. The next session reads the files and continues.autoresearch.md, autoresearch.jsonl, and git log --oneline -20 to understand where things stand. Check current status:When the experiment loop ends (termination condition met, user interrupts, or context exhausted), finalize the results into clean, reviewable branches. This is the last phase of an autoresearch session.
Review the git log for actual commits:
Group kept experiments into logical changesets. Each group should:
Present the proposed grouping to the user for approval:
Wait for user confirmation before proceeding. In mission worker mode, proceed with the best grouping without waiting for confirmation.
If groups share files, resolve before creating branches:
Groups must not share files — each branch must be independently mergeable. If all changes touch the same file and can't be separated, create a single finalized branch with all improvements combined.
For each group:
For each finalized branch, run the benchmark to confirm the improvement holds, run any checks if applicable, and verify it merges cleanly with main.
Present a summary to the user:
The original experiment branch is always preserved — finalization creates new branches.
When running as a mission worker, the feature description specifies the optimization goal, termination condition, files in scope, and constraints. Read it carefully, follow the same loop procedure above, and respect the termination condition. When the condition is met, run finalization and report results in the handoff.
# Autoresearch: <goal>
## Objective
<Specific description of what we're optimizing and the workload.>
## Metrics
- **Primary**: <name> (<unit>, lower/higher is better) — the optimization target
- **Secondary**: <name>, <name>, ... — independent tradeoff monitors
## How to Run
`./autoresearch.sh` — outputs `METRIC name=number` lines.
## Files in Scope
<Every file the agent may modify, with a brief note on what it does.>
## Off Limits
<What must NOT be touched.>
## Constraints
<Hard rules: tests must pass, no new deps, etc.>
## Termination
<When to stop: experiment count, time budget, target metric, or run until interrupted.>
## What's Been Tried
<Update this section as experiments accumulate. Note key wins, dead ends,
and architectural insights so the agent doesn't repeat failed approaches.>#!/bin/bash
set -euo pipefail
# Pre-check: syntax validation
python3 -c "import ast; ast.parse(open('train.py').read())" 2>&1 || { echo "SYNTAX ERROR"; exit 1; }
# Run the workload
output=$(uv run train.py 2>&1)
# Extract and output metrics
val_bpb=$(echo "$output" | grep -oP 'val_bpb=\K[0-9.]+' | tail -1)
echo "METRIC val_bpb=$val_bpb"#!/bin/bash
set -euo pipefail
pnpm test --run --reporter=dot 2>&1 | tail -50
pnpm typecheck 2>&1 | grep -i error || truepython3 autoresearch_helper.py init --jsonl autoresearch.jsonl --name '<goal>' --metric-name '<metric_name>' --direction <lower|higher>git add autoresearch.md autoresearch.sh autoresearch.jsonl
git commit -m "autoresearch: initialize experiment session"bash autoresearch.shpython3 autoresearch_helper.py log --jsonl autoresearch.jsonl \
--commit $(git rev-parse --short=7 HEAD) \
--metric <baseline_value> \
--status keep \
--description "baseline" \
--asi '{"hypothesis": "baseline measurement"}'timeout 600 bash autoresearch.shtimeout 300 bash autoresearch.checks.shpython3 autoresearch_helper.py evaluate --jsonl autoresearch.jsonl --metric <value> --direction <lower|higher>python3 autoresearch_helper.py log --jsonl autoresearch.jsonl \
--commit $(git rev-parse --short=7 HEAD) \
--metric <value> \
--status keep \
--description "<what was tried>" \
--asi '{"hypothesis": "<what you tried>"}' \
# --metrics '{"compile_us": <value>, "render_us": <value>}' # optional secondary metrics
--direction <lower|higher>git add -A
git commit -m "<description>
Result: {\"status\": \"keep\", \"<metric_name>\": <value>}"python3 autoresearch_helper.py log --jsonl autoresearch.jsonl \
--commit "0000000" \
--metric <value_or_0> \
--status <discard|crash|checks_failed> \
--description "<what was tried>" \
--asi '{"hypothesis": "<what you tried>", "rollback_reason": "<why it failed>"}' \
# --metrics '{"compile_us": <value>, "render_us": <value>}' # optional secondary metrics
--direction <lower|higher># Backup state files
cp autoresearch.jsonl autoresearch.jsonl.bak 2>/dev/null || true
cp autoresearch.md autoresearch.md.bak 2>/dev/null || true
cp autoresearch.ideas.md autoresearch.ideas.md.bak 2>/dev/null || true
# Revert all changes
git checkout -- .
git clean -fd 2>/dev/null
# Restore state files
cp autoresearch.jsonl.bak autoresearch.jsonl 2>/dev/null || true
cp autoresearch.md.bak autoresearch.md 2>/dev/null || true
cp autoresearch.ideas.md.bak autoresearch.ideas.md 2>/dev/null || true
rm -f autoresearch.jsonl.bak autoresearch.md.bak autoresearch.ideas.md.bak{"type": "config", "name": "...", "metricName": "...", "metricUnit": "...", "bestDirection": "lower|higher"}{
"run": 1,
"commit": "abc1234",
"metric": 1.234,
"metrics": {"compile_us": 4200, "render_us": 9800},
"status": "keep|discard|crash|checks_failed",
"description": "what was tried",
"timestamp": 1711600000000,
"segment": 0,
"confidence": 2.1,
"asi": {"hypothesis": "...", "rollback_reason": "...", "next_action_hint": "..."}
}python3 autoresearch_helper.py status --jsonl autoresearch.jsonlpython3 autoresearch_helper.py summary --jsonl autoresearch.jsonlgit log --oneline --stat $(git merge-base HEAD main)..HEADGroup 1: "Reduce model depth from 8 to 6"
Files: train.py (DEPTH, HEAD_DIM, N_EMBED)
Metric improvement: val_bpb 1.15 -> 1.08 (-6.1%)
Experiments: #3, #7, #12
Group 2: "Switch to cosine LR schedule"
Files: train.py (lr_schedule, warmup_steps)
Metric improvement: val_bpb 1.08 -> 1.05 (-2.8%)
Experiments: #15, #18merge_base=$(git merge-base HEAD main)
git checkout -b autoresearch/finalize/<group-name> $merge_base
git checkout autoresearch/<session-branch> -- <file1> <file2> ...
git commit -m "<group description>
Autoresearch results:
- Metric: <name> improved from <baseline> to <best> (<delta>%)
- Confidence: <score>x noise floor
- Experiments: <count> total, <kept> kept"Created 2 clean branches from 20 experiments:
autoresearch/finalize/reduce-depth
val_bpb: 1.15 -> 1.08 (-6.1%)
Ready for review
autoresearch/finalize/cosine-schedule
val_bpb: 1.08 -> 1.05 (-2.8%)
Ready for review
Original experiment branch preserved: autoresearch/<session-branch>