| name | scalar-loop |
| description | Run deterministic autonomous iteration against a local git repo. Use when the user wants Claude Code to optimize a metric (bundle size, benchmark, test coverage, lighthouse score, etc.) over many iterations with structural guarantees against cheating. Triggers on phrases like "autoresearch", "scalar-loop", "iterate on this metric", "keep trying to improve X", "run the optimizer", or any request to repeatedly propose-verify-keep-or-revert a code change. Requires the `scalar-loop` CLI and a `scalar-loop.yaml` config at the repo root. |
scalar-loop
A deterministic autonomous iteration engine. This skill is a thin shell
around the scalar-loop Python CLI — the CLI owns all control flow. Your
job as the agent is only to propose edits when the loop asks for them.
Why this skill exists
Most "autoresearch" Claude Code skills are prompts-as-a-product: the
agent is told to loop, told not to touch the verifier, told to stop when
the metric plateaus. In practice an LLM will drift under pressure —
shorten the loop, edit the benchmark, declare victory on noise.
scalar-loop moves those guarantees into Python code:
- Scope enforcement: a pre-commit diff check rejects any file path
outside the declared scope globs. Not a prompt nudge — a mechanical
rejection.
- Harness integrity: sealed files (verify script, tests, manifest)
are SHA-256 pinned. Tampering is detected and the iteration rejected.
- Precondition gate: the loop refuses to start on
main/master,
on a dirty tree, or with a metric command that doesn't produce a
number. Each failure is named explicitly.
- Deterministic commit/revert: the loop stages only in-scope files
(never
git add -A), commits with a structured message, runs verify,
runs guard, and either keeps or git reset --hard HEAD~1s. Uncommitted
trash is stashed (recoverable) rather than git clean -fd'd.
When to use
Use scalar-loop when the user has:
- A mechanical metric they can extract with a shell command
(number out on stdout — bundle size, benchmark time, val_bpb, accuracy,
coverage percent, lighthouse score).
- A guard that catches regressions (test suite exit code, or a
second metric that must not drop past baseline).
- A bounded scope of files that optimization is allowed to touch.
- A git repo on a feature branch.
If any of those are missing, do not silently start anyway. Tell the user
what's missing and stop.
When NOT to use
- The user wants a one-shot change — use Edit directly.
- There's no mechanical metric ("make this code cleaner" — no).
- The target involves subjective output (creative writing, UX copy).
- The target is
main or a production branch.
- The user is already mid-debug on an unrelated change.
How to run
Step 1 — preflight
Check that scalar-loop is installed:
scalar-loop version
If not present, tell the user:
You need to install scalar-loop first. It's not on PyPI yet — install
from source: git clone https://github.com/mandar-karhade/scalar-loop,
then uv venv && uv pip install -e ".[dev]" inside that repo, and make
sure the resulting scalar-loop binary is on your PATH.
Step 2 — config
Look for scalar-loop.yaml at the repo root. If missing, help the user
write one. The schema:
goal: "One sentence describing what to improve"
scope:
- "src/**/*.ts"
- "src/**/*.tsx"
metric:
command: "npm run build -s && wc -c < dist/bundle.js"
direction: lower
guard:
command: "npm test"
mode: pass_fail
harness:
sealed:
- "package.json"
- "tests/**"
- "scripts/verify.sh"
max_iterations: 50
plateau_patience: 15
timeout_seconds: 600
agent:
command: "claude -p"
timeout_seconds: 1800
Step 3 — check preconditions
scalar-loop check
This prints a PASS/FAIL line for each precondition. If anything fails,
do not proceed. Surface the failures to the user verbatim.
Step 4 — run
scalar-loop run
Iterations stream to the terminal. Full state is in .scalar-loop/:
results.tsv (one row per iteration), summary.json (final report),
harness.lock (the SHA-256 manifest).
Step 5 — report
When the run completes, read .scalar-loop/summary.json and tell the user:
- Why the loop stopped (
termination_reason)
- Baseline vs final metric and delta
- Number of accepted commits
- The best commit SHA (so they can check it out / diff it)
- Path to
results.tsv if they want the full iteration trace
Do NOT invent numbers from your own memory of what happened. The JSON
file is the source of truth.
Your role inside an iteration
When scalar-loop invokes the agent (you, via claude -p), you receive a
prompt with:
- The goal
- The current baseline and best metric so far
- The scope patterns you must not violate
- The sealed files you must not touch
- The verify and guard commands that will run after you finish
- The last ~20 commits for context
You make edits and exit. You do not:
- Commit changes (the loop commits)
- Run the verify or guard command (the loop runs them)
- Decide whether your change was good (the loop decides)
- Ask whether to keep going (the loop decides)
- Edit sealed files (detected, reverted)
The single extra signal scalar-loop respects from your stdout is the
SCALAR_LOOP_GIVE_UP: <reason> line. Use it only when you've genuinely
run out of ideas and further iterations would be wasted — for example,
the remaining gap requires a change scalar-loop's scope won't allow.
Failure modes — read these before running
- Precondition failure → scalar-loop exits non-zero. Do not retry
blindly; show the user the report.
- Harness tamper → iteration rejected with
tampered status. Tell
the user if it happens repeatedly; the agent is probably misunderstanding
scope.
- Scope violation → iteration rejected with
rejected + "scope
violation" note. Same advice.
- Plateau → loop stops naturally. Report this as a success (the system
converged), not a failure.
See also
docs/preconditions.md — exhaustive list of what the gate checks
docs/goodhart.md — why harness integrity matters
docs/vs-udit.md — contrast with the existing uditgoenka/autoresearch skill
- mandar-karhade/test-case-tiny-js-bundle — live demo repo with a frozen run history