MIT · zero-dependency · no LLM · Node 18+

Add a referee to your coding loop.

A green test should mean your agent fixed the code — not that it edited the test to match the bug. PromptWheel is the referee that tells the difference: it reverts the test and config edits, re-runs your gate, and sees if the win survives. Deterministic, no LLM — and the gate's own files count as evaluator edits, never as wins.

$npx promptwheel@latest run
The moment it fires

An agent “fixed” the bug by editing the test. Green CI, live bug.

PromptWheel re-proves the win from the agent's source edits alone. The green came from the test file, not the code — so it evaporates, and the verdict is GAMED.

bash — the agent just made a red suite green
# code is still broken; the "agent" edited the TEST to expect the bug
$ npx promptwheel@latest run --base HEAD~1 --head HEAD

PromptWheel  base → head
   tests_pass    0 → 1  (+1, improved) [guard✓, high]
      🚩 GAMED — the "win" changed zero production-source files — only test/config/grader/golden

  VERDICT: GAMED  — a metric "improved" by editing the goalposts, not the source
$ echo $?
2

A real fix that survives the source-only re-run returns PASS / exit 0. Copy-paste the full self-contained repro from the README.

How it works

A diff partition and a re-run. No model in the loop.

Deterministic, explainable, and free — every flag is reproducible in seconds with a human-readable reason.

01 · measure

In throwaway worktrees

For any base→head, it re-runs your metric commands — tests, lint, tsc, coverage, bundle, evals — before and after, in isolated git worktrees. It never touches your working tree, and refuses to trust a delta inside the measurement noise band.

02 · re-prove

From source edits alone

It splits the diff into production source vs. {test, config, grader, golden}, rebuilds a clean base worktree with only the source slice applied, and re-runs the gate. If under half the gain survives — or the win touched zero source files — it was gamed.

03 · verdict

A CI-friendly exit code

You get a structured verdict and an exit code your harness can gate on: a real win, a guarded regression, a gamed metric, or an inconclusive guard that measured nothing. It prints exactly which edits it reverted and how much of the gain held.

0real win — the code earned it
1guarded regression
2GAMED — the win lived in tests/config
3inconclusive — a guard measured nothing
Why trust it

The gate everyone ships asks “did the number move?” This also asks “did the agent earn it?”

It never reads the agent's trajectory and never calls an LLM — so the verdict can't be obfuscated, costs nothing, and is identical on every re-run. And it catches what a read-only test dir or a PreToolUse hook can't: a win moved into the source — a hardcoded value, a mocked grader, a swallowed error, a relaxed config — on already-committed diffs, on any metric.

0
runtime dependencies
$0
per check — no tokens
57
unit tests, all green
1,000
real repos in the corpus sweep

Hardened against a 13-scenario labeled benchmark, a sweep across ~1,000 public TS/JS/Python/Go/Rust repos, and a 136-commit usage replay — all rerunnable in corpus/ and bench/.

Wire it in

The signal inside your loop, or the audit in your CI.

PromptWheel is the verifier, not the loop driver — a contestant can't referee itself.

As the agent's reward signal

while promptwheel improve \
  --attempt "$AGENT"; do :; done
# stops on plateau or regression;
# only keeps turns that earned it

As the CI outcome gate

npx promptwheel init
npx promptwheel run --working
# exit 0 real · 1 regression · 2 GAMED
# the audit the agent can't skip
Field notes

Building with AI.

Long-time web dev sharing what I build and learn. PromptWheel included. No spam.