CI: enforce ty type-checking + pre-commit config (ruff + ty)#125
Conversation
ty was only available locally via Homebrew, so CI's ubuntu runner had no type checker. Pin ty and pre-commit in the dev group (updating uv.lock) so `uv sync` provides them deterministically in CI, locally, and in the hook. mne is the optional 'viz' extra, absent from the base env; add scoped `# ty: ignore[unresolved-import]` to its two lazy imports (matching the mlx treatment) so `ty check .` is clean on the plain base env without pulling optional extras -- the foundation for the CI gate and pre-commit hook.
New 'Type check (ty)' job runs 'uv run ty check .' on the base env; test and build now need [lint, typecheck] so a type error fails fast before the ~10-min test run.
Local hooks run ruff (check --fix + format, staged files) and ty (whole project, since type errors are cross-file) through uv, so versions match pyproject/CI with no mirror drift. Install with: uv run pre-commit install.
Address PR #125 review: ty is version-locked via uv.lock (pyproject uses a lower bound), and note uv sync as a prerequisite for pre-commit install.
/review-pr summary (2 Sonnet reviewers: code + comments)No Critical or Important findings. Both reviewers verified the CI + pre-commit wiring end-to-end (locally ran Confirmed correct: the acyclic Addressed (minor precision nits, commit 1065499)
No other changes needed. |
Summary
Closes #119. Enforces
tytype-checking in CI and adds a.pre-commit-config.yamlwith ruff + ty hooks. Unblocked by #122 (repo is now at 0
tydiagnostics), soty checklands as a hard gate (option (a) from the issue), not informational.Changes
ty+pre-commitpinned in the dev group (pyproject.toml+uv.lock).tywas only available locally via Homebrew, so CI's ubuntu runner had no typechecker at all. Pinning both means
uv syncprovides the same versions in CI,locally, and in the hook (ty 0.0.59, pre-commit 4.x).
CI
typecheckjob (.github/workflows/ci.yml): a newType check (ty)job runs
uv run ty check .on the plain base env.testandbuildnowneeds: [lint, typecheck], so a type error fails fast before the ~10-min testrun. Added as its own job (not folded into
lint) so it surfaces as a distinctstatus without renaming the existing
Lint (ruff)check..pre-commit-config.yaml:localhooks running ruff + ty through uv,so versions track pyproject/CI with no pre-commit-mirror drift.
check --fix --unsafe-fixes+formaton staged Python files.ty check .whole-project (pass_filenames: false): type errors areinherently cross-file, so per-file checking is unsound; ty is fast enough
(Rust, sub-second here) to re-check everything each commit. Fires only when a
Python file is staged, and runs the identical command to the CI job, so a
green pre-commit implies a green CI gate.
Scoped
# ty: ignore[unresolved-import]on the two lazymneimports inbenchmarks/benchmark_decompose.py.mneis the optionalvizextra, absentfrom the base env; this matches the existing
mlxtreatment and keepsty check .clean on the base env (so every dev and the hook see 0 diagnosticswithout installing optional extras), rather than coupling the gate to
--extra viz.Design notes
"staged files only" for ty specifically): a type checker that only sees staged
files misses errors an edit introduces in other files. ruff (a fixer) stays
staged-only where scoping actually matters. The whole-project ty run is cheap.
mlx(Apple-only) can neverresolve on Linux, and
mne(viz extra) would need a heavier CI env and stillleave local
ty check .dirty for anyone without the extra. Scoped inlineignores keep the base-env check honestly clean and the gate simple.
Validation
uv run ty check .-> All checks passed! (0 diagnostics, base env)uv run pre-commit run --all-files-> ruff-check / ruff-format / ty all Passeduv run ruff check/ruff format --checkcleanpytest test_decompose_equiv.pygreen (only Python change is the two comments)