Skip to content

devlog/agent-loops/let-the-coding-agent-write-the-tests-first-3.md

Let the Coding Agent Write the Tests First

Give the agent a contract, not an implementation

I get better results from coding agents when I separate defining behavior from producing code. Otherwise, the agent tends to write an implementation and then write tests that congratulate it. Here is a small, reproducible workflow: build a Python slug function, make the agent write the tests first, inspect the failures, and only then allow implementation.

The function will lowercase ASCII letters, preserve ASCII digits, replace runs of other characters with one hyphen, and trim surrounding hyphens. Empty results raise ValueError; non-string inputs raise TypeError. That is deliberately narrower than a production Unicode slug library.

Connect Claude Code through Fluxo

Fluxo gives this terminal workflow one key and one base URL, https://api.fluxorelay.com, across Claude, GPT, Gemini, DeepSeek, Kimi, GLM, Qwen, Grok, and more. It works with Claude Code, Codex, Gemini CLI, and any OpenAI SDK. Here, I use Claude Code with Claude Sonnet 5.5; copy its actual API model identifier from your dashboard rather than guessing a slug.

If you want to follow along through the same relay, create a Fluxo account.

With Node.js and Python 3 installed, run these commands individually. The read prompts keep credentials out of shell history; replace the model prompt with the identifier shown in your dashboard.

npm install -g @anthropic-ai/claude-code — read -rsp 'Fluxo API key: ' ANTHROPIC_API_KEY; printf '\n'; export ANTHROPIC_API_KEY

unset ANTHROPIC_AUTH_TOKEN — export ANTHROPIC_BASE_URL='https://api.fluxorelay.com' — read -rp 'Fluxo model identifier: ' FLUXO_MODEL_ID; export FLUXO_MODEL_ID

Create a repository with an intentionally unfinished function

These commands assume Bash. The stub makes the first test run fail on missing behavior, not a missing module. Nothing beyond Python's standard library is needed.

mkdir fluxo-tests-first && cd fluxo-tests-first && git init

python3 -m venv .venv && source .venv/bin/activate

printf '.venv/\n__pycache__/\n' > .gitignore — mkdir tests

printf 'def slugify(value):\n raise NotImplementedError("Implementation intentionally missing")\n' > slugify.py

Ask Claude Code to write only the contract tests

Keep this first task restrictive. No implementation, no dependencies, and no tests that merely check whether a function exists.

claude --model "$FLUXO_MODEL_ID" --permission-mode acceptEdits -p 'Write only tests/test_slugify.py using unittest. Import slugify from slugify. Do not edit slugify.py or other files. Contract: accept strings only; otherwise raise TypeError. Lowercase ASCII letters, preserve ASCII digits, replace each run of everything else with one hyphen, and trim surrounding hyphens. Raise ValueError if the result is empty. Include explicit expected outputs for Hello World, already--slugged, version 42, surrounding whitespace, punctuation, and non-ASCII separators. Test empty strings, whitespace-only strings, punctuation-only strings, None, integers, and lists. Do not implement anything or weaken the contract.'

cat tests/test_slugify.py — git diff -- slugify.py

The diff must be empty. Read every expected value yourself: Hello World should become hello-world, and version 42 should become version-42. Check that exception assertions require the specified exception types, not just any exception.

Record a real red test run

python -m unittest discover -s tests -v

This command must exit nonzero. NotImplementedError is expected from the stub; syntax errors, import errors, and zero discovered tests are not useful red states. Fix test setup problems before continuing.

git add .gitignore slugify.py tests/test_slugify.py && git commit -m 'Specify slugify behavior before implementation'

Allow implementation without moving the goalposts

Now give the agent a narrower editing boundary than its usual repository-wide freedom.

claude --model "$FLUXO_MODEL_ID" --permission-mode acceptEdits -p 'Implement slugify in slugify.py to satisfy the existing contract tests. Modify only slugify.py. Do not edit tests, add dependencies, or change the contract. Run python -m unittest discover -s tests -v and report the result. Prefer a small, readable implementation.'

python -m unittest discover -s tests -v

git diff --exit-code -- tests/test_slugify.py

git diff -- slugify.py — The permission mode is convenience, not enforcement. The unchanged-tests check matters more than the agent's summary. Review the implementation for ASCII-only behavior and explicit type validation before committing.

Keep the red-green loop small enough to review

Fluxo bills real tokens, including cache reads and writes, without a monthly fee, so repeated agent passes still deserve a budget. Its referral share applies when invited users pay; that is separate from this workflow. My useful metric here is simpler: one reviewed contract, one verified red run, and one implementation that turns it green without rewriting the evidence.

Next in the buffer

Sign up in a minute and get credit to try every model mentioned here. Sign up →