Guidance for Claude Code (and any agent) working in this repository.
This is the canonical hybrid Rust + Python template. Practices that
land here propagate to every downstream consumer seeded by
gh repo create --template zackees/template-python-rust-cmd. Bias
toward keeping things tight and load-bearing; the leverage is high.
- Always run gates through
./ci.sh <gate>. Never pasteuv run python ci/gates/...orcargo clippydirectly into a command. Theci/hooks/tool_guard.pyPreToolUse hook blocks bare forms and tells you why. - Reserve full
uv run(without--no-project --script) for named build entry points:./test,./build,ci/build_wheel.py,./publish,./install. Everything else needs the protective flags — seeci.shfor the rationale. - Every directory must have a
README.mdof ≥ 50 lines. Enforced byci/hooks/readme_guard.pyon every edit. - Source files ≤ 1000 lines (warn) / ≤ 1500 (fail). Enforced both
on every CI run (
ci/gates/loc.py) and per-edit (ci/hooks/loc_guard.py). Split convention:foo.rs→foo/mod.rs+ per-domain submodules withpub usere-exports inmod.rs. - Logic lives in Python under
ci/; YAML stays thin. Every CI step isrun: ./ci.sh <gate>. No multi-line shell embedded in.github/workflows/ci.yml. buildis the only fatal gate. A failing build halts the rest of the run because every downstream gate would produce noise against an uncompiled tree.
./install # verify toolchain shape (no maturin build)
./ci.sh fmt # one gate
./ci.sh all # every gate, continue past failures
./ci.sh --list # show registered gates
./test # cargo test + maturin develop + pytest
./lint # convenience: fmt + clippy + ruffFor the full design rationale see zackees/zccache#835.
| Concern | Home |
|---|---|
Repo-state checks (git push catches it) |
ci/gates/*.py |
| Agent-intent checks (only during sessions) | ci/hooks/*.py |
| LOC budget across the workspace | ci/gates/loc.py |
| LOC growth on this edit | ci/hooks/loc_guard.py |
| README presence + size | ci/hooks/readme_guard.py |
Bare cargo / unsafe uv run shape |
ci/hooks/tool_guard.py |
If a rule would fire equally well on a git push from a terminal as
from a Claude edit, write it as a gate. If it needs to know what tool
is about to run, write it as a hook.
crates/template-core— reusable Rust library logiccrates/template-cli— bare Rust binary (the packaged CLI)crates/template-py—PyO3wrapper cratesrc/template_python_rust_cmd/— thin Python surface, packaging glue, Python CLI shimci/— automation (seeci/README.md)action.yml,action/cleanup/action.yml— composite action contract
- Grow
template-corefirst; expose throughtemplate-cliandtemplate-py. Don't let them diverge on core behavior. - The wheel exposes
template_python_rust_cmd._native(PyO3) AND ships the cargo-builttemplate-cli[.exe]as a raw wheel script attemplate_python_rust_cmd-<ver>.data/scripts/. Pip extracts that directly into the venv'sScripts//bin/on install — no Python shim sits in front of the binary.ci/build_wheel.py::verify_artifacts()enforces both deliverables are present in the wheel. - When changing user-visible commands, update README.md,
UPDATE.md, docs/ARCHITECTURE.md,
and any affected
ci/gates/<name>.py. - The release pipeline builds
template-clivia cargo into the pinnedCARGO_TARGET_DIR, thenci/build_wheel.pypost-processes the maturin wheel to inject the binary at.data/scripts/with a fresh RECORD row. There is no_bin/staging step under the package source tree anymore; see #7 for the rationale (Windowsos.execvrace).
- Design rationale → zackees/zccache#835
- Architecture → docs/ARCHITECTURE.md
- Release flow → docs/RELEASE.md
- Linting policy → LINTING.md