Repository rules for python-template, the cargo-generate template. These apply to every
contributor, human or AI-assisted. The tools, the checks and the release are
CONTRIBUTING.md's.
template/is what a generated project gets: every file in it, rendered by cargo-generate's Liquid unlesstemplate/cargo-generate.tomlexcludes it. Its own rules -- the lint and type policy, the types, the tests -- are template/AGENTS.md's, and they hold for what it generates.- The rest is this repository's: the smoke test and render-check that prove the template, and the files any repository has (these, the hooks, the workflows, the flake).
A change to template/ is judged by what it generates, never by its text alone: just smoke <kind> generates it with the real cargo-generate and runs the project's own checks.
- A placeholder names a declared variable. cargo-generate renders an unknown one as an empty
string, without a word;
just render-checkrefuses it. - A file with another
{{ }}or{% %}syntax is excluded (copied verbatim): the workflows (GitHub's expressions), the justfile (just's),cliff.toml(Tera, which Liquid would evaluate silently), the hooks and the Nix files undernix/. An excluded file holds no placeholder: render-check refuses one. - Every answer combination renders and parses. render-check renders each templated file for 108 combinations and parses the TOML, YAML, JSON and Nix; whitespace is the smoke test's, as python-liquid trims one newline liquid-rust keeps.
These files are the template's own, copied here, and render-check holds each byte for byte equal to
its original under template/: cog.toml, cliff.toml, .editorconfig,
.github/release-notes.sh, the commit-msg and pre-push hooks, the dprint, Conventional Commits
and Dependabot auto-merge workflows, and the justfiles' shell-check, shfmt and shfmt-recipes
recipes. Change the original, then copy it; its wording must hold in both places.
Shell is formatted by shfmt (.editorconfig holds its style) and passes ShellCheck -- a justfile
script recipe included: just shell-check reads them out of just --dump, and refuses one that
splices a value into its text (it takes arguments). The Nix is nixfmt's, the justfiles just's own
(nix fmt formats the three); the Markdown and TOML, dprint's; just format runs them all. The
workflows pass actionlint. Actions are pinned to a full commit SHA, the version in a comment, as
Dependabot keeps them.
A comment says what the code cannot: why it is so, the source it follows, the constraint it keeps. Make it concise and precise -- a line where a line does -- and never restate the code: that is noise, and it goes stale. A change that falsifies a comment rewrites it.
Conventional Commits are enforced by cog check locally
and in CI. The changelog is generated from the commits (cliff.toml), so the subject line is
what a template user reads; chore, ci and style commits stay out of it, and ! or a
BREAKING CHANGE footer -- a prompt renamed, a file a generated project no longer has -- lists a
commit first:
feat(template): ask for the oldest Python
fix(template): keep the workspace's members on one version
docs: the release steps
feat!: rename the `kind` prompt to `project_kind`
The git hooks (just install-hooks) check the same before a commit and a push; never bypass them
with --no-verify. git revert writes Revert "...", which CI refuses: reword it as
revert: ....
Run just checks -- render-check, the shell, the workflows, dprint, the commit messages and the
flake -- and, for a change under template/, just smoke <kind> for each of bin, lib,
workspace. Together they are what CI runs, but the smoke test's releases and its other jobs: CI's.
Reporting "done" on a change that has not passed them is the one thing that wastes the most time
here.
If a check fails for reasons unrelated to your change, say so explicitly rather than working around it -- a broken check that everyone routes around stops protecting anything.