fix(lint): turn master green by removing the 7 casts over the type-hygiene baseline - #1833
Merged
Merged
Conversation
…giene baseline The per-file ratchet in bin/ci_type_hygiene_baseline.json is a SNAPSHOT, so it goes stale whenever a PR other than the one that owns it touches a baselined file. #1830 captured its baseline on a branch whose merge-base is b6181d3 -- 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 merged combination was not. The baseline is UNCHANGED. The findings are removed instead: - 3 of the 7 were never typing.cast. The guard matches any call named `cast`, including the attribute form, so pl.Expr.cast -- a polars RUNTIME dtype conversion -- counts as a typing finding. Those carry the documented `# hygiene-ok: explicit-cast` escape hatch with a reason. - The other 4 are real and are gone by DECLARATION rather than by call-site assertion. _two_hop_cached_equal_domain_degree_counts declares `counts: Tuple[DataFrameT, DataFrameT]` once, collapsing four casts into one localized `# type: ignore[assignment]` on the polars arm. _apply_connected_optional_match declares `seed_ids: SeriesT` / `node_ids: SeriesT`, since selecting one column off a frame is a Series on every engine. 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. The one restructured line binds an unchanged pl.Expr list to a name so the per-line suppression fits the 127-column limit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YYZRXegrALuXd3NHH5evqx
lmeyerov
force-pushed
the
fix/type-hygiene-baseline-drift
branch
from
July 29, 2026 03:14
5dfc83d to
d5b3858
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
master(68638794c) is RED:python-lint-typesfails on 3.11 / 3.13 / 3.14 with the type-hygiene guard 7cast()findings over baseline.This PR removes those 7 findings.
bin/ci_type_hygiene_baseline.jsonis not touched — no cap is raised.Root cause (verified, not assumed)
bin/ci_type_hygiene_baseline.jsonis a per-file snapshot, so it goes stale whenever a PR other than the one that owns the baseline touches a baselined file.Verified directly:
#1830 (which added the guard + baseline) branched from
b6181d355and contains none of #1800 / #1799 / #1816 — each of which addedcast()calls to a file #1830 had already pinned. All four PRs were green on their own bases; the merged combination was not, and nothing in any of the four merges could signal it.Confirmed by running the guard against both trees:
b6181d355yields 193explicit-castfindings across the three files,68638794cyields 200 — exactly +7 (+1 / +5 / +1).A second, separate finding: 3 of the 7 were never
typing.castThe guard matches
ast.Attributecalls namedcastas well asast.Nameones (correctly, so it catches qualifiedtyping.cast(...)), but that also makespl.Expr.cast— a polars RUNTIME dtype conversion — count as a typing finding:row_pipeline.pycol.cast(pl.String)gfql_fast_paths.py.sum().fill_null(0).cast(pl.Int64)gfql_fast_paths.pypl.col(c).cast(pl.String)These are load-bearing runtime code with nothing to do with type hygiene, so they use the guard's documented escape hatch (
# hygiene-ok: explicit-cast -- polars dtype cast) with a real reason, rather than a re-baseline.The other 4 were real casts, and are gone by declaration
Removed the way the guard's own message asks for — a correct annotation, not a call-site assertion:
_two_hop_cached_equal_domain_degree_counts(gfql_fast_paths.py): declarescounts: Tuple[DataFrameT, DataFrameT]once, so both arms assign directly. Four casts collapse into one localized# type: ignore[assignment]on the polars arm (DataFrameTis pinned to pandas at checking time)._apply_connected_optional_match(gfql_unified.py): declaresseed_ids: SeriesT/node_ids: SeriesTinstead of casting — selecting one column off a frame is a Series on every engine, so the annotation states that directly.The neighbouring
cast(DataFrameT, df_to_engine(...))pair is deliberately left alone. Two reasons:df_to_enginecarries no return annotation and has 115 call sites, so annotating it is out of scope for a go-green PR; and the polars arm of that branch (gfql_unified.py:412) is not executed by any CI lane — traced directly,engine='polars'never enters this function at all, andengine='pandas'only ever takes theelsearm. Rewriting it would have dragged a pre-existing coverage blind spot into thechanged-line-coveragegate for zero typing gain. (An earlier revision of this PR did exactly that and the gate failed at 75%; worth knowing that this blind spot is there.)Result
row_pipeline.pygfql_fast_paths.pygfql_unified.pypython ./bin/ci_type_hygiene_guard.py→ exit 0,Type-hygiene guard OK (4555 grandfathered finding(s), no growth)../bin/lint.sh→ exit 0./bin/typecheck.sh→Success: no issues found in 327 source filesNo runtime behaviour change
typing.castis the identity function at runtime, so every removal is provably value-preserving. The remaining edits are comments plus one line that binds an unchangedpl.Exprlist to a name (to_string = [...]) so the guard's per-line suppression fits the 127-column limit. Net diff: 3 source files, ~20 lines.THIS IS A LATENT HAZARD, NOT A ONE-OFF
The baseline is a snapshot, so any future PR that adds a finding to a baselined file will red
masterexactly the same way, and neither PR can see it from the inside: the PR that adds the finding is green on its base, and the baseline owner is green on its own. This will recur.Recommended follow-up (deliberately not implemented here, to keep this PR minimal):
masterrather than only a hard failure at merge time, so staleness is visible before it breaks the branch.explicit-castso<expr>.cast(...)method calls are not counted while qualifiedtyping.cast(...)still is — 3 of the 7 findings here were false positives of that rule.Overlap
PR #1832 (
gfql/1806-residual-translator-widen) is open and also touchesgfql_fast_paths.pyandrow_pipeline.py. That branch was not touched here, and this diff was kept as small as possible, but a textual conflict is possible. Note thatgfql_fast_paths.pynow sits one under baseline, which gives #1832 a small amount of headroom rather than none.Test evidence
Run on
dgx-spark(GB10) ingraphistry/test-rapids-official:26.02-gfql-polarswith--gpus all --network none -e PYTHONDONTWRITEBYTECODE=1. Image versions: polars 1.35.2 / cudf 26.02.01 / pandas 2.3.3.gfql-core (the exact file list from the
test-gfql-coreCI lane):All 7 failures are pre-existing on unmodified
master(6863879) — the identical 7, re-run against a pristine68638794ccheckout in the same container, fail identically (7 failed in 1.81s). They are cuDF-on-GB10 environment failures (cudf/core/frame.py:559: TypeError) and none touch the code this PR changes:polars (
./bin/test-polars.sh, both phases):One deselection:
test_engine_coercion.py::TestChainCoercion::test_chain_dask_edges, which also fails identically on pristinemasterin the same container (a dask coercion assertion, unrelated to this diff). It was deselected only so the lane's second phase could run; with it included the lane is1 failed, 5061 passed.Net: zero new failures on either lane.
🤖 Generated with Claude Code
https://claude.ai/code/session_01YYZRXegrALuXd3NHH5evqx