fix(gfql): chain result contract: binding-column aliases decline, duplicate ids answer once, no internal columns (#2050, #2051, #2067) - #2056
Conversation
3cc3846 to
3312f00
Compare
7c6fc3d to
157a55c
Compare
|
READY (157a55c, stacked on #2055) — CI 60/60 non-skipped check-runs success (7 path-skipped lanes, same filter gap as #2055). tck-gfql run locally from its current main (66fee10) against this head: exit 0 — 4143 passed / 6 skipped / 689 xfailed (baseline), 2964 supported scenarios, direct-Cypher non-validation debt 0. Local suites: 2372 passed / 24 skipped with the cuDF lane; guards OK; mypy = master; ruff clean. Contract change (TO-side binding aliases now a typed decline instead of served-with-overwrite) is stated in the description for your call. |
157a55c to
f04fe11
Compare
|
GPU receipt (dgx-spark, graphistry/test-rapids-official:26.02-gfql-polars, cudf 26.02.01 / polars 1.35.2, safe_run, TEST_CUDF=1; suites test_chain.py, test_hop.py, test_chain_alias_column_collision.py, tests/compute/gfql/**) on the restacked stack head a54ac4b (#2065, containing #2055 rework, #2056, #2054, #2061, #2062): 13386 passed / 95 failed / 38 xfailed / 4 xpassed. 93 of the failures are master 86de0f5's polars-gpu fused-lane set (#2064, fixed separately in #2066). The other 2 are strict XPASS of the #2043 prune_to_endpoints markers on cuDF 26.02: the prune shapes now agree fast-vs-full on this stack (the #2062 closure fix removed the divergence), so #2062 drops that marker in a follow-up commit (tree otherwise identical; #2065 restacked). |
…ne on native chains too (#2050) The Cypher route already rejected an edge alias equal to the source or destination binding (E108); native chains kept an "overwrite parity" where the marker silently replaced the binding column on pandas and cuDF (the seed's edges vanished, or the edge ids became True) and polars raised a raw SchemaError. The same validator now runs for native chains and also covers the edge-ID binding, so every engine declines before execution, like the node-ID collision. Pins: the collision matrix's binding-column shapes flip from strict expected failure to green on every engine; the TO-side shapes test_chain.py pinned as served-with-overwrite are re-pinned as the typed decline on both routes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QztW7jYsDd66e8rb8pJNQA
…e id (#2051) The unnamed, untyped single-hop chain shape is served by the polars chain's skip-combine branch, which built its node frame with a semi-join against the node table and so kept a node table's duplicate rows for the same id, where the full polars chain, pandas and cuDF collapse them. Root cause confirmed by route-off amplification: with the branch forced to decline, the #2051 strict expected failures pass. The branch now applies the same one-row-per-id rule as the full chain's node materialization. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QztW7jYsDd66e8rb8pJNQA (cherry picked from commit 4fa87f3)
…real-GPU receipt), marker dropped (cherry picked from commit 347d975)
…line, never on op-list results The polars chain kept a shadowed user column under __gfql_shadow_restore__<alias>__ so the Cypher row pipeline could read the value back; that internal column also reached op-list results. Now a compiled Cypher execution marks itself (cypher_pipeline() context) and only then does the chain keep the column; on gfql([...]) / chain([...]) the marker shadows the column outright, as on pandas and cuDF. Nothing is stripped from results: user-defined columns (the name= marker, requested hop labels, a caller's own __gfql_-prefixed column) and pipelines composed from successive gfql calls are untouched. Pins: no internal column on pandas/cuDF/polars for the shadow shape with the marker and payload intact; Cypher still reads the shadowed user value on both engines; user hop labels and a user-prefixed column survive; the deprecated chain() surface is clean. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QztW7jYsDd66e8rb8pJNQA (cherry picked from commit 0b610b0)
11ad4f4 to
6d2c654
Compare
…ion kept the old strict xfail)
|
GPU receipt (dgx-spark, graphistry/test-rapids-official:26.02-gfql-polars, cudf 26.02.01 / polars 1.35.2, safe_run, TEST_CUDF=1; suites test_chain.py, test_hop.py, test_chain_alias_column_collision.py, tests/compute/gfql/**) on the folded stack top 500265e (#2062, containing #2056 and #2054): 13532 passed / 7 failed / 37 xfailed / 4 xpassed, versus master 86de0f5's 13227 / 93 in the same image. Every one of the 7 is inside master's set (5 polars-gpu strictness cases and the routing-contract / indexed-bypass pins tracked on #2064 and #1803); zero branch-specific failures, and the fused-lane fix folded from #2066 clears the other 86. |
Contract resolution (owner thread, 2026-09-06): op lists vs Cypher for an alias named like a binding columnOne rule on both surfaces, taken from openCypher: scope shadows, projection declines duplicate output names. (
Dynamic pipelines that generate alias names can pre-check against |
0cd9d65 to
2a645a3
Compare
Owner question: does the decline break TCK, q1–q9, or SNB? Checked, not reasoned.
Three dgx sentinel runs with the corrected shape, gate vs the 86de0f5 baseline:
The polars node-only misses reproduce on master itself, so they are sentinel variance around a fast baseline run, not this PR. Baseline swap after this merges (pyg-bench #250). Gap found by the same repro, fixed here (2a645a3)
|
…chain() surface The typed E108 decline for an alias named like a same-frame binding column lived only on the gfql() dispatch; g.chain() still reached the engines and died with a raw pandas ValueError or polars DuplicateError. The helper moves to chain.py and runs on both surfaces; pins cover both surfaces (decline) and the cross-frame case (served, marker present). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QztW7jYsDd66e8rb8pJNQA
2a645a3 to
5775f76
Compare
Receipts for head 5775f76
Stack restacked on this head: #2054 b5ba132 (own product delta byte-identical before and after the rebase; helper kept out of the moved block) and #2062 c2443da (own delta identical), both pushed with CI re-fired. |
|
Benchmarks pass with this PR, not after it. pyg-bench #250 now carries the corrected sentinel shape, a new gate baseline taken at this head (5775f76), and a per-engine drift tolerance derived from four back-to-back master runs. Gate at 5775f76 vs that baseline: ok, on all 18 points. Master fails that gate on exactly one field, the polars collision point's output hash, which is this PR removing the internal shadow-restore column from op-list output. TCK, q1–q9 and the SNB query arms are unaffected (checked above). #250 admin-merges on my side once its smoke CI is green. |
|
pyg-bench #250 merged (main 56873798e): sentinel shape + baseline at this head + per-engine drift tolerance are on main. The benchmark side of this change is closed. |
…, binding names are rejected Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QztW7jYsDd66e8rb8pJNQA
|
Docs-only commit 32ea472: the spec's Named Results section now states the marker rule in two sentences (the marker replaces a same-named column on the same frame; a binding-column name is rejected). No product or test change, so the GPU lane, broad-suite and sentinel receipts at 5775f76 stand; CI re-fired on 32ea472. |
|
CI on 32ea472: 77/77 green. Head is final. |
Three related contract fixes on the chain result, folded into one PR at the owner's request (formerly #2056 + #2060 + #2067). Base master; first of the three-PR stack (this → #2054 → #2062), then #2017 last.
src/dst/edge-id or a node alias named like the node id raises the sameGFQLValidationErrorthe Cypher route raises. TO-sidetest_chain.pypins re-pinned as typed declines (contract change stated here).cypher_pipeline()(context flag) marks a compiled Cypher execution; only then does the polars chain keep a shadowed user column under__gfql_shadow_restore__<alias>__for its row pipeline. Ongfql([...])/chain([...])the marker shadows the column outright, like pandas and cuDF. Nothing is stripped from results (a blanket strip was tried and rejected: it broke pipelines composed from successivegfqlcalls and frame-returning ops).Receipts on this head are posted below (CI, local, GPU on the stack top). Earlier receipts for each part are on the closed PRs #2060 and #2067.
🤖 Generated with Claude Code
https://claude.ai/code/session_01QztW7jYsDd66e8rb8pJNQA