Launching the Execution: From Plan to PRs
Seventh and final article in the series “From Specification to Execution.” The previous article showed the practical implementation on a React + Go stack. I finish here with the question that really matters. How do we launch this workflow without turning it into just more decoration?
I return to four simple questions.
- How does the agent know which test to implement next?
- How do I stop it from cheating while coding?
- How do I automate verification after each PR?
- How do I take over an existing repo without breaking everything?
I will answer these four points in order.
plan.md: the file the agent follows
The move that turns the workflow into practice fits into one file. Kent Beck calls it plan.md in his augmented coding workflow (2025). I keep the same principle. The file lives at the root of the project, or the module I am working on, and contains four things.
- The spec, meaning the intent, examples, and properties.
- An ordered list of tests to implement.
- Architectural decisions made during development.
- Follow-up items taken out of scope.
I can summarize it with a minimal example.
# Plan, billing module
## Spec
Calculate the cost of a call from a duration and a tariff.
## Tests
- [x] T1, happy path, 120s call to +33612345678
- [x] T2, zero duration returns 0
- [ ] T3, negative duration returns 0
- [ ] T4, the most specific prefix wins
- [ ] T5, cost remains monotonic with duration
The agent follows this file one test at a time. It implements T1, then T2, then stops. After each green cycle, it checks the box, creates a commit, then waits for my green light before moving to the next test.
I want this pause. Without it, the agent chains tasks too quickly and eventually drifts. With it, I keep a human checkpoint at each cycle.
I keep three very concrete benefits to this file:
- It survives session crashes and agent restarts. The continuity is in the file, not in the conversational memory.
- It can be audited with a simple
git diff plan.md. I see the real progress, not just an impression of progress. - It is reusable across multiple agents. If I change tool or model, I resume the same plan.
The full template and associated system prompt are in tdd-skill/plan-template.md of the public repo.
Pre-commit hooks as guardrails
The hard rules from the agent-discipline.md file become useful when I enforce them via hooks. I keep three.
Hook 1: Detect test modifications during a green cycle
I keep a .tdd-cycle marker to remember the current test. The hook rejects any modification of other tests in the same commit. This cleanly cuts the reflex of deleting or modifying a test to make the code pass.
#!/usr/bin/env bash
MARKER=".tdd-cycle"
[[ -f "$MARKER" ]] || exit 0
CURRENT_TEST="$(cat "$MARKER")"
CHANGED_TESTS=$(git diff --cached --name-only | grep -E 'test')
for f in $CHANGED_TESTS; do
[[ "$f" == "$CURRENT_TEST" ]] || {
echo "ERROR: test '$f' modified outside the cycle ($CURRENT_TEST)"
exit 1
}
done
I create the marker at the beginning of the cycle, then remove it once the clean commit has passed.
Hook 2: Require a true red before writing the code
I want to see a failing test before allowing production code to be written. If the test passes on the first run, I stop. Either the test tests nothing, or the feature already exists and I am not in the right cycle.
if pytest "$TEST_ID" --tb=short -q > /dev/null 2>&1; then
echo "ERROR: test passed on first run. STOP."
exit 1
fi
I can also verify that the failure is for the right reason, not an import error or a syntax fault.
Hook 3: Lock test files during the green phase
I can go further and make test files read-only during the green phase.
chmod a-w tests/test_billing.py
# code, green tests
chmod u+w tests/test_billing.py
This third guardrail does not replace the first two. It complements them.
I keep three options for implementing these hooks.
- Pure bash Git hooks, no dependencies, for a quick start.
pre-commit, if I want a more standard framework on the Python side.lefthookorhusky, if the stack mixes several languages.
The full versions by language are in tdd-skill/hooks-{python,typescript,go}.md of the public repo.
The multi-level CI pipeline
I want the CI to reflect the same breakdown.
unit, front and back -> integration, testcontainers -> contract, Pact -> mutation -> E2E
I keep simple rules:
- Unit and integration tests block every PR.
- Contract testing also blocks if pacts have changed.
- Mutation testing blocks on PRs, but only on modified files.
- E2E runs on
main, not on every PR. - The full mutation suite and flakiness checks run overnight.
I summarize the logic in a simplified GitHub Actions excerpt.
jobs:
unit:
runs-on: ubuntu-latest
steps: [ ..., go test ./..., pnpm test:ci ]
integration:
steps: [ ..., go test -tags=integration ./... ]
mutation:
if: github.event_name == 'pull_request'
steps: [ ..., gremlins unleash $(changed_pkgs) ]
contract:
steps: [ ..., pnpm test:pact, go test -tags=pact ./pacts/... ]
e2e:
if: github.ref == 'refs/heads/main'
steps: [ docker compose up, playwright test ]
I’m not looking for a spectacular pipeline. I’m looking for a pipeline that properly separates responsibilities.
Each level covers a different class of failure:
- Unit does not replace integration.
- Integration does not replace contract testing.
- Mutation testing does not replace the rest.
- E2E is not for validating every detail.
For a team of less than three developers, I start simpler: unit, integration, and lint. I add mutation and contract testing when the cost of a bug exceeds the cost of the CI.
Auditing an existing repo in three levels
When I arrive on a project already in place, I don’t start by imposing the workflow. I look at where things stand.
Level 1: Fast static audit
In 15 to 30 minutes, I can already see a lot of things.
For Python:
ruff check src tests --select PT,S,B
grep -rn 'mock.patch' tests/
grep -rEn 'except\s*:\s*$' src/
For Go:
golangci-lint run --enable=testifylint,thelper,paralleltest,errcheck
grep -rn 'sqlmock'
grep -rEn 't\.Skip\(\s*\)' --include='*_test.go'
For TypeScript:
pnpm exec eslint . --max-warnings 0
grep -rEn 'vi\.mock\(["\x27]\\./'
grep -rEn '\.(only|skip)\('
I am primarily looking for three signals: internal mocks, tests without clear assertions, and forgotten skip statements.
Level 2: Dynamic audit
I then move to a deeper audit, over one to two hours.
- Branch coverage across the entire project.
- Mutation testing on the most critical module.
- Flakiness check with ten successive runs and randomized order.
I don’t care about the percentage alone. I look for files at 0%, files with very high coverage but few assertions, and modules that survive too many mutations.
Level 3: AI-assisted audit
If I want to go further, I use a critic-agent that applies the hard rules from agent-discipline.md. I ask it for a prioritized report, BLOCKER, HIGH, MEDIUM, LOW, with the detected violations and the missed PBT tests.
I keep the report format so a manager can read it in a few minutes, and a dev can act on it without reinterpreting the diagnosis.
An audit without action is not very useful. I see it as the starting point for hooks and the pipeline, not as an end in itself.
Bringing it all together
The workflow reinforces itself when I let it breathe.
The more precise the spec, the more solid the tests. The more solid the tests, the more the AI can produce without drifting. The more reliable the code, the faster I can iterate. And the more I iterate, the more details I discover to feed back into the spec.
I can summarize the series in seven sentences.
- AI has not killed tests; it has shifted the work to the spec and to judgment.
- Executable spec is built in four levels, from natural language to contracts.
- The test precedes the code and comes from elsewhere than the agent writing the code.
- The quality of a test suite is measured on four axes: coverage, mutation, smells, robustness.
- Property-based testing is the defense against weak oracles.
- On a React + Go stack, three structural choices keep the stack reliable:
testcontainers-go, strict MSW, and Pact. - The operational side rests on four pillars:
plan.md, pre-commit hooks, CI pipeline, and audit.
This series stops here. The public repo contains the demos, the hooks by language, and the extended TDD skill for Claude Code. I can fork it, break it, adapt it, then put it back in its place.
AI produces code in volume. The workflow produces the trust to use it.
Further reading
- Kent Beck, Augmented Coding: Beyond the Vibes (2025) - signals.aktagon.com
- pre-commit framework - pre-commit.com
- lefthook - lefthook.dev
- husky - github.com/typicode/husky
- The series public repo - github.com/mwolff44/spec-to-tests
tdd-skill/plan-template.md, plan.md template and agent prompts.tdd-skill/agent-discipline.md, the hard rules.tdd-skill/hooks-python.md,hooks-typescript.md,hooks-go.md, the hooks by language.examples/billing-react-go/, the React + Go + Pact demo from article 6.examples/pbt-sip/, the Hypothesis stateful demo on SIP from article 5.
This is the seventh and final article in the series on the “from specification to execution” workflow. If this series has been useful, share it and leave me some feedback, in comments or via repo issues. Happy coding.