- **`master` is green again: the type-hygiene ratchet tripped on merge, and the fix is fewer `cast()`s rather than a raised cap**: `bin/ci_type_hygiene_baseline.json` is a per-file SNAPSHOT, so it goes stale whenever a PR *other than the one that owns the baseline* touches a baselined file. #1830 captured its baseline on a branch whose merge-base is `b6181d355` — before #1800, #1799 and #1816 landed — and each of those three added `cast()` calls to a file #1830 had already pinned. All four were green on their own bases; the combination was not, and nothing in any of the four merges signalled it: `explicit-cast` came out 7 over baseline across `gfql_fast_paths.py` (+5), `gfql_unified.py` (+1) and the polars `row_pipeline.py` (+1), failing `python-lint-types` on 3.11/3.13/3.14. The baseline is **unchanged** here; the findings were removed instead. Three of the seven were never `typing.cast` at all — the guard matches any call whose name is `cast`, including the attribute form, so `pl.Expr.cast`, a polars RUNTIME dtype conversion, counts as a typing finding; those three carry the documented `# hygiene-ok: explicit-cast` escape hatch with the reason. The other four were real, and are gone by DECLARATION rather than by call-site assertion, which is the idiom the guard is asking for: `_two_hop_cached_equal_domain_degree_counts` declares `counts: Tuple[DataFrameT, DataFrameT]` once and both arms assign to it, so four casts collapse to one localized `# type: ignore[assignment]` on the polars arm (`DataFrameT` is pinned to pandas at checking time); `_apply_connected_optional_match` declares `seed_ids: SeriesT` / `node_ids: SeriesT` instead of casting, because selecting one column off a frame is a Series on every engine. (The neighbouring `cast(DataFrameT, df_to_engine(...))` pair is deliberately left alone: `df_to_engine` carries no return annotation, so removing those would mean annotating a helper with 115 call sites, and the polars arm of that branch is not reached by any CI lane — rewriting it would have dragged a pre-existing coverage blind spot into the changed-line gate for no typing gain.) All three files now sit AT or BELOW baseline (18/18, 132/133, 41/42). Typing-only: `typing.cast` is the identity function at runtime, so every removal is provably value-preserving, and the one restructured line binds an unchanged `pl.Expr` list to a name so the per-line suppression fits the 127-column limit. **The hazard is latent, not spent** — any future PR adding a finding to a baselined file reds `master` the same way, and the guard cannot see it from inside either PR.
0 commit comments