agentleFS
GitHubKnown publishergithub/lever/skills/experiment-arms/SKILL.md

experiment-arms

Skill279 starsChanged 17 days agoMITView on GitHub

Runs many single-hypothesis "arms" as do-not-merge PRs, measures each with paired, interleaved runs against a cost-matched control, keeps only the arms that clear a keep bar set in advance, records every negative, and composes the winners into a stack that is re-measured on real traffic after merge. It opens PRs and burns CI, so it runs only on `/lever arms` or an explicit ask to measure ideas ("which of these ideas actually helps?", "is this change worth it?", "try that tip I saw", "run arms on this") for a CI, perf, eval, benchmark or cost hill-climb where the system can be run on demand.

The file

We split this skill into the rules it gives an agent

5 rules. About 0 are common, 4 are distinctive, and 1 is very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈4

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 1

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Start from a Loop Card (templates/loop-card.md).

    line 11
  2. Distinctive:

    Don't use it when:

    line 20
  3. Distinctive:

    Name the felt metric and the statistic: Use the number a person actually experiences: PR time-to-green, not one job's duration.

    line 37
  4. Distinctive:

    Measure noise first (A/A): Run the control against itself (two identical arms) for at least 3 rounds.

    line 39
  5. Distinctive:

    Watch the base: If main (or the harness) breaks, void every round that ran on it.

    line 46

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Experiment arms

The experiment arm method turns a pile of ideas into measured, landed improvements. Each hypothesis gets its own small, revertible PR (an arm) that exists only to be measured. Arms run in parallel against a shared control that does the same work, and each one is judged against a keep bar you wrote down before looking. Winners get composed into a stack and landed at whatever autonomy level the repo allows. Losers become closed [negative result] PRs plus a ledger row, so no one has to try them again. The number you report at the end comes from real traffic after merge, not from the estimate.

Start from a Loop Card (templates/loop-card.md). If you don't have one yet, run find-the-loop first.

When to use / not use

Use it when:

  • the metric has run-to-run noise and you can run the real system on demand (CI, a perf harness, an eval, a benchmark);
  • there are several candidate changes, or one popular tip nobody has measured on your setup;
  • the result must survive review by someone who will ask "compared to what?".

Don't use it when:

  • one deterministic test settles the question. Write the test.
  • the expected effect is smaller than the noise floor and you can't afford the n needed to see it. Pick a different instrument or a bigger lever.
  • the system can't be run on demand. Observe production instead and say the evidence is weaker.

Roles

RoleOwns
Coordinator (you, or one session)Loop Card, hypothesis table, ledger, budget, cutoff, status
One worker per armOne change, its arm PR, its measurement, a KEEP/DROP report with numbers
Reviewer on a different modelCritique of the hypothesis table and the final stack (see cross-model-review)

A coordinator that starts editing code stops coordinating. Keep it to orchestration.

Steps

  1. Name the felt metric and the statistic. Use the number a person actually experiences: PR time-to-green, not one job's duration; long frames, not render count. Report p50 and p90. Note any guard metrics (cost, failure rate, test count) that must not regress.
  2. Baseline and critical path. Pull 50–150 historical samples. Decompose them and name the slowest required lane, since only changes to the critical path move the felt metric. Write the result to baseline.md.
  3. Measure noise first (A/A). Run the control against itself (two identical arms) for at least 3 rounds. The spread of those deltas is the noise floor. If the keep bar is inside it, raise n or change the instrument before you run a single treatment.
  4. Write the hypothesis table. One row per arm, with these columns: concept, expected Δ, on critical path?, risk, equivalence proof, owner. One concept per arm and no drive-by edits. Read the ledger first, and don't re-run a recorded negative without a new reason.
  5. Pre-register the keep bar in the Loop Card before any treatment runs. The default: the arm wins against control in every round, the median paired Δ beats the bar and is outside the noise floor, the 95% CI of the paired Δ excludes zero, and equivalence passes (same test identities, byte-identical outputs, same eval answer key).
  6. Freeze a measurement base, a branch or SHA that stays fixed for the whole round. Branch every arm from it.
  7. Build arms in parallel, in batches. Run as many at once as your spend tier's lane cap and the repo's budgets.max_parallel_arms allow, whichever is lower; queue the rest for the next batch. Each gets one worktree, one worker and one draft PR from a lever/arm-<name> branch, titled [measurement — do not merge] <arm> with a do-not-merge label. Push early so other work can stack on it. Don't serialize measurement while waiting for an earlier arm's result. Run everything now and decide the order afterwards.
  8. Make the control cost-matched. The control must do the same work as the treatment. Put the same nonce change in every arm and the control so that caches bust equally and path filters can't skip lanes. Reject any arm whose heavy checks were skipped. An empty commit that goes green in 2 minutes is not a control.
  9. Run paired, interleaved rounds. In each round, push every arm and the control together. Pilot with 3 pairs, decide with n ≥ 6 per arm (12 for small effects), and use n ≥ 10 for any headline number. Below 6 complete pairs the verdict is INCONCLUSIVE, whatever the tier. A failed or missing treatment run counts against the arm, not as a skipped pair. Run at least 2 rounds. Label small-n p90s as exploratory. For CI, use scripts/ci_arm_driver.py (see ci-adapter.md).
  10. Watch the base. If main (or the harness) breaks, void every round that ran on it. Merge the fix into every arm's measurement commit, since that's for measurement only, and re-run.
  11. Decide keep or kill against the bar from step 5, using scripts/arm_stats.py (--min-pairs, default 6; it checks reliability before speed). Write every DROP to the ledger (templates/ledger.md) with its numbers, close its PR, and retitle it [negative result] <arm>. Re-test popular tips like any other arm: a widely shared one can lose on your hardware.
  12. Compose the winners. Order them by dependency, then by measured value. Run one combined confirmation round (about 5 pairs) of the full stack against control, because effects don't always add up. If interactions are unclear, report a range ("between A and B depending on whether X and Y stack").
  13. Land at the configured level. Open the real PRs, validate each one on its exact head SHA, and hand the set to landing-plan. Arms stay at L1. Winners land at the level resolved by docs/autonomy.md.
  14. Re-measure after merge on real traffic at +24 h and +72 h. Publish the estimate next to the realized result, and say which is which. Paired arms are the decision instrument; post-merge traffic is the reporting instrument.
  15. Clean up. Close every remaining arm and validation PR, file follow-ups, and feed the realized-vs-estimated error back into the next Loop Card.

Report status only when something changes, in the form metric: x → y (Δz%), with p50/p90, arms running/kept/dropped, budget used, and evidence level (templates/status-digest.md).

Dials

DialDefaultOptions
Metric / statisticfelt metric, p50 + p90any metric with an instrument the agent can run
Keep barwins every round; median paired Δ ≥ bar and outside A/A noise; 95% CI excludes 0; equivalence passesabsolute (e.g. ≥ 1 min on the critical path), relative (≥ X%), cost or reliability win with no regression
A/A rounds before treatments30 (only if the noise floor is already known and recent)
Pairs per armpilot 3 → decide 6 → headline 10+12+ for small effects
Rounds23 for borderline arms
Max concurrent armsmin(tier lane cap, budgets.max_parallel_arms), batchedhard cap 10, so shared runners aren't starved for real users
API rate floorpause below 3,000 remaining core callsany; set a shared budget across all agents
Poll interval≥ 4 min, query by head SHA onlylonger for long jobs; never tight loops
Drivers per arm set1a duplicate driver's push cancels runs
Compute budgetthe budget /lever hands down; billed minutes per arm, reported (unknown runner cost says unknown, not $0)hard cap per round
Rerun policyrerun failed jobs ≤ 2× as a person would; count reruns in the metric0 for pure-speed metrics on a stable fleet
Broken-base policyvoid the round; merge the fix into every armpause until the base is green
Arm PRsdraft + do-not-merge label, closed at hand-offlocal-only arms (no PR) for harness-only metrics
Negative resultsclosed [negative result] PR + ledger rowledger row only
Merge authority for winnersrepo ceiling via docs/autonomy.mdL2 hand-off · L3 on a recorded grant · L4 gated self-merge or enqueue
Cutoffnonewall-clock time after which no new arms start
Post-merge re-measure+24 h and +72 hoff only if the metric has no production signal

Outputs

  • baseline.md: historical p50/p90, critical path, noise floor.
  • A hypothesis table and a ledger, with every arm, kept or dropped, and numbers.
  • One draft do-not-merge PR per arm, plus closed [negative result] PRs for the drops.
  • A stack of real PRs, each with a before/after table, cost delta and risk.
  • Final report: estimated vs realized x → y (Δz%), p50/p90, n, cost delta.

This skill's own feedback loop

  • Estimate error: realized post-merge Δ vs pre-merge paired Δ. If they keep disagreeing, the instrument is missing something, such as flake reruns or load.
  • Hit rate: kept arms / total arms. A rate near 100% means the bar is too loose or you're only testing safe bets.
  • Repeat negatives: arms re-run without a new reason. This should be 0.
  • Budget incidents: rate-limit exhaustion and starved runners. This should be 0.

Failure modes it prevents

  • Comparing against history instead of a paired control, or against a cheap control that skipped the real work.
  • Declaring wins from n = 1, or from deltas inside the noise floor.
  • Waiting for each arm in turn when they could all run now.
  • Speed-ups that weaken the signal: fewer tests, skipped lanes, retries hiding failures.
  • Agents exhausting the shared API quota or runner pool.
  • Re-trying an idea the team already disproved.
  • Reporting a projection as a result.

Why this exists

  • In one CI speedup project across two repos, 79 do-not-merge experiment PRs produced 27 kept changes in about 1.5 days. Measured on real traffic after merge, p50 time-to-green fell 43% in one repo and 34% in the other.
  • The first control was an empty commit. Path filters skipped the heavy lanes, so it went green in 2.25 minutes. Controls must do the same work as treatments.
  • "Why do we have to wait until 4pm?" That one question turned serial measurement into parallel arms with ordering decided afterwards.
  • The project began from a viral Windows Dev Drive tip. It lost in both paired runs on those runners and broke checkout credentials. Meanwhile "cache corruption" turned out to be page-file exhaustion (15.2% → 0% failures), so the speed work also found reliability bugs.
  • Agents polling CI made about 45k API calls in 3 hours and took down unrelated tools. The rate floor, poll interval and arm cap are here because of that.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.