- If the
rtkCLI is available, prefix shell commands withrtk(token-optimizing proxy); otherwise run commands directly. - Put short flags before long flags in shell commands, alphabetize short flags
and long flags within their groups, and write long flags with values using
--flag=value. - Use the fff MCP tools for file search operations when available; otherwise
use
rgorrg --files. - Do not commit unless explicitly asked.
- Preserve existing uncommitted work. Never revert user changes unless explicitly asked.
- Keep generated-project dependency files consistent when editing template
dependencies:
{{cookiecutter.project_slug}}/pyproject.tomlchanges normally require regenerating or updating the bakeduv.lockafter cookiecutter renders the project.
- This repository is a Cookiecutter template for a Python 3.14 Django 6 API service using Django Ninja, Celery, django-structlog, django-storages, pytest, Ruff, Ty, uv, Docker, and Compose.
- Root-level files control the template itself:
cookiecutter.json,hooks/,.github/,plans/, andREADME.md. - Files under
{{cookiecutter.project_slug}}/are rendered into the generated project. Keep Jinja expressions valid and avoid changes that only work before rendering. .github/workflows/*and.agents/*are copied without rendering. Do not add Cookiecutter variables to those paths unless_copy_without_renderis changed deliberately.- The generated project's
AGENTS.mdshould stay focused on the baked Django application. Root guidance belongs in this file. - Do not add root-project instructions that assume the root repository is the generated Django app.
- Keep
project_slugderived fromproject_nameunless there is a clear reason to changecookiecutter.json. - Preserve hook validation for values written into rendered files:
author_nameanddescriptionmust not contain double quotes, backslashes, or newlines;author_emailmust be an email address;github_usernamemust be a valid GitHub username. project_slugmust start with a lowercase letter, contain only lowercase letters, digits, and single hyphen separators, and stay 50 characters or fewer.
- Follow Ruff formatting and linting for Python files that are valid before rendering.
- Remember that
hooks/pre_gen_project.pycontains Cookiecutter substitutions and is not plain Python until rendered. - Avoid trailing commas on newlines unless Ruff adds them to keep long lines valid/readable.
- Never add
from __future__ import annotations. - Prefer clear, explicit code over clever compression.
- Use extended YAML block style instead of compact flow style, for example
branches:followed by- maininstead ofbranches: [main]. - Order unordered list items alphabetically when dependency order does not
matter. This includes Markdown inventory bullets, YAML lists of pre-commit
hook ids, full Docker Compose volume entries, GitHub Actions matrix entries,
Dependabot update entries, environment-variable documentation, and command
argument lists. For example, put
check-dependabotbeforecheck-github-workflows,CACHE_URLbeforeDATABASE_URL,../../src:/app/srcbeforemedia_data:/app/media, anddockerbeforegithub-actions. - Separate each multi-line control-flow statement from adjacent statements with
one blank line. Within compound control-flow statements, insert one blank
line immediately before every continuation clause, including
elif,else,except, andfinally. Apply this toif,try,for,while,with, and equivalent constructs. For example, leave a blank line between atrysuite and its firstexceptclause. - At module scope, order declaration blocks as call-style markers such as
pytestmark, then constants, then variables. Separate each block with a blank line, and keep constants blocks prefixed and suffixed by a blank line unless they are at the start or end of a file. - Order constants alphabetically within each file when dependency order does not matter.
- Order
pyproject.tomlsubsections alphabetically when dependency order does not matter; for example, place[tool.coverage.*]before[tool.django-stubs]. - Order public classes, public functions, and methods alphabetically within their group when dependency order does not matter.
- Keep classes and functions grouped separately.
- Put private helper utilities at the bottom of the file under a
# Utilsheading, alphabetized there. - Alphabetize exception classes in an
excepttuple when order has no semantic significance. For example:except (KeyError, TypeError, ValidationError, ValueError) as error:. - Respect semantic ordering constraints, such as Django model fields, inheritance dependencies, decorators, framework-required signatures, Cookiecutter rendering behavior, and import-time behavior.
- Use
.yamlfor workflow files and name them with lower kebab-case basenames that describe the workflow scope, for exampleci.yamlordocker-checks.yaml. - Keep each workflow
name:as a Title Case noun phrase aligned with the file basename, such asTemplate CI,Docker Checks, orOpenAPI Schema Export. - Use lower kebab-case job ids, and keep them stable because other workflow
fields may reference them through
needs. - Use concise, user-facing job
name:values because they become GitHub status check names. Prefer action-oriented names such asCheck migrations,Build Docker images, andSmoke test Docker Compose. - For matrix jobs, put the matrix value at the end in parentheses, for example
Smoke test Docker Compose (${{ matrix.variant }}). - Use sentence case imperative step names, for example
Check out repository,Set up Python,Install dependencies,Audit dependencies,Probe API container health, andTear down Docker Compose.
- Keep root
README.mdaligned withcookiecutter.json, hooks, and the generated project surface. - Keep generated-project documentation aligned with files under
{{cookiecutter.project_slug}}/. - When adding, renaming, or removing a job in
.github/workflows/ci.yaml, update the GitHubmainbranch protection required status checks in the upstream repository to match. Use each job's renderedname, including every matrix-expanded check such asBake example-api; new jobs are not enforced until added, and stale entries block merges until removed. - When adding, renaming, or removing generated-project workflow jobs under
{{cookiecutter.project_slug}}/.github/workflows/, document the required downstream branch-protection check names for generated repositories that use those workflows. - Keep operational constants fixed in generated code unless there is a real deployment need to configure them.
- Add environment variables only for secrets, deployment topology, or resource sizing.
- Use
example.complaceholders only in human-facing documentation and cookiecutter defaults; machine-consumed placeholders in template tests, CI, and the Docker build env use.testhostnames such assentry.example.test. The production boot sentinel deliberately checks forexample.combecause that is thedomain_namedefault. - Do not add empty optional values to
.env.example; document optional AWS variables as commented examples. - Keep Docker images pinned, not floating.
- Format complex shell commands over multiple lines with backslashes.
- For curl healthchecks, use compact short flags in the established style, such
as
-fsS -o /dev/null.
- Run relevant root checks before completion:
uv sync --lockeduv run --locked pytest testsuv run --locked pre-commit run --all-files
- Run the canonical full bake verification with
uv run --locked python scripts/verify_bake.py. - For template behavior changes, bake a project in a temporary output directory and run the generated-project checks that match the change.
- Freshly baked projects are expected to pass:
docker compose -f .docker/compose/dev.yaml --env-file=.env up -d --wait postgresuv run pytestuv run pre-commit run --all-filesdocker compose -f .docker/compose/dev.yaml --env-file=.env up -d --build --waitcurl -fsS http://localhost:8000/api/readydocker compose -f .docker/compose/dev.yaml --env-file=.env down -v
- Prefix these with
rtkwhen it is available.