A Copier template for Python projects with verification guardrails built for AI-generated code.
A plumb line checks whether something is actually true and reliable. That's what this template does for the code in your project.
Plenty of Python templates handle linting, tests, and CI. Very few address the thing that actually breaks projects now: you cannot fully trust AI-generated code and neither can your tests.
AI-assisted development introduces failure modes that a standard quality floor does not catch:
- Hallucinated dependencies. Models invent package names that do not exist, and attackers register them. Between 5.2% and 21.7% of package references generated by popular code LLMs point at packages that were never published, and because models hallucinate the same names repeatedly, those names are predictable and reservable.
- Insecure patterns in generated code. Dependency CVE scanning tells you nothing about the code the model just wrote for you.
- Tests that pass without testing anything. Coverage counts lines executed, not assertions that would fail if the code broke. Generated tests are especially prone to this.
- Deceptively simple-looking code that is actually load-bearing. A model has no sense of which functions are structurally critical versus peripheral, so it applies the same casual scrutiny to both.
This template closes those gaps by default, alongside the usual floor.
Install the prerequisites:
brew install uv just gitleaks
uv tool install copierGenerate a project:
copier copy gh:the-jay-schwartz/plumb-python my-project
cd my-project
uv sync
just init-whitelist
just checkjust check runs the fast verification floor. just --list shows everything else.
Quality floor
- uv for dependency and environment management
- Ruff for linting and formatting, with the
Ssecurity rules enabled - Pyright for type checking
- pytest with a coverage gate that fails CI below a threshold you choose
- just as the task runner — one command vocabulary across every check
Verification guardrails
- Hallucinated-package detection via slopcheck, verifying every dependency actually exists in the registry
- Mutation testing via cosmic-ray, checking that tests would actually catch a bug rather than merely executing code
- Secret scanning via gitleaks, in CI and locally
- pip-audit for known CVEs in dependencies
- Complexity gating via xenon, failing on functions past a configurable cyclomatic-complexity grade
- Type-annotation complexity limits — a nesting-depth and length cap that forces named types instead of deeply nested generics
Periodic insights (non-blocking; just insights, weekly in CI)
- Risk hotspots — files ranked by complexity x change frequency, written to
HOTSPOTS.md - Dead-code detection via vulture
- Dependency hygiene via deptry (missing and unused declared dependencies)
Supply chain and release
- GitHub Actions CI with hardened defaults and a locked lockfile
- Release Please with OIDC trusted publishing to PyPI, so no long-lived tokens are stored anywhere
- Conventional Commits, enforced: a
commit-msghook and a PR-title CI lint (plus aconventional-commitsskill for agents) keep messages in the format Release Please needs to choose the right version bump - Dependabot for weekly dependency and Actions updates
Agent support
AGENTS.mdandCLAUDE.mdso coding agents follow the project's conventions rather than inventing their own- A Claude Code
PostToolUsehook that auto-formats every file the agent edits, in the same second, before you ever see it
Most of the floor above (Ruff, pytest, pip-audit, hardened CI, Dependabot) is table stakes now. A representative modern Python template already has it. Where this template actually differs is the AI-trust surface:
| AI-relevant guardrail | plumb-python | Typical Python template |
|---|---|---|
| Security linting of your own code | Ruff S rules |
Usually absent |
| Hallucinated-package detection | slopcheck | Absent |
| Weak-test detection (mutation) | cosmic-ray | Absent |
| Complexity gating | xenon (radon) | Usually absent |
| Type-annotation complexity limits | Custom check | Absent |
| Dead code / dependency hygiene | vulture + deptry | Absent |
| Risk hotspot analysis | Complexity x churn | Absent |
| Secret scanning | gitleaks | Varies |
| Agent instructions | AGENTS.md + CLAUDE.md |
AGENTS.md only, if any |
| Real-time enforcement on AI edits | PostToolUse hook |
Absent |
Generated projects stay linked to this template. When the template improves, pull the changes forward from inside a project:
copier updateA GitHub template repository does a one-time file copy with no variable substitution at all. Cookiecutter is the closer comparison: real templating, but like a GitHub template, it generates once and severs the connection to the source. Copier does both — templating and an update path — so improvements reach projects you have already generated instead of only the next one.
By default, this template does not include a web framework, Docker or deployment configuration, a database or ORM, or opinions about project architecture beyond src/ layout.
That is a deliberate choice for now, not a permanent stance. The differentiator here is the verification guardrails, and keeping those constant across every generated project is what keeps the template focused. Framework choices could be added later as conditional Copier prompts without touching the guardrails, which stay non-negotiable either way. For the moment, the template stops at the floor.
plumb-node— the same approach for Node and TypeScript projects (coming)
MIT