ci: type-check the backend — the gate's ninth lane (E27 S3)

Three tsc projects held the frontend and NOTHING held the backend: ~36k
lines of `app/`, annotated in 113 of 128 files, that no lane had ever
read. Asymmetric by accident, not by decision.

basedpyright, in basic mode. Measured against mypy on this tree with the
dependencies actually installed — without them basedpyright errors on
every import and mypy's --ignore-missing-imports quietly turns them into
Any and UNDER-reports. Both land ~190-200 errors with the same
distribution, so the choice fell to what differs: basedpyright is ~2x
faster (4.4s vs 7.4s, and this runs on every `siftlode lint`), resolves
our `app.*` imports from its own config with no MYPYPATH, and has a
first-class `--writebaseline`. That last one is the ratchet itself;
mypy would have needed a third-party tool for it.

198 pre-existing errors are baselined, so the lane fails on NEW errors
only and the list burns down epic by epic. A green lane means no new
damage, not a clean codebase.

It runs in the pytest test image rather than on the host, and that is
forced rather than chosen: `requirements.lock` cannot be installed on
Windows at all — uvloop refuses to build there — so a host venv could
only ever be the lock minus a package, and a type checker without the
real third-party packages reports nonsense rather than nothing. The
image installs the pin from requirements-dev.txt, so the lane's version
IS the pin and there is no host copy to drift. The cost is that
`siftlode lint` now needs Docker, which it did not before.
This commit is contained in:
2026-09-20 23:02:02 +02:00
parent 51daf3e388
commit c9101e3450
5 changed files with 1719 additions and 1 deletions
+24
View File
@@ -0,0 +1,24 @@
// The backend type-check lane (E27 S3) — the gate's ninth lane, and the first thing that has ever
// type-checked the ~36k lines of `app/`. Read as JSONC: pyright accepts comments here.
//
// basedpyright, not mypy: measured on this tree with the dependencies actually installed, both land
// ~190-200 errors with the same distribution, so the choice was made on the things that differ —
// basedpyright is ~2x faster, needs no MYPYPATH to resolve our `app.*` imports, and has a FIRST-CLASS
// baseline (`--writebaseline`), which is the whole ratchet. mypy would have needed a third-party tool
// for that.
//
// It runs in the pytest test image, never on the host: `requirements.lock` cannot be installed on
// Windows at all (uvloop refuses to build there), so a host run would type-check against absent
// third-party packages and report nonsense. The version is pinned in requirements-dev.txt, which the
// image installs — so the lane's version IS the pin, with no host-vs-pin check to drift.
//
// `pythonVersion` is not free-standing: `/.python-version` is the single source of truth and
// `tests/test_python_engine_contract.py` fails this gate if this file, the two Dockerfiles, the CI
// container or ruff's target-version disagree with it.
{
"include": ["app"],
"extraPaths": ["."],
"typeCheckingMode": "basic",
"pythonVersion": "3.12",
"pythonPlatform": "Linux"
}