Skip to content

fix(gfql): WITH->MATCH carry restricts every route (#1712 residual); alias-shadowed property reads (#1911 defect-4) - #1950

Merged
lmeyerov merged 11 commits into
masterfrom
fix/gfql-1911d4-1712-carry
Aug 19, 2026
Merged

lmeyerov merged 11 commits into
masterfrom
fix/gfql-1911d4-1712-carry

Conversation

@lmeyerov

Copy link
Copy Markdown
Contributor

Two verified reentry/carry-family defects, fixed against master 8f4041336 with red-at-master pins, anti-vacuity counts, and a killed-mutant check per fix. Partial-scope residuals are pinned and called out on the issues; #1911 and #1712 stay open (refs #1712, refs #1911 — no auto-close).

1. #1712 residual — WITH→MATCH re-entry seeds ignored by three execution routes

MATCH (p …) WITH p[, p.x AS t] MATCH (p)-… must restrict the trailing MATCH to the carried p rows. The bare single-pattern shape was fixed earlier; three routes still re-matched from the whole graph:

route before (pandas/cuDF) after
single-hop grouped-aggregate fast path (RETURN t, count(p)) extra NULL-keyed group of the 2 un-carried persons: [{t: Person, n:1}, {t: NaN, n:2}] [{t: Person, n:1}]
connected comma-pattern join (MATCH (p)-→(c), (p)-→(d)), bare AND projection carry un-carried person leaked: [{pid:0,…},{pid:1,…}] (also polars) [{pid: 0, pets: 1}] (all three engines)
two-hop count fast path (MATCH (p)-→(c)-→(x) RETURN count(*)) n=3 (also polars) n=1

Mechanics: the two seed-blind fast paths now decline when a reentry seed is present (reentry_start_nodes) and fall back to the seeded chain path; the connected join post-filters its joined rows to the carried seed ids via a new restrict_connected_join_rows_to_reentry_seed (typed E108 decline when the alias binding column cannot be recovered). polars' pre-existing typed NIE for scalar-carry reentry is pinned as a decline.

NULL-in-the-carried-column: WITH p, p.nickname AS t where the carried person's nickname is NULL came back as the sentinel string '0000-00-00' — a vacuously-true all-null temporal-constructor probe in entity_props._all_non_null_match. All-null series no longer "prove" any constructor shape; the NULL survives the carry.

Remaining on #1712: polars still typed-declines scalar-carry reentry (WITH p, p.x AS t MATCH …) — pre-existing NIE, pinned; native polars support is future work.

2. #1911 defect-4 — alias shadowing a property name reads the marker, not the values

MATCH (name:P) RETURN name.name answered [True]×4 on pandas + polars and crashed cuDF with a raw mixed-dtype TypeError. Root cause: the alias flag is stamped as <alias> = True, overwriting the same-named user column that property reads then consume.

  • chain.combine_steps now coalesces alias-marker _x/_y pairs as markers (boolean, null→False) instead of .where-merging marker booleans with user values (the cuDF crash, and the pandas empty-match column drop).
  • The single-alias rows(table=…, source=alias) route restores the user values from the base frame under the dotted self-name (name.name) — index-keyed with a key-merge fallback — keeping the boolean marker intact for every other property read; polars re-keys via id join.
  • The cartesian binding path unshadows like the connected one (both pandas/cuDF and the native polars twin, which had reproduced the leak deliberately).
  • Result projection resolves alias.alias property columns from the restored dotted column; whole-entity flatten keeps its documented behavior (shadowed column omitted).
  • New typed decline (E108) for a Cypher edge alias named after an edge endpoint binding column (MATCH (a)-[s:K]->(b) with source bound to s): previously an incidental GFQLSchemaError, and the marker-aware coalesce would have made it silently empty. Scoped to Cypher chains — raw GFQL chains keep their documented overwrite parity (test_fast_path_alias_shadowing_column_matches_full_path unchanged).
  • Same-family verify+pin: whole-entity RETURN r with a user column literally named __gfql_edge_ident__ keeps omitting dunder-named user columns from the flattened entity (generic internal-name policy) while explicit r.__gfql_edge_ident__ projection returns the user's values — no crash, no misattribution.

Remaining on #1911: (a) polars edge alias colliding with the very column its own type filter reads (MATCH (a)-[type:K]->(b) RETURN type.type) stays a typed GFQLSchemaError (honest decline; pandas/cuDF answer K,K); (b) polars multi-hop relationship-alias row over-multiplication (independent of the alias name) — both pinned as residuals; (c) MATCH (id:P) RETURN id.id remains the pre-existing typed decline by design.

Verification

  • Pins red at master 8f4041336: 35 failing cells across test_reentry_carry_seed_restriction.py (new) and test_alias_scoping_semantics.py (extended); all green at head; anti-vacuity via distinct multi-row oracles (a marker leak, an unrestricted count, or an empty frame cannot pass).
  • Mutation check: 11 targeted mutants (one per fix line-cluster) — all killed by the pins.
  • Engines: pandas + polars + cuDF 25.10 (dataframe ops) exercised locally; typed-decline pins where an engine declines.
  • Gates: ruff clean; comment-density / cypher-surface / type-hygiene guards rc=0; mypy — no new errors vs master; full graphistry/tests/compute failure SET identical to the master baseline; changed-line coverage clean locally (no cuDF-only changed lines); new polars-mentioning test file registered in bin/test-polars.sh (lane-completeness gate green).

🤖 Generated with Claude Code

https://claude.ai/code/session_01AjbKuKheqDu78oapRT5AYm

lmeyerov and others added 10 commits August 18, 2026 18:37
…ing base frames

The dotted self-name flipped the bindings-alias-prefix heuristic (NA-ing sibling
property reads) and an intermediate dispatch base frame's own marker could be
misread as user data (comma-pattern r.weight -> NA).

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AjbKuKheqDu78oapRT5AYm
Same version-skew class as the #1948 pin fix: three alias-scoping pins compared
nan == None and failed only on the py3.13 lane. Normalizing once in _rows
covers every record assertion in the file.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AjbKuKheqDu78oapRT5AYm
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant