Skip to content

[META] Cypher binder coverage gaps blocking #1357 strict_name_resolution rollout #1371

Description

@lmeyerov

Meta-issue tracking the binder coverage gaps surfaced when PR #1372 (work on #1357) ran a discovery flip of strict_name_resolution=True at graphistry/compute/gfql/cypher/lowering.py:8400 and observed 75 test failures in graphistry/tests/compute/gfql/cypher/. Most failures are false positives in strict mode — pre-existing binder limitations, not real-bugs-strict-catches. Each gap below is a separate binder fix that, once landed, unlocks one slice of the full strict flip.

PR #1372 ships the safe portion of #1357 (cross-kind alias rebind guard at _bind_node_pattern / _bind_relationship_pattern / _bind_path_alias — node↔edge, edge↔node, path↔node, path↔edge). Validator strict mode (gfql_validate(..., strict=True)) already exercises all of the below; runtime parity follows the items below.

Discovery analysis: plans/1357-binder-strict-name-resolution/research/discovery-flip-blast-radius.md (in PR #1372).
Regression-pin baselines: graphistry/tests/compute/gfql/cypher/test_binder_strict_compile_baseline.py (each parametrized case will flip from "admits" → "raises" as gaps close).
Validator/runtime parity ledger: graphistry/tests/compute/gfql/cypher/test_validator_runtime_strict_parity.py.

Gaps in priority order

P1 — Post-WITH UNWIND traversal order in _bind_graph_sequence

Symptom: MATCH ... WITH collect(b1) AS bees UNWIND bees AS b2 MATCH ... — strict binder rejects bees as unresolved.

Root cause: _bind_graph_sequence (binder.py:289) iterates ast.unwinds before the WITH stages. The parser puts post-WITH unwinds into ast.unwinds, so the binder evaluates the UNWIND expression against pre-WITH scope.

Fix: interleave UNWIND/WITH/MATCH by AST text position in _bind_graph_sequence, or fix the parser to route post-WITH UNWINDs to ast.reentry_unwinds.

Why P1: every LDBC SNB IC1/IC4/IC6 shape that uses collect(...) AS x UNWIND x AS y MATCH (...) is blocked by this — the highest-traffic shape on the benchmark lane. ~12 of the 75 failures are direct hits, plus most of the post-with-match-collect-unwind-match category (~5 more).


P2 — Quantifier predicates and list comprehensions

Symptom: MATCH (n) WHERE all(x IN n.labels WHERE x = 'A') RETURN n — strict rejects x as unresolved.

Root cause: _infer_expression_binding (binder.py:942) does not bind comprehension-scoped locals (x in all(x IN list WHERE ...), [x IN list WHERE ... | ...]).

Fix: in the strict-name-resolution unresolved-identifier check (_unresolved_identifiers in binder.py), recognize comprehension-binder syntax (all|any|none|single|[ ... IN ... ]) and exclude the local from the unresolved set.

Why P2: ~7 of 75 failures, common in WHERE clauses across many SNB shapes (IC*, BI*).


P3 — CALL/YIELD scope through prepass→normalize→bind

Symptom: CALL graphistry.degree() YIELD nodeId RETURN nodeId — strict rejects nodeId in RETURN.

Root cause: _bind_call_clause (binder.py:561) populates the scope from clause.yield_items, but the post-normalize bind path may receive a normalized AST shape that drops the YIELD aliases before re-binding. Need to verify and fix the cycle.

Fix: ensure YIELD aliases survive prepass→ASTNormalizer→bind. Likely a normalizer-side fix (don't strip YIELD context on rewrite).

Why P3: ~4 of 75 failures, narrow to graphistry.* / cugraph.* CALL paths.


P4 — Namespaced builtin function calls

Symptom: RETURN duration.inSeconds(localtime(), localtime()) AS duration — strict rejects duration as unresolved alias.

Root cause: _PROPERTY_RE.fullmatch(\"duration.inSeconds\") matches and routes through the alias-scope check; the binder does not recognize duration.* as a builtin namespace.

Fix: in _validate_property_ref_schema and _infer_expression_binding, short-circuit the alias-scope check when the prefix is a known builtin namespace (duration, time, date, datetime, localtime, localdatetime, point, …).

Why P4: ~5 of 75 failures, isolated to temporal/duration test fixtures. Niche.


Smaller items

  • type(x) builtin not modeled by _infer_expression_binding (~3 failures).
  • Aggregate precedence after collect() + range comparison (~3 failures).
  • [x IN list | x.foo] map-comprehension form — same root cause as P2.

Closure criteria

Once all four P1-P4 are landed, the post-normalize strict flip at graphistry/compute/gfql/cypher/lowering.py:8400 (FrontendBinder().bind(query, PlanContext(), strict_name_resolution=True)) becomes safe. The regression-pin baselines in test_binder_strict_compile_baseline.py will then flip from "admits" → "raises" as the corresponding fix lands. When the file is empty (all cases moved to parity in test_validator_runtime_strict_parity.py), this meta closes alongside #1357.

Related

Status (2026-05-09)

Status (2026-05-09)

Activity

  1. lmeyerov commented on May 9, 2026

    @lmeyerov
    ContributorAuthor

    Merged: #1384 (c9eedc47) landed the P1 slice for this meta.

    What landed (P1):

    • Strict binder traversal-order fix in FrontendBinder._bind_graph_sequence so post-WITH UNWIND resolves WITH-projected aliases in source order.
    • Scoped to strict_name_resolution=True path to avoid changing loose/runtime behavior during staged rollout.
    • Test amplification included:
      • strict post-WITH UNWIND alias resolution
      • multi-WITH + post-WITH UNWIND sequencing
      • strict scalar-UNWIND alias rebind rejection
      • baseline/parity updates removing obsolete P1 divergence pins.

    PR: #1384

    Remaining scope in this meta stays open for P2/P3/P4.

  2. added 2 commits that reference this issue on May 9, 2026
  3. lmeyerov commented on May 9, 2026

    @lmeyerov
    ContributorAuthor

    Merged via PR #1386 on 2026-05-09.

    Thanks all.

  4. added 4 commits that reference this issue on May 9, 2026
  5. lmeyerov commented on May 9, 2026

    @lmeyerov
    ContributorAuthor

    Post-merge update for P3 (CALL/YIELD scope):\n\n- Merged: https://github.com/graphistry/pygraphistry/pull/1387\n- Merge commit: cb5eaef\n- CI: green on both push + pull_request workflows after rebase/changelog update\n\nDelivered in this slice:\n- now binds before row-sequence projection binding for row-only shapes, so strict mode resolves aliases correctly.\n- Added strict regressions for direct + aliased YIELD projections.\n- Updated with the #1371 P3 entry under Development/Internal.\n\nThis closes the P3 lane previously tracked under #1371.

  6. lmeyerov commented on May 9, 2026

    @lmeyerov
    ContributorAuthor

    Post-merge update for P3 (CALL/YIELD scope):

    Delivered in this slice:

    This closes the P3 lane previously tracked under #1371.

  7. added 6 commits that reference this issue on May 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions