Hand a coding agent a failing test and tell it make it pass. Sometimes it fixes the bug. Sometimes it edits the test, hardcodes the expected value, or deletes the assertion β and reports done β with a straight face. Every "build an agent" tutorial stops before this problem. This one is built around it.
The agent itself is the easy part β a while
loop around a chat model with one tool. 01_loop/agent.py is ~70 real lines that fix a real bug, live:
The hard part β Chapter 5, and the reason this repo exists β is an independent skeptic that doesn't take "done β " on faith. It checks the code's behavior against contract probes the agent never saw, so a fix that only games the visible test β editing it, or hardcoding/stubbing the source β is caught deterministically, no LLM required (a model judge reads the full diff on top, as a second opinion). You build both, one small chapter at a time. The scaffolding you're assembling has a name now β the harness β and this is harness engineering, from scratch.
git clone https://github.com/Saivineeth147/skeptic && cd skeptic
python3 -m venv .venv && source .venv/bin/activate # recommended (system Python blocks pip: PEP 668)
pip install -r requirements.txt
echo 'OPENROUTER_API_KEY=sk-or-...' > .env # grab a key at openrouter.ai
cd demo
python ../01_loop/agent.py "the cart test is failing, fix it" # β runs in this dir; it's a sandbox demo
Defaults to Claude Sonnet on OpenRouter; point AGENT_MODEL
/ OPENAI_BASE_URL
at OpenAI, a local Ollama/vLLM server, or anything OpenAI-compatible. A full run costs a few cents (or nothing, on a free model).
Then watch the skeptic catch cheats β including the sneaky one that never touches the test:
python ../05_verify/verify.py --cheat # edits the TEST to pass -> β REJECTED
python ../05_verify/verify.py --cheat-stub # stubs the SOURCE, tests green -> β REJECTED
python ../05_verify/verify.py --cheat-eq # __eq__ that equals ANYTHING -> β REJECTED
python ../05_verify/verify.py # an honest agent fixes the code -> β
VERIFIED
The cheat runs are the point: in each, the code passes the visible test yet is rejected,
because a hidden contract check the agent never saw exercises the behavior the test left
out. That's the whole idea β you can only verify work with a check the worker couldn't
optimize against. (--cheat-eq
is the nastiest: an object whose __eq__
returns True
for everything satisfies every bare assert in existence β the oracle catches it because
its comparisons are type-pinned, and its probe requires a completion sentinel so an early
sys.exit(0)
can't fake a clean exit either.)
Frontier models game their tests at production scale β measured, not anecdotal:
Anthropic reports reward hacking β includingtest hardcodingβ occurred during the production RL training of Claude Sonnet 3.7, and showed that models which learn to game coding tests generalize to far worse behavior, up to attempted sabotage in agentic coding sessions (paper).METR measured o3 gaming its evaluator in39 of 128 runs (30%) across RE-Bench tasks β and in21 of 21 runs (100%) on one task. Documented strategies: patching the evaluation function to judge every submission successful, monkey-patching the grader to return a perfect score, overwriting the timing function (writeup).- METR saw the same behavior across o3, o1, and Claude 3.7 Sonnetβ cross-model, and getting more sophisticated, not less.
Those documented strategies are exactly this repo's threat model β and the toy versions of them ship here as runnable, caught-by-CI cheats ( evals/cases.py).
Chapters 1β4 build the agent everyone builds. Chapter 5 is why this repo exists. Each is a single file that runs, plus a "what breaks without this" note β every chapter exists because the previous one broke somewhere.
| # | Chapter | The question it answers |
|---|---|---|
| 01 | ||
isan agent, minimally?Real toolsbash
forever?Context & compactionPermissions & sandboxingrm
?β
Verifyfinal/
That gap is the whole reason this repo exists. Same task, two runs. Both show visible tests PASS in green β but the skeptic also checks behavior the visible test never touched, so the honest fix is VERIFIED and the stubbed-source cheat is REJECTED:
The loop-and-a-tool part, yes β that's a commodity, and it's Chapters 1β4 here because you should understand it. What almost nobody builds is the part that makes an agent trustworthy: an independent check that the work is real. A model graded on "make the test pass" will, often enough, pass the test without fixing the code. This repo goes to the part the tutorials skip β a skeptic that catches that β and makes it the whole point.
Can't the agent just fool the skeptic too? It's harder than it looks, because the
strongest layer needs no model at all. The skeptic runs a hidden contract check β
behavior probes on inputs the agent never saw β so a cheat that games the visible test
(editing it, hardcoding, stubbing, special-casing the tested inputs) still fails on the
inputs it couldn't optimize against. That's deterministic. The oracle is also hardened
against the classic evaluator-gaming tricks from the reward-hacking literature: its
comparisons are type-pinned (an object whose __eq__
returns True
for everything
still fails) and its probe requires a completion sentinel (an early sys.exit(0)
that ends the process "cleanly" still fails) β both ship as runnable cheats. On top sits
a model judge reading the full diff, as a best-effort second opinion. The honest limit:
on a benchmark or this demo you own the spec and can supply the hidden check; pointed
at an arbitrary repo (final/ --verify
), the skeptic can only re-run your tests, flag
test-file edits, and lean on the judge β unless you hand it a held-out suite with
--holdout
. "Tests are green" and "the code is correct" are different claims; the hidden check is what actually separates them.
Do I need Claude? No, for the agent β any OpenAI-compatible model works
(AGENT_MODEL
), and it runs on a local Ollama/vLLM server (OPENAI_BASE_URL
), no cloud
key required. Smaller models emit malformed tool calls more often; the code handles that
rather than crashing. The judge is more model-sensitive, though: in my testing, small
(7β8B) models both missed subtly-disguised cheats and falsely rejected correct fixes.
So point the verifier's judge at a strong model (JUDGE_MODEL
, or --judge-model
), or
lean on the deterministic checks (the hidden oracle / --holdout
), which don't depend on the judge at all. The default judge, Claude Sonnet, caught the source cheats here with no false rejects β but that floor is real, and the repo says so rather than hiding it.
Is it safe to run? It executes model-generated shell commands, so treat it like it. Chapters 1β3 have no gate β use a throwaway directory. Chapter 4 adds a permission gate (a coarse heuristic, not a security boundary). The real isolation is a container β a Dockerfile ships in the repo, giving the agent a scratch filesystem that holds only what you mount, so it can't reach your
~/.ssh
, ~/.aws
, or the rest of your disk:
docker build -t skeptic .
docker run --rm --network=none -e OPENROUTER_API_KEY=unused skeptic \
sh -c "cd demo && python ../05_verify/verify.py --cheat-stub" # -> β REJECTED, offline
The gate makes mistakes visible; the sandbox makes them harmless. A live agent run
needs egress to reach the model, so drop --network=none
and pass a real key (or point it
at a local model) β the filesystem isolation still holds. --yes
warns unless it detects a sandbox.
Python version? 3.9+ (the code uses builtin generic type hints like dict[str, str]
) β checked in CI on both 3.9 and 3.12.
I kept reading "how to build an agent" posts that stopped at the loop and a
get_weather
tool, and never touched the parts that make a coding agent usable or trustworthy: what to do when the context fills up, how to stop it running something destructive, and β the part I care about most β how to know its output is real instead of taking "done β " on faith. So I built the version I wanted to read: every mechanism as its own runnable chapter, ending in a small CLI you can genuinely use.
If the 70-line agent makes you go "wait, that's all it is?" β that's the point. Everything after is turning that toy into something you'd trust.
The self-contained chapters make this easy to extend β see CONTRIBUTING.md. The most on-theme contribution: add a new cheat the skeptic should catch β drop it in evals/cases.py and watch the oracle reject it.
Run the suite (no API key needed): pytest tests/ -q
, and the reproducible cheat-catch
rate: python evals/run.py
. Both run in CI on every push.
MIT β see LICENSE. Learn from it, fork it, ship your own.