Getting Started
This tutorial adopts testing-conventions on a repository and walks you through the loop you'll live in from then on: a pull request trips a check, CI goes red with the exact violation, you fix it, CI goes green. By the end, every check runs on every pull request — and you'll have seen one fire for real.
You need a GitHub repository you can push to. The steps below use a fresh Python library so the output matches exactly; the same flow works for TypeScript and Rust, or on an existing project.
1. Add the workflow
Add one file to your repository — no inputs, no config — and commit it to your default branch:
# .github/workflows/conventions.yml
name: Conventions
on: [pull_request]
jobs:
conventions:
uses: thekevinscott/testing-conventions/.github/workflows/testing-conventions.yml@v0On every pull request it auto-detects the languages present (Python, TypeScript, and Rust), scans src, and runs every check with strict defaults — each as its own job that fails the build on a violation. A language with no sources is skipped, never failed, so the default is safe on any library.
That one file is the whole adoption: your repository is now held to the full standard.
2. Open a pull request that trips a check
See it fire. On a branch, add a source file with no test:
# src/greet.py
def greet(name: str) -> str:
return f"Hello, {name}!"Push the branch and open a pull request.
3. Watch the check go red
The Static checks (python) job fails — its Check colocated test step names the violation:
missing colocated unit test: greet.pyThis is the standard's core move: every source file carries a colocated, matching-named unit test, and the check is a blocking gate — the pull request stays red until the test exists (or the file carries a reasoned exemption). The Static checks job bundles the four source-scanning checks into one job per language; open the red step to see which check failed.
4. Make it green
Add the colocated test, named after the source file, side by side with it:
# src/greet_test.py
from greet import greet
def test_greet():
assert greet("Ada") == "Hello, Ada!"Push. The checks run again and come back green: the test exists (colocated-test), it runs the new lines at a 100% floor (unit-coverage), and its assertion pins the behavior (mutation breaks the code and requires a test to fail — an assertion-free test would leave the check red).
Where you are
Every pull request now runs the full standard: colocated tests, one substantial function per file, a 100% coverage floor, mocked-out collaborators in unit tests, real first-party code in integration tests, a binary mutation gate on changed lines, and clean packaging. When a job goes red, its step log names the file and the check; you fix the code, or record a reasoned exemption in testing-conventions.toml.
Teach your agent
Run npx testing-conventions install to write the contract into your repo's AGENTS.md, so a coding agent knows the rules before it writes code. The managed block carries the contract's non-negotiables and points at this site and its machine-readable digest (llms.txt). Re-running refreshes the block; everything outside its markers stays yours. install reads the block by its begin/end marker pair — if the end marker is missing (a hand edit deleted or fenced it), install stops and names it so you can restore the marker, keeping your surrounding prose intact. Pass a path to manage a different file: npx testing-conventions install CLAUDE.md.
Next
- A repository with several packages adopts per package — see Adopt on a monorepo. If a check isn't finding its subjects, read
sourcevs. the package root and the check-by-check discovery table first — most checks read fixed paths at the derived package root, not whereversourcehappens to point. - The testing model explains what each check enforces and why.
- Respond to a red check tunes a floor or exempts a file, with a reason.