autoresearch — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited autoresearch (Agent Skill) and scored it 92/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 2 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 2 flagged
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
An autonomous optimization loop where Claude edits code, runs a benchmark, measures a metric, and keeps improvements or reverts — repeating forever until stopped.
The loop is simple: edit → commit → run → measure → keep or discard → repeat.
git revert.autoresearch.jsonl (append-only log) and autoresearch.md (living session document).When the user triggers autoresearch, gather the following (ask if not provided). If $ARGUMENTS is provided, use it as the optimization goal:
$ARGUMENTS if providedpnpm test, uv run train.py)lower or higher is better)Optionally check for .claude/autoresearch-ai-plugin.local.md in the project root for persistent configuration:
---
enabled: true
max_iterations: 50
working_dir: "/path/to/project"
benchmark_timeout: 600
checks_timeout: 300
---
# Autoresearch Configuration
Additional context or notes for this project's autoresearch setup.enabled — whether autoresearch is active (default: true)max_iterations — stop after N experiments (default: 0 = unlimited)working_dir — override directory for experiment files (default: current directory)benchmark_timeout — benchmark timeout in seconds (default: 600)checks_timeout — correctness checks timeout in seconds (default: 300)If the file doesn't exist, use defaults. The file should be added to .gitignore (.claude/*.local.md).
Then execute these setup steps:
git checkout -b autoresearch/<goal>-<date>git revert will fail if autoresearch.jsonl is tracked, and if autoresearch.md (which you update mid-loop) is tracked, a broad experiment commit could sweep it in and a later revert would erase your learnings: printf '%s\n' autoresearch.jsonl autoresearch.md autoresearch.ideas.md run.log >> .gitignore
git add .gitignore && git commit -m "autoresearch: add session files to gitignore"autoresearch.md — the session document (see examples/autoresearch.md). It is gitignored (living state), so it is never committed or reverted.autoresearch.sh — the benchmark script (see examples/autoresearch.sh)autoresearch.checks.sh — correctness checks (tests, lint, types)autoresearch.sh and (if present) autoresearch.checks.sh. These never change after setup (the file-protection hook enforces it), so keeping them in history is safe and aids reproducibility. Do not commit autoresearch.md/.jsonl/.ideas.md (gitignored above).bash autoresearch.shMETRIC name=value)autoresearch.jsonl: write the "type":"config" header, then a {"type":"status","state":"running",...} marker, then the baseline resultLOOP FOREVER. Never ask "should I continue?" — just keep going.
The user might be asleep, away from the computer, or expects you to work indefinitely. If each experiment takes ~5 minutes, you can run ~12/hour, ~100 overnight. The loop runs until the user interrupts you, period.
Each iteration:
0. Check stopping conditions: run `bash ${CLAUDE_SKILL_DIR}/scripts/session-status.sh --file autoresearch.jsonl [--max-iterations N]` and act on its VERDICT (concluded|max_iterations|wall|backstop|continue). Only continue if VERDICT=continue. (See "Stopping Conditions" for what the script computes and the spec it implements.)
1. Read current git state and autoresearch.md
2. Choose an experimental change (informed by past results and ASI notes)
3. Edit files in scope
4. git add <files-in-scope only> && git commit -m "experiment: <description>"
(Commit ONLY the files in scope — never autoresearch.md/.jsonl/.ideas.md; they are gitignored so a revert can't erase them.)
5. Run: bash autoresearch.sh > run.log 2>&1
6. Parse METRIC lines from output
7. If autoresearch.checks.sh exists, run it (separate timeout, default 300s)
8. Decide: keep or discard
9. Log result to autoresearch.jsonl (include ASI annotations)
10. If discard/crash: git revert $(git rev-parse HEAD) --no-edit
11. Update autoresearch.md with learnings (every few experiments)
12. Repeatkeep (commit stays, branch advances)discard (run git revert $(git rev-parse HEAD) --no-edit)discard (revert, note the failure in ASI)keep (removing complexity is a win)discard even if primary improved (e.g., 1% speed gain but 10x memory usage)autoresearch.ideas.md if it exists. Re-read source files for new angles. Try combining previous near-misses. Try more radical changes. Read any papers or docs referenced in the code.All else being equal, simpler is better. Weigh complexity cost against improvement magnitude:
If the user sends a message while the loop is running:
The consecutive-failure streak is the key signal, and it is defined so it can be recomputed from autoresearch.jsonl alone (never from memory — you may have been context-reset):
Starting from the last line ofautoresearch.jsonl, count backward the run entries whosestatusisdiscardorcrash. Stop counting (streak resets to 0) at the firstkeep, the firstchecks_failed, or the first run belonging to an earlier `segment`.
autoresearch.ideas.md for untried ideas.Status: WALL). Starting a genuinely different direction usually means a new segment, which resets the streak.The > 8 boundary is deliberately high so a fruitful overnight loop with the occasional rough patch is never aborted — only a truly stuck segment trips it.
Compute these with bash ${CLAUDE_SKILL_DIR}/scripts/session-status.sh --file autoresearch.jsonl [--max-iterations N] at the top of every iteration (loop step 0) — it emits SEGMENT, SEGMENT_RUNS, STREAK, LAST_STATUS, BACKSTOP_ACK, and a single VERDICT, all recomputed from `autoresearch.jsonl` and scoped to the current segment (delimited by config headers), so they survive context resets. The rules the script implements — and which you follow directly if the script is unavailable:
status in keep|discard|crash|checks_failed) whose segment equals the current segment. Count the matching entries directly — do not use the run number for this, since run is a session-wide sequential counter that keeps climbing across segments (segment 1 might start at run 51), so it would over-count a fresh segment.max_iterations > 0 (from .claude/autoresearch-ai-plugin.local.md) and the current-segment run count ≥ it → stop (Status: DONE (max_iterations)). 0 or absent = unlimited.max_iterations is 0/unlimited, pause for a human check-in every 200 current-segment runs (at counts 200, 400, 600, …). On reaching a boundary B (a multiple of 200) for which the log holds no acknowledgement, append a non-terminal marker recording it — {"type":"status","state":"running","backstop_ack":B,...} — and stop this dispatch to ask the user before continuing. Two properties make the continuation coherent: the status stays running (so a confirmed relaunch is not blocked by the resume guard), and the backstop_ack:B record is durable in the log — so the next dispatch sees B is already acknowledged, does not re-pause at it, and runs on to the next un-acknowledged boundary. The pause rule is precisely: pause at boundary `B` only if no status record already carries `backstop_ack` ≥ `B`. An explicit max_iterations replaces this backstop with a hard stop.Status: WALL).{"type":"status",...} record in the log is not running (e.g. cancelled/done), the session is over; do not silently resume it.Benchmark scripts output metrics as structured lines:
METRIC total_time=4.23
METRIC memory_mb=512
METRIC val_bpb=1.042Parse these with the helper script at ${CLAUDE_SKILL_DIR}/scripts/parse-metrics.sh:
bash autoresearch.sh 2>&1 | bash ${CLAUDE_SKILL_DIR}/scripts/parse-metrics.shBeyond the primary metric, output additional METRIC lines for tradeoff monitoring:
METRIC total_ms=4230 # primary
METRIC compile_ms=1200 # secondary — helps identify bottlenecks
METRIC memory_mb=512 # secondary — monitors resource usage
METRIC cache_hit_rate=0.85 # secondary — instrumentation dataSecondary metrics are tracked in the JSONL log and help guide future experiments, but they rarely affect keep/discard decisions (only discard if a catastrophic secondary regression accompanies a marginal primary improvement).
Output instrumentation data — phase timings, error counts, cache rates, domain-specific signals. This data guides the next iteration and helps identify where optimization effort should focus.
ASI is structured annotation per experiment that survives reverts. When code changes are discarded, only the description and ASI remain — making them the only structured memory of what happened.
Record ASI for every experiment:
{
"hypothesis": "Reducing loop iterations by breaking early",
"result": "Marginal speedup but code readability suffered",
"next_action_hint": "Try vectorization instead of loop unrolling",
"bottleneck": "Memory bandwidth on L2 cache misses"
}ASI fields are free-form — use whatever keys are useful:
hypothesis — what you expectedresult — what actually happenednext_action_hint — guidance for the next experimentbottleneck — identified performance bottleneckerror_details — crash/failure diagnostics{"type":"config","name":"Optimize unit test runtime","metricName":"total_ms","metricUnit":"ms","bestDirection":"lower"}Immediately after the config header, append a status record, and append a new one whenever the session's lifecycle state changes:
{"type":"status","state":"running","timestamp":1700000000}state — one of: running (session active — this also covers a backstop check-in, which pauses for confirmation without concluding), done (concluded normally, e.g. max_iterations reached or the user ended an unbounded run), cancelled (user stopped it), wall (consecutive-failure wall), blocked (needs user input).backstop_ack (optional, integer) — on a running marker only: records the 200-run backstop boundary the loop paused at for a check-in, so a later dispatch doesn't re-pause at the same boundary. See the backstop rule under Stopping Conditions.running at setup; write done/wall/blocked when the loop stops; write cancelled in the Cancel procedure.status record is running (or there is no status record — legacy logs). If the last status is cancelled/done/wall, do not silently resume — a concluded session stays concluded until the user explicitly restarts it.run number and is skipped by run-counting. It is agent-written, not runtime-enforced, so a hard crash could leave a stale running — acceptable, since the alternative (treating any log as active forever) is worse.Each experiment appends one JSON line:
{"run":5,"commit":"abc1234","metric":4230,"metrics":{"compile_ms":1200,"memory_mb":512},"status":"keep","description":"parallelized test suites","timestamp":1700000000,"segment":0,"confidence":2.3,"asi":{"hypothesis":"parallel tests reduce wall time","next_action_hint":"try worker pool size tuning"}}Fields:
run — experiment number (1-indexed, sequential)commit — short git commit hash (7 chars)metric — primary metric valuemetrics — secondary metrics dict (optional)status — one of: keep, discard, crash, checks_faileddescription — brief description of what was triedtimestamp — Unix timestamp (seconds)segment — session segment index (0-based, incremented when optimization target changes)confidence — MAD-based confidence score (null if < 3 experiments)asi — Actionable Side Information dict (optional, omit if empty)Use ${CLAUDE_SKILL_DIR}/scripts/log-experiment.sh to append entries:
bash ${CLAUDE_SKILL_DIR}/scripts/log-experiment.sh \
--run 5 \
--commit "$(git rev-parse --short HEAD)" \
--metric 4230 \
--status keep \
--description "parallelized test suites" \
--metrics '{"compile_ms":1200,"memory_mb":512}' \
--segment 0 \
--confidence 2.3 \
--asi '{"hypothesis":"parallel tests reduce wall time"}'Valid statuses: keep, discard, crash, checks_failed
Keep the log analyzable and the run-counts/streaks trustworthy:
checks_failed regardless of how good the metric looks. (This also keeps the failure streak honest: checks_failed breaks a discard/crash streak, so mislabeling it as keep would hide a stuck segment.){"type":"status",...} marker is the one intentional non-run append; it is expected, not a violation.)metrics, include it in every later run; if it is genuinely unavailable for a run, say so in ASI rather than silently dropping it.When the optimization target changes mid-session (different benchmark, metric, or workload):
autoresearch.jsonl with the updated targetThis allows a single session to evolve — e.g., first optimize compilation speed, then switch to runtime performance.
If autoresearch.jsonl exists in the working directory (the authoritative session state):
autoresearch.jsonl to see all past experiments, current best, ASI annotations, and the last `{"type":"status",...}` record. If that last status is cancelled/done/wall, the session is concluded — do not silently resume; confirm with the user first. Only resume when the last status is running (or there is no status record — a legacy log).autoresearch.md for full context (goal, metrics, files, constraints, learnings). If it is missing (it is gitignored, so a fresh clone won't have it), reconstruct it from the JSONL config header and git log.autoresearch.ideas.md if it exists — prune stale entries, experiment with remaining ideasAfter 3+ experiments, assess whether improvements are real or noise:
Record confidence on each experiment result in the JSONL log. When confidence is low, consider:
autoresearch.sh and reporting the medianSee references/confidence-scoring.md for detailed methodology.
| File | Purpose | Created by |
|---|---|---|
autoresearch.md | Living session document — goal, metrics, scope, learnings | Setup phase |
autoresearch.sh | Benchmark script — outputs METRIC name=value lines | Setup phase |
autoresearch.checks.sh | Optional correctness checks (tests, lint, types) | Setup phase |
autoresearch.jsonl | Append-only experiment log (survives restarts) | First experiment |
autoresearch.ideas.md | Optional backlog of ideas to try | Anytime |
.claude/autoresearch-ai-plugin.local.md | Optional persistent configuration (max_iterations, working_dir, timeouts) | User-provided |
When the user asks to cancel or stop autoresearch:
autoresearch.jsonl to count total experiments and results{"type":"status","state":"cancelled","timestamp":...} marker to autoresearch.jsonl — this is what makes the session stay stopped instead of being silently resumed later.autoresearch.jsonl or autoresearch.md — they contain valuable history.claude/autoresearch-ai-plugin.local.md — it is the user's persistent per-project config; the cancelled marker (not config removal) is what ends the session/run-autoresearch (which will see the cancelled marker and ask before restarting)When the user asks about autoresearch status or progress:
autoresearch.jsonl exists — if not, report "No active session"autoresearch.md for the goal and primary metricautoresearch.jsonl to compute: total runs, kept/discarded/crashed counts, baseline vs best, improvement percentage, confidence score, and the lifecycle state — the state of the last {"type":"status",...} record (running/done/cancelled/wall/blocked)running) or concluded~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.