Pre-push CI gate for Python — ruff format can introduce mypy errors

Two CI bounces on R-Dash PR #45 exposed a 3-tool pre-push checklist that, run in order, catches what each single tool misses:

  1. uv run ruff format --check . — catches line-length / wrap drift that ruff check accepts as lint-clean.
  2. uv run ruff check . — catches F401 unused imports, undefined names, etc.
  3. uv run mypy <changed-files> — catches type-narrowing regressions.

Critical caveat: when ruff format simplifies code shape (e.g. lhs: bytes = foo.private_bytes(...); return lhs collapses to return foo.private_bytes(...)), the type-narrowing assignment is dropped and mypy may report “Returning Any from function declared to return bytes”. Always re-run mypy on files reformatted by ruff format — never assume format-only changes are type-safe.

Cost when skipped: 2 CI bounces (~3 min each) before the trio passed in correct order.

Automation candidate: a .git/hooks/pre-push running the trio in order with non-zero exit on any failure catches both classes in 0 round-trips.

Reference: r-dash main commits 521d4d6 (ruff format) and f95a6ef (mypy bytes annotation fix); failed GHA runs 25967982404 and b98b86e CI.