Production-ready Python scaffolding with a structured AI-agent workflow — from idea to shipped feature.
git clone https://github.com/nullhack/python-project-template
cd python-project-template
curl -LsSf https://astral.sh/uv/install.sh | sh # skip if uv installed
uv sync --all-extras
opencode && @setup-project # personalise for your project
uv run task test && uv run task lint && uv run task static-checkMost Python templates give you a folder structure and a Makefile. This one gives you a complete delivery system:
- No feature starts without written acceptance criteria — Gherkin
Example:blocks traced to tests - No feature ships without adversarial review — the reviewer's default hypothesis is "broken"
- No guesswork on test stubs — they are generated automatically from your
.featurefiles - No manual
@idtags — assigned automatically when you run tests - AI agents for every role — PO, SE, and reviewer each have scoped instructions; none can exceed their authority
SCOPE → ARCH → TDD LOOP → VERIFY → ACCEPT
| Step | Role | Output |
|---|---|---|
| 1 · SCOPE | Product Owner | Discovery interviews + Gherkin stories + acceptance criteria |
| 2 · ARCH | Software Engineer | Module stubs, ADRs, auto-generated test stubs |
| 3 · TDD LOOP | Software Engineer | RED → GREEN → REFACTOR, one criterion at a time |
| 4 · VERIFY | Reviewer | Adversarial check — lint, types, coverage, semantic review |
| 5 · ACCEPT | Product Owner | Demo, validate, ship |
WIP limit: 1 feature at a time. Features are .feature files that move through folders:
docs/features/backlog/ ← waiting
docs/features/in-progress/ ← building (max 1)
docs/features/completed/ ← shipped
| Agent | Responsibility |
|---|---|
@product-owner |
Scope, stories, acceptance criteria, delivery acceptance |
@software-engineer |
Architecture, TDD loop, git, releases |
@reviewer |
Adversarial verification — default position: broken |
@setup-project |
One-time project initialisation |
| Tool | Role |
|---|---|
uv |
Package & environment management |
ruff |
Lint + format (Google docstrings) |
pyright |
Static type checking — 0 errors |
pytest + hypothesis |
Tests + property-based testing |
pytest-beehave |
Auto-generates test stubs from .feature files |
pytest-cov |
Coverage — 100% required |
pdoc |
API docs → GitHub Pages |
taskipy |
Task runner |
uv run task test # Full suite + coverage
uv run task test-fast # Fast, no coverage (use during TDD loop)
uv run task lint # ruff check + format
uv run task static-check # pyright
uv run task run # Run the app| Coverage | 100% |
| Type errors | 0 |
| Function length | ≤ 20 lines |
| Class length | ≤ 50 lines |
| Max nesting | 2 levels |
| Principles | YAGNI › KISS › DRY › SOLID › Object Calisthenics |
Write acceptance criteria in Gherkin:
@id:a3f2b1c4
Example: User sees version on startup
Given the application starts
When no arguments are passed
Then the version string is printed to stdoutRun tests once — a traced, skipped stub appears automatically:
@pytest.mark.skip(reason="not yet implemented")
def test_display_version_a3f2b1c4() -> None:
"""
Given the application starts
When no arguments are passed
Then the version string is printed to stdout
"""Each test is traced to exactly one acceptance criterion. No orphan tests. No untested criteria.
v{major}.{minor}.{YYYYMMDD} — each release gets a unique adjective-animal name.
MIT — see LICENSE.
Author: @nullhack · Documentation