Lint + type + test + (optionally) seed + build-and-push image. The matrix runs per Python version. The pipeline is a contract: no green pipeline, no merge.
Job 1: ruff + black —check + mypy.
Job 2: pytest with —cov-fail-under=80.
Job 3: docker build, push to ghcr.io, sign with cosign.
Job 4: alembic upgrade head against a temporary Postgres.
PR -> lint+type+test -> main -> build image -> push to GHCR -> deploy K8s
Each step gates the next
Pipeline is the audit log. If a step skipped, the PR is suspect.