Round-008 scoping probe: ~175 query-cases × both engines over 6 fixtures, oracles pre-registered in writing before any code ran, every finding re-verified in a fresh process with different label/type names. Scripts in session scratchpad probe-r8-scope/.
Scoping is strong where it counts — scope narrowing (17 cases) errors clearly on every dropped node/rel/path alias with zero silent re-binding; OPTIONAL cross-clause visibility (11), parameter/alias collision (8), degenerate scopes (9/9), alias-kind confusion (11, declines only), and an internal-column leak audit were all QUIET. Four silent-wrongs survived.
1. SILENT-WRONG (pandas, incl. default engine): WITH X AS Y where Y is already bound returns all-NULL properties.
MATCH (a:P)-[:K]->(b:P) WITH a AS b RETURN b.name
oracle Sa, Sb · pandas None, None · polars correct. The renamed alias takes its ROWS from the source alias but resolves .property against the SHADOWED alias's node table — on disjoint node sets that is 100% data loss. Symmetric in both directions, both dtypes, 3 fixtures, survives ORDER BY. Discriminators: whole-entity RETURN b is correct on both engines (only property access corrupts), and WITH a AS fresh (non-colliding) is a clean typed decline — so the guard exists and is bypassed exactly when the target name is another live alias.
2. SILENT-WRONG (BOTH engines): simultaneous rebind ignored. WITH a AS b, b AS a returns the unswapped values, byte-identical to the no-WITH control (openCypher WITH projections are simultaneous; oracle is the swap). Same with item order reversed. Also WITH a, b WITH a AS b RETURN b.name returns the shadowed original. Note test_ir_query_graph.py:345 shows the IR already models WITH a AS b correctly — the defect is downstream in execution. Cross-kind rebind IS guarded (#1357); same-kind onto a live alias is not.
3. SILENT-WRONG (pandas) + HARD CRASH (polars): user edge column named __gfql_edge_ident__. Introduced by this campaign's trail work. gfql_unified.py:671 _with_edge_identity tests the hardcoded literal instead of using generate_safe_column_name, contrary to the convention lazy/engine/polars/reserved_columns.py mandates:
- pandas:
RETURN r.__gfql_edge_ident__ → nulls (oracle E1, E2); whole-entity RETURN r drops the column entirely.
- polars: every relationship query on such a graph dies, including ones never mentioning the column —
duplicate column name __gfql_edge_ident__ from row_pipeline.py:1775's unconditional with_row_index.
Node-side __gfql_edge_ident__ and both-side __cypher_group__ are handled correctly; this is edge-ident-specific. Two-line fix with the helper the repo already mandates.
4. Alias named identically to the property it projects returns True — MATCH (name:P) RETURN name.name → True, True. Root cause compute/ast.py:273: assign(**{self._name: True}) overwrites the user column with the alias marker. The node-side case is already pinned by a test asserting cross-engine parity — but that asserted parity is broken in three uncovered variants: rel-alias-equals-edge-column (pandas True / polars typed error), MATCH (id:P) RETURN id.id (pandas raw untyped ValueError / polars True), and a WHERE variant (pandas schema error / polars correct []). Corruption is contained (doesn't spread to other aliases; WHERE runs before tagging).
Lower severity: WITH $x AS x ... WHERE a.name = x leaks the substituted value as a token in the error; a misattributed "Unresolved identifier 'a'" where the fresh alias is actually q; path aliases are never bound at all (MATCH p = ... RETURN p.name → "Unknown Cypher alias 'p'" with a misleading suggestion); cosmetic null/dtype divergences (nan vs None, 5.0 vs 5).
Priority: 1+2 share one root (unguarded same-kind rebind onto a live alias) — either implement true simultaneous rebind or extend the existing guard to reject it, mirroring #1357. 3 is a two-line fix and is a regression this campaign introduced.
Round-008 scoping probe: ~175 query-cases × both engines over 6 fixtures, oracles pre-registered in writing before any code ran, every finding re-verified in a fresh process with different label/type names. Scripts in session scratchpad probe-r8-scope/.
Scoping is strong where it counts — scope narrowing (17 cases) errors clearly on every dropped node/rel/path alias with zero silent re-binding; OPTIONAL cross-clause visibility (11), parameter/alias collision (8), degenerate scopes (9/9), alias-kind confusion (11, declines only), and an internal-column leak audit were all QUIET. Four silent-wrongs survived.
1. SILENT-WRONG (pandas, incl. default engine):
WITH X AS YwhereYis already bound returns all-NULL properties.oracle
Sa, Sb· pandasNone, None· polars correct. The renamed alias takes its ROWS from the source alias but resolves.propertyagainst the SHADOWED alias's node table — on disjoint node sets that is 100% data loss. Symmetric in both directions, both dtypes, 3 fixtures, survives ORDER BY. Discriminators: whole-entityRETURN bis correct on both engines (only property access corrupts), andWITH a AS fresh(non-colliding) is a clean typed decline — so the guard exists and is bypassed exactly when the target name is another live alias.2. SILENT-WRONG (BOTH engines): simultaneous rebind ignored.
WITH a AS b, b AS areturns the unswapped values, byte-identical to the no-WITH control (openCypher WITH projections are simultaneous; oracle is the swap). Same with item order reversed. AlsoWITH a, b WITH a AS b RETURN b.namereturns the shadowed original. Notetest_ir_query_graph.py:345shows the IR already modelsWITH a AS bcorrectly — the defect is downstream in execution. Cross-kind rebind IS guarded (#1357); same-kind onto a live alias is not.3. SILENT-WRONG (pandas) + HARD CRASH (polars): user edge column named
__gfql_edge_ident__. Introduced by this campaign's trail work.gfql_unified.py:671_with_edge_identitytests the hardcoded literal instead of usinggenerate_safe_column_name, contrary to the conventionlazy/engine/polars/reserved_columns.pymandates:RETURN r.__gfql_edge_ident__→ nulls (oracle E1, E2); whole-entityRETURN rdrops the column entirely.duplicate column name __gfql_edge_ident__fromrow_pipeline.py:1775's unconditionalwith_row_index.Node-side
__gfql_edge_ident__and both-side__cypher_group__are handled correctly; this is edge-ident-specific. Two-line fix with the helper the repo already mandates.4. Alias named identically to the property it projects returns
True—MATCH (name:P) RETURN name.name→True, True. Root causecompute/ast.py:273:assign(**{self._name: True})overwrites the user column with the alias marker. The node-side case is already pinned by a test asserting cross-engine parity — but that asserted parity is broken in three uncovered variants: rel-alias-equals-edge-column (pandasTrue/ polars typed error),MATCH (id:P) RETURN id.id(pandas raw untypedValueError/ polarsTrue), and a WHERE variant (pandas schema error / polars correct[]). Corruption is contained (doesn't spread to other aliases; WHERE runs before tagging).Lower severity:
WITH $x AS x ... WHERE a.name = xleaks the substituted value as a token in the error; a misattributed "Unresolved identifier 'a'" where the fresh alias is actuallyq; path aliases are never bound at all (MATCH p = ... RETURN p.name→ "Unknown Cypher alias 'p'" with a misleading suggestion); cosmetic null/dtype divergences (nanvsNone,5.0vs5).Priority: 1+2 share one root (unguarded same-kind rebind onto a live alias) — either implement true simultaneous rebind or extend the existing guard to reject it, mirroring #1357. 3 is a two-line fix and is a regression this campaign introduced.