Skip to content

feat(cypher): admit simple free-form intermediate MATCH (#1263) - #1279

Merged
lmeyerov merged 7 commits into
masterfrom
feat/1263-freeform-intermediate-match-impl
May 4, 2026
Merged

lmeyerov merged 7 commits into
masterfrom
feat/1263-freeform-intermediate-match-impl

Conversation

@lmeyerov

@lmeyerov lmeyerov commented May 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes the conservative half of #1263 (LDBC SNB IC3 endpoint, "free-form intermediate MATCH after WITH"): admit the trailing MATCH whose first alias is not in the prefix WITH's carried whole-row set, when no carried-alias property is referenced in the trailing scope. The carried-property variant (the literal IC3 query's WHERE country.id IN [x.id, y.id] shape) and multi-prefix-row free-form remain rejected with scoped failfasts pointing at follow-ups; #1263 stays open until the rewrite-order refactor + multi-row admit land.

Predecessor PR #1270 (658dfdf3f) sharpened the failfast wording. This PR lifts it for the simple case.

What changed (3 design commits + tests)

  1. IR (graphistry/compute/gfql/cypher/reentry_plan.py): ReentryPlan.free_form: bool = False marker.
  2. Compile (graphistry/compute/gfql/cypher/lowering.py): _compile_bounded_reentry_query admits free-form when first_alias not in whole_row_carried. Sets reentry_alias = first_alias (the trailing-MATCH source) as the rewriter carrier label and non_source_alias_names = whole_row_carried so all carried whole-row aliases route through the existing property-carry mechanism (feat(cypher): ReentryPlan + multi-whole-row prefix WITH (#989 slices 4.1+4.3a) #1248). Builds a ReentryPlan(free_form=True, ...) with no is_reentry_alias=True entry. Adds compile-time failfast: free_form AND any(props for props in props_by_alias.values()) → scoped Cypher/GFQL: free-form intermediate MATCH after WITH (LDBC SNB IC3 endpoint) #1263 follow-up error.
  3. Runtime (graphistry/compute/gfql_unified.py): new _compiled_query_freeform_reentry_state helper. Single-prefix-row case broadcasts every __cypher_reentry_* hidden column from the prefix row onto every base node, returns start_nodes=None so the suffix runs as a global MATCH and the row pipeline carries the broadcast values through whichever alias the trailing MATCH binds. Multi-prefix-row raises a clear failfast pointing at a follow-up. Branch dispatched in _execute_compiled_query_with_reentry on plan.free_form.

Plus tests retargeted + added.

Out of scope (#1263 stays open)

Tests

graphistry/tests/compute/gfql/cypher/test_lowering.py:

  • ⤳ test_string_cypher_failfast_rejects_intermediate_reentry_match_with_no_carried_source → ..._with_carried_property_in_trailing_where (retargeted; pins new Cypher/GFQL: free-form intermediate MATCH after WITH (LDBC SNB IC3 endpoint) #1263 scoped failfast for carried-property variant).
  • ⤳ test_string_cypher_failfast_rejects_simple_freeform_intermediate_reentry_match → test_string_cypher_executes_simple_freeform_intermediate_reentry_match (retargeted to positive; locks runtime broadcast contract).
  • ➕ ..._executes_simple_freeform_intermediate_reentry_match_on_cudf_when_available (cuDF parity per coordinator def-of-done).
  • ➕ ..._failfast_rejects_simple_freeform_intermediate_reentry_match_on_multi_row_prefix (Wave 1 review fix — pins multi-row runtime failfast).

Receipts

  • ./bin/ruff.sh on touched files: ✅ clean
  • ./bin/mypy.sh on touched files (lowering.py, gfql_unified.py, reentry_plan.py): ✅ clean
  • Targeted: 4/4 passed (3 free-form + 1 multi-row failfast); cuDF skipped locally (no cudf installed).
  • Full cypher suite: 1118 passed, 68 skipped, 15 xfailed (89s).
  • Row pipeline + parser: 288 passed, 3 skipped (7s).
  • Full TCK: 3784 passed, 911 xfailed (185s) — same baseline as master, with 3 new admits in TCK contract (expr-typeconversion{2,3,4}-{7,5,7} — all simple MATCH (single-node) WITH * MATCH (n) RETURN <typecast>(n.<prop>)). Paired TCK update needed at tests/cypher_tck/direct_cypher_xfail_contract.py (added to DIRECT_CYPHER_XFAIL_MATCHES_EXPECTED_BASE_KEYS).
  • DGX-Spark RAPIDS 25.02 + 26.02 pandas: ✅ 2/2 targeted tests pass on both. cuDF test environment-blocked by VLLM tenancy (host has 7.6 GB free of 119 GB; cuDF allocations fail at 24 bytes during the run window) — not a code regression. The same row-pipeline/broadcast code path is exercised by _compiled_query_scalar_reentry_state already covered by master cuDF tests.

Multi-wave review

Per agents/skills/review/SKILL.md. Wave 1 with 3 parallel reviewers (correctness+tests / DRY+quality / conventions+operability). Triage:

  • CONFIRMED IMPORTANT (1): missing test for multi-prefix-row runtime failfast → fixed in 138e0234e.
  • REJECTED (3 IMPORTANT): D1 (whole_row_carried "duplication" — different concept), D2 (5-line dispatch tail extraction), D4 (return-type "inconsistency" intentional).
  • DOWNGRADED to SUGGESTION (others): cosmetic / cleanup; defer to follow-up hygiene pass.

Below significant-advance threshold. Wave 1 artifact: plans/1263-freeform-intermediate-match/waves/wave-1/wave-report.md.

Test plan

  • pytest -q graphistry/tests/compute/gfql/cypher/test_lowering.py
  • pytest -q graphistry/tests/compute/gfql/
  • ./bin/ruff.sh + ./bin/mypy.sh on touched files
  • Full TCK with paired contract update
  • DGX RAPIDS 25.02 + 26.02 pandas spot-check
  • Hosted CI green (push pending)
  • cuDF spot-check rerun once VLLM frees GPU memory (tracked as a non-blocker)

Refs

@lmeyerov
lmeyerov force-pushed the feat/1263-freeform-intermediate-match-impl branch from cc9c9ef to 95414e1 Compare May 4, 2026 04:15
lmeyerov added a commit that referenced this pull request May 4, 2026
Adds a "Vectorization & engine compatibility (GFQL / row pipeline /
compute)" subsection under "Pygraphistry-specific review checks" so
reviewers consistently catch:

* Per-row Python loops (`apply(axis=1)`, `iterrows`, `itertuples`,
  `loc[i, col] = ...` in a loop) on hot row paths — both a perf cliff
  on pandas and an outright break on cuDF.
* cuDF compatibility regressions — pandas-only API patterns, GPU↔CPU
  round-trips that signal missing vectorization, signatures locked to
  `pd.DataFrame` instead of the engine-neutral `DataFrameT`.
* When a code change requires paired cuDF coverage vs. when CPU-only
  is acceptable.

Severity rubric and false-positive guards included so reviewers
distinguish hot row paths from control-plane code and don't flag
pre-existing repo debt as new findings.

The pre-existing GPU-validation-evidence rule (line 172-174) keeps its
current scope; this subsection adds the static-review-time checks that
should fire BEFORE running GPU CI.

Catalyst: pygraphistry #1279 (free-form intermediate MATCH admit) made
the vectorization + cuDF assumptions concrete enough to codify.
lmeyerov added a commit that referenced this pull request May 4, 2026
Adds a "Vectorization & engine compatibility (GFQL / row pipeline /
compute)" subsection under "Pygraphistry-specific review checks" so
reviewers consistently catch:

* Per-row Python loops (`apply(axis=1)`, `iterrows`, `itertuples`,
  `loc[i, col] = ...` in a loop) on hot row paths — both a perf cliff
  on pandas and an outright break on cuDF.
* cuDF compatibility regressions — pandas-only API patterns, GPU↔CPU
  round-trips that signal missing vectorization, signatures locked to
  `pd.DataFrame` instead of the engine-neutral `DataFrameT`.
* When a code change requires paired cuDF coverage vs. when CPU-only
  is acceptable.

Severity rubric and false-positive guards included so reviewers
distinguish hot row paths from control-plane code and don't flag
pre-existing repo debt as new findings.

The pre-existing GPU-validation-evidence rule (line 172-174) keeps its
current scope; this subsection adds the static-review-time checks that
should fire BEFORE running GPU CI.

Catalyst: pygraphistry #1279 (free-form intermediate MATCH admit) made
the vectorization + cuDF assumptions concrete enough to codify.
lmeyerov added a commit that referenced this pull request May 4, 2026
#1283)

Adds a "Vectorization & engine compatibility (GFQL / row pipeline /
compute)" subsection under "Pygraphistry-specific review checks" so
reviewers consistently catch:

* Per-row Python loops (`apply(axis=1)`, `iterrows`, `itertuples`,
  `loc[i, col] = ...` in a loop) on hot row paths — both a perf cliff
  on pandas and an outright break on cuDF.
* cuDF compatibility regressions — pandas-only API patterns, GPU↔CPU
  round-trips that signal missing vectorization, signatures locked to
  `pd.DataFrame` instead of the engine-neutral `DataFrameT`.
* When a code change requires paired cuDF coverage vs. when CPU-only
  is acceptable.

Severity rubric and false-positive guards included so reviewers
distinguish hot row paths from control-plane code and don't flag
pre-existing repo debt as new findings.

The pre-existing GPU-validation-evidence rule (line 172-174) keeps its
current scope; this subsection adds the static-review-time checks that
should fire BEFORE running GPU CI.

Catalyst: pygraphistry #1279 (free-form intermediate MATCH admit) made
the vectorization + cuDF assumptions concrete enough to codify.
lmeyerov and others added 7 commits May 3, 2026 22:36
…TCH (#1263)

Adds a `free_form: bool = False` field to `ReentryPlan` so the runtime can
distinguish the LDBC SNB IC3 free-form intermediate MATCH shape from the
existing whole-row and scalar-only prefix shapes.

When the trailing MATCH after a prefix `WITH` introduces aliases none of
which is in the carried set, no carried alias anchors the seed pattern.
The runtime needs to broadcast carried columns onto the base node table
(single-prefix-row) or fall back to per-row union (multi-prefix-row) so
the trailing MATCH cross-joins implicitly via the row pipeline.

Default value preserves existing whole-row and scalar-only behavior.
Compile + runtime branches that consume this marker land in subsequent
commits.

Refs #1263 #999 #989

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Lifts the failfast at the trailing-MATCH-first-alias check in
``_compile_bounded_reentry_query`` for the free-form case (LDBC SNB IC3
endpoint): when the trailing MATCH binds aliases none of which is in the
prefix WITH's carried whole-row set, treat every carried alias as
non-source and use the trailing MATCH's first alias as the carrier label
for downstream rewrites.

The existing ``_collect_non_source_alias_property_refs`` /
``_rewrite_reentry_expr_to_hidden_properties`` machinery (slice 4.3a/b
from #1248) materializes carried-alias property references as hidden
columns; with this change the same machinery covers free-form cases by
passing all whole-row aliases as ``non_source_alias_names``.

The ``ReentryPlan`` constructed for free-form sets
``free_form=True`` (no ``CarriedAlias`` entry has
``is_reentry_alias=True``) so the runtime can branch into a broadcast
path. The runtime change lands in a follow-up commit; until then,
free-form queries compile but return wrong rows because the existing
``_compiled_query_reentry_state`` still seeds from the wrong alias's
node ids — the regression is gated by the failfast tests (which fail
intentionally at this commit and are retargeted alongside the runtime
commit).

Also tightens the surviving guard from ``first_alias is None or
first_alias != reentry_alias`` to ``first_alias is None`` since the
admit branch now sets ``reentry_alias = first_alias`` for the
previously-rejected case.

Refs #1263 #999 #989

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
#1263)

Lands the runtime half of the #1263 conservative admit:

* New ``_compiled_query_freeform_reentry_state`` in ``gfql_unified.py``
  branches on ``ReentryPlan.free_form``. For a single-row prefix WITH
  it broadcasts every ``__cypher_reentry_*`` hidden column from the
  prefix row onto every base node and returns ``start_nodes=None``, so
  the trailing MATCH runs as a regular MATCH and inherits the carried
  values via the row pipeline. Multi-row prefix raises a clear
  failfast pointing at the multi-row free-form follow-up slice.

* Compile gate added in ``_compile_bounded_reentry_query``: when
  ``free_form`` is True AND the trailing scope references any carried
  alias property (e.g. ``WHERE country.id IN [x.id, y.id]`` in literal
  IC3), raise a scoped ``#1263`` failfast pointing at the rewrite-order
  refactor follow-up. The double-rewrite that emerges from composing
  ``_demote_secondary_whole_row_aliases`` (#1071) with
  ``_rewrite_reentry_expr_to_hidden_properties`` (#1248) under
  free-form is deferred to its own focused slice.

End-to-end:
* Simple free-form (``MATCH (a) WITH a MATCH (c)-[:T]->(d) RETURN ...``)
  compiles + executes vectorized on pandas.
* IC3-shape with carried-property WHERE rejects with a scoped #1263
  failfast (clear error pointing at the follow-up).

Refs #1263 #999 #989

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…e admit

Two #1263 regression-locks added in #1270 expected the carried-alias gate to
fire for free-form intermediate MATCH. The conservative admit landed in the
preceding commits flips the simple shape from rejection to positive
execution and tightens the rejection scope to the carried-property variant:

* ``test_string_cypher_failfast_rejects_intermediate_reentry_match_with_no_carried_source``
  → renamed to ``..._with_carried_property_in_trailing_where`` and pinned to
  the new scoped #1263 failfast (carried-alias property in trailing scope).
* ``test_string_cypher_failfast_rejects_simple_freeform_intermediate_reentry_match``
  → renamed to ``test_string_cypher_executes_simple_freeform_intermediate_reentry_match``
  and converted to positive: asserts the trailing MATCH binds correctly
  against the broadcast carried row table.
* New ``..._on_cudf_when_available`` mirror with ``pytest.importorskip("cudf")``
  for engine parity per coordinator def-of-done.

Refs #1263 #999 #989

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Wave 1 review (single CONFIRMED IMPORTANT): the runtime failfast at
gfql_unified.py for multi-prefix-row free-form admit was untested. Adds a
positive regression-lock that builds a 2-row prefix and asserts the scoped
``single-row prefix WITH only`` failfast wording.

Refs #1263

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Records the conservative #1263 close: simple free-form admit + scoped
failfast for the carried-property-in-trailing-WHERE variant + runtime
broadcast helper + retargeted regression tests + three TCK admits.

Refs #1263 #999 #989

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…e-form (#1263)

Wave 2 review per agents/skills/review/SKILL.md confirmed convergence
(two consecutive non-significant-advance waves) and surfaced two
amplification SUGGESTIONs that the reviewers verified produce correct
behavior. Locking the contract:

* test_string_cypher_executes_freeform_intermediate_reentry_match_with_multi_carried_aliases
  — covers `WITH a, b MATCH (c)-[:T]->(d)` shape (multi-carried-aliases admit).
* test_string_cypher_executes_freeform_intermediate_reentry_match_with_empty_prefix
  — covers `_compiled_query_freeform_reentry_state`'s empty-prefix
  early-return path.

Refs #1263

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@lmeyerov
lmeyerov force-pushed the feat/1263-freeform-intermediate-match-impl branch from 059acc0 to 816a415 Compare May 4, 2026 05:37
@lmeyerov
lmeyerov merged commit bf9755c into master May 4, 2026
261 of 263 checks passed
@lmeyerov
lmeyerov deleted the feat/1263-freeform-intermediate-match-impl branch May 4, 2026 06:00
lmeyerov added a commit that referenced this pull request May 4, 2026
* feat(cypher/ir): add ReentryPlan.free_form marker for intermediate MATCH (#1263)

Adds a `free_form: bool = False` field to `ReentryPlan` so the runtime can
distinguish the LDBC SNB IC3 free-form intermediate MATCH shape from the
existing whole-row and scalar-only prefix shapes.

When the trailing MATCH after a prefix `WITH` introduces aliases none of
which is in the carried set, no carried alias anchors the seed pattern.
The runtime needs to broadcast carried columns onto the base node table
(single-prefix-row) or fall back to per-row union (multi-prefix-row) so
the trailing MATCH cross-joins implicitly via the row pipeline.

Default value preserves existing whole-row and scalar-only behavior.
Compile + runtime branches that consume this marker land in subsequent
commits.

Refs #1263 #999 #989

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* feat(cypher): admit free-form intermediate MATCH at compile (#1263)

Lifts the failfast at the trailing-MATCH-first-alias check in
``_compile_bounded_reentry_query`` for the free-form case (LDBC SNB IC3
endpoint): when the trailing MATCH binds aliases none of which is in the
prefix WITH's carried whole-row set, treat every carried alias as
non-source and use the trailing MATCH's first alias as the carrier label
for downstream rewrites.

The existing ``_collect_non_source_alias_property_refs`` /
``_rewrite_reentry_expr_to_hidden_properties`` machinery (slice 4.3a/b
from #1248) materializes carried-alias property references as hidden
columns; with this change the same machinery covers free-form cases by
passing all whole-row aliases as ``non_source_alias_names``.

The ``ReentryPlan`` constructed for free-form sets
``free_form=True`` (no ``CarriedAlias`` entry has
``is_reentry_alias=True``) so the runtime can branch into a broadcast
path. The runtime change lands in a follow-up commit; until then,
free-form queries compile but return wrong rows because the existing
``_compiled_query_reentry_state`` still seeds from the wrong alias's
node ids — the regression is gated by the failfast tests (which fail
intentionally at this commit and are retargeted alongside the runtime
commit).

Also tightens the surviving guard from ``first_alias is None or
first_alias != reentry_alias`` to ``first_alias is None`` since the
admit branch now sets ``reentry_alias = first_alias`` for the
previously-rejected case.

Refs #1263 #999 #989

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* feat(cypher): runtime broadcast for simple free-form + scoped failfast (#1263)

Lands the runtime half of the #1263 conservative admit:

* New ``_compiled_query_freeform_reentry_state`` in ``gfql_unified.py``
  branches on ``ReentryPlan.free_form``. For a single-row prefix WITH
  it broadcasts every ``__cypher_reentry_*`` hidden column from the
  prefix row onto every base node and returns ``start_nodes=None``, so
  the trailing MATCH runs as a regular MATCH and inherits the carried
  values via the row pipeline. Multi-row prefix raises a clear
  failfast pointing at the multi-row free-form follow-up slice.

* Compile gate added in ``_compile_bounded_reentry_query``: when
  ``free_form`` is True AND the trailing scope references any carried
  alias property (e.g. ``WHERE country.id IN [x.id, y.id]`` in literal
  IC3), raise a scoped ``#1263`` failfast pointing at the rewrite-order
  refactor follow-up. The double-rewrite that emerges from composing
  ``_demote_secondary_whole_row_aliases`` (#1071) with
  ``_rewrite_reentry_expr_to_hidden_properties`` (#1248) under
  free-form is deferred to its own focused slice.

End-to-end:
* Simple free-form (``MATCH (a) WITH a MATCH (c)-[:T]->(d) RETURN ...``)
  compiles + executes vectorized on pandas.
* IC3-shape with carried-property WHERE rejects with a scoped #1263
  failfast (clear error pointing at the follow-up).

Refs #1263 #999 #989

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* test(cypher): retarget free-form failfast tests for #1263 conservative admit

Two #1263 regression-locks added in #1270 expected the carried-alias gate to
fire for free-form intermediate MATCH. The conservative admit landed in the
preceding commits flips the simple shape from rejection to positive
execution and tightens the rejection scope to the carried-property variant:

* ``test_string_cypher_failfast_rejects_intermediate_reentry_match_with_no_carried_source``
  → renamed to ``..._with_carried_property_in_trailing_where`` and pinned to
  the new scoped #1263 failfast (carried-alias property in trailing scope).
* ``test_string_cypher_failfast_rejects_simple_freeform_intermediate_reentry_match``
  → renamed to ``test_string_cypher_executes_simple_freeform_intermediate_reentry_match``
  and converted to positive: asserts the trailing MATCH binds correctly
  against the broadcast carried row table.
* New ``..._on_cudf_when_available`` mirror with ``pytest.importorskip("cudf")``
  for engine parity per coordinator def-of-done.

Refs #1263 #999 #989

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* test(cypher): add multi-prefix-row free-form failfast regression (#1263)

Wave 1 review (single CONFIRMED IMPORTANT): the runtime failfast at
gfql_unified.py for multi-prefix-row free-form admit was untested. Adds a
positive regression-lock that builds a 2-row prefix and asserts the scoped
``single-row prefix WITH only`` failfast wording.

Refs #1263

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* docs(changelog): #1263 simple free-form intermediate MATCH admit

Records the conservative #1263 close: simple free-form admit + scoped
failfast for the carried-property-in-trailing-WHERE variant + runtime
broadcast helper + retargeted regression tests + three TCK admits.

Refs #1263 #999 #989

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* test(cypher): Wave 2 amplification — multi-carried + empty prefix free-form (#1263)

Wave 2 review per agents/skills/review/SKILL.md confirmed convergence
(two consecutive non-significant-advance waves) and surfaced two
amplification SUGGESTIONs that the reviewers verified produce correct
behavior. Locking the contract:

* test_string_cypher_executes_freeform_intermediate_reentry_match_with_multi_carried_aliases
  — covers `WITH a, b MATCH (c)-[:T]->(d)` shape (multi-carried-aliases admit).
* test_string_cypher_executes_freeform_intermediate_reentry_match_with_empty_prefix
  — covers `_compiled_query_freeform_reentry_state`'s empty-prefix
  early-return path.

Refs #1263

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
lmeyerov added a commit that referenced this pull request May 4, 2026
…is defaults (#1280)

* phase1: validators, deep ring axis checks, and persist axis defaults

* fix: satisfy mypy for dataset_id URL refresh helper

* fix: make validate_settings typing compatible with python 3.8

* refactor: centralize axis field constants and typed dict contracts

* docs(changelog): record settings/axis validation + persist defaults work

* refactor(validate): centralize ring axis payload validators

* types(validate): add named url params/react settings aliases

* types(url_params): propagate named URLParamsDict across APIs

* feat(cypher): admit simple free-form intermediate MATCH (#1263) (#1279)

* feat(cypher/ir): add ReentryPlan.free_form marker for intermediate MATCH (#1263)

Adds a `free_form: bool = False` field to `ReentryPlan` so the runtime can
distinguish the LDBC SNB IC3 free-form intermediate MATCH shape from the
existing whole-row and scalar-only prefix shapes.

When the trailing MATCH after a prefix `WITH` introduces aliases none of
which is in the carried set, no carried alias anchors the seed pattern.
The runtime needs to broadcast carried columns onto the base node table
(single-prefix-row) or fall back to per-row union (multi-prefix-row) so
the trailing MATCH cross-joins implicitly via the row pipeline.

Default value preserves existing whole-row and scalar-only behavior.
Compile + runtime branches that consume this marker land in subsequent
commits.

Refs #1263 #999 #989

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* feat(cypher): admit free-form intermediate MATCH at compile (#1263)

Lifts the failfast at the trailing-MATCH-first-alias check in
``_compile_bounded_reentry_query`` for the free-form case (LDBC SNB IC3
endpoint): when the trailing MATCH binds aliases none of which is in the
prefix WITH's carried whole-row set, treat every carried alias as
non-source and use the trailing MATCH's first alias as the carrier label
for downstream rewrites.

The existing ``_collect_non_source_alias_property_refs`` /
``_rewrite_reentry_expr_to_hidden_properties`` machinery (slice 4.3a/b
from #1248) materializes carried-alias property references as hidden
columns; with this change the same machinery covers free-form cases by
passing all whole-row aliases as ``non_source_alias_names``.

The ``ReentryPlan`` constructed for free-form sets
``free_form=True`` (no ``CarriedAlias`` entry has
``is_reentry_alias=True``) so the runtime can branch into a broadcast
path. The runtime change lands in a follow-up commit; until then,
free-form queries compile but return wrong rows because the existing
``_compiled_query_reentry_state`` still seeds from the wrong alias's
node ids — the regression is gated by the failfast tests (which fail
intentionally at this commit and are retargeted alongside the runtime
commit).

Also tightens the surviving guard from ``first_alias is None or
first_alias != reentry_alias`` to ``first_alias is None`` since the
admit branch now sets ``reentry_alias = first_alias`` for the
previously-rejected case.

Refs #1263 #999 #989

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* feat(cypher): runtime broadcast for simple free-form + scoped failfast (#1263)

Lands the runtime half of the #1263 conservative admit:

* New ``_compiled_query_freeform_reentry_state`` in ``gfql_unified.py``
  branches on ``ReentryPlan.free_form``. For a single-row prefix WITH
  it broadcasts every ``__cypher_reentry_*`` hidden column from the
  prefix row onto every base node and returns ``start_nodes=None``, so
  the trailing MATCH runs as a regular MATCH and inherits the carried
  values via the row pipeline. Multi-row prefix raises a clear
  failfast pointing at the multi-row free-form follow-up slice.

* Compile gate added in ``_compile_bounded_reentry_query``: when
  ``free_form`` is True AND the trailing scope references any carried
  alias property (e.g. ``WHERE country.id IN [x.id, y.id]`` in literal
  IC3), raise a scoped ``#1263`` failfast pointing at the rewrite-order
  refactor follow-up. The double-rewrite that emerges from composing
  ``_demote_secondary_whole_row_aliases`` (#1071) with
  ``_rewrite_reentry_expr_to_hidden_properties`` (#1248) under
  free-form is deferred to its own focused slice.

End-to-end:
* Simple free-form (``MATCH (a) WITH a MATCH (c)-[:T]->(d) RETURN ...``)
  compiles + executes vectorized on pandas.
* IC3-shape with carried-property WHERE rejects with a scoped #1263
  failfast (clear error pointing at the follow-up).

Refs #1263 #999 #989

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* test(cypher): retarget free-form failfast tests for #1263 conservative admit

Two #1263 regression-locks added in #1270 expected the carried-alias gate to
fire for free-form intermediate MATCH. The conservative admit landed in the
preceding commits flips the simple shape from rejection to positive
execution and tightens the rejection scope to the carried-property variant:

* ``test_string_cypher_failfast_rejects_intermediate_reentry_match_with_no_carried_source``
  → renamed to ``..._with_carried_property_in_trailing_where`` and pinned to
  the new scoped #1263 failfast (carried-alias property in trailing scope).
* ``test_string_cypher_failfast_rejects_simple_freeform_intermediate_reentry_match``
  → renamed to ``test_string_cypher_executes_simple_freeform_intermediate_reentry_match``
  and converted to positive: asserts the trailing MATCH binds correctly
  against the broadcast carried row table.
* New ``..._on_cudf_when_available`` mirror with ``pytest.importorskip("cudf")``
  for engine parity per coordinator def-of-done.

Refs #1263 #999 #989

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* test(cypher): add multi-prefix-row free-form failfast regression (#1263)

Wave 1 review (single CONFIRMED IMPORTANT): the runtime failfast at
gfql_unified.py for multi-prefix-row free-form admit was untested. Adds a
positive regression-lock that builds a 2-row prefix and asserts the scoped
``single-row prefix WITH only`` failfast wording.

Refs #1263

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* docs(changelog): #1263 simple free-form intermediate MATCH admit

Records the conservative #1263 close: simple free-form admit + scoped
failfast for the carried-property-in-trailing-WHERE variant + runtime
broadcast helper + retargeted regression tests + three TCK admits.

Refs #1263 #999 #989

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* test(cypher): Wave 2 amplification — multi-carried + empty prefix free-form (#1263)

Wave 2 review per agents/skills/review/SKILL.md confirmed convergence
(two consecutive non-significant-advance waves) and surfaced two
amplification SUGGESTIONs that the reviewers verified produce correct
behavior. Locking the contract:

* test_string_cypher_executes_freeform_intermediate_reentry_match_with_multi_carried_aliases
  — covers `WITH a, b MATCH (c)-[:T]->(d)` shape (multi-carried-aliases admit).
* test_string_cypher_executes_freeform_intermediate_reentry_match_with_empty_prefix
  — covers `_compiled_query_freeform_reentry_state`'s empty-prefix
  early-return path.

Refs #1263

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
lmeyerov added a commit that referenced this pull request May 4, 2026
…admit

Records the runtime per-row union path that replaces the single-row-only
failfast from #1279, plus the OPTIONAL MATCH failfast preserved from the
scalar-only multi-row guard.

Refs #1285, #1263, #999, #989

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
lmeyerov added a commit that referenced this pull request May 4, 2026
…admit

Records the runtime per-row union path that replaces the single-row-only
failfast from #1279, plus the OPTIONAL MATCH failfast preserved from the
scalar-only multi-row guard.

Refs #1285, #1263, #999, #989

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
lmeyerov added a commit that referenced this pull request May 4, 2026
) (#1289)

* feat(cypher): admit multi-prefix-row free-form intermediate MATCH (#1285)

Replaces the single-row-only failfast in the free-form runtime path with a
per-row union loop, mirroring the scalar-only multi-row pattern at
``_compiled_query_scalar_reentry_state`` (#1047). For each prefix row, the
new ``_freeform_broadcast_row_to_nodes`` helper builds a dispatch graph with
that row's hidden carry columns broadcast onto every base node; the suffix
runs as a global MATCH (``start_nodes=None``) and per-row results are unioned
via the existing engine-polymorphic ``_union_scalar_reentry_results``.

Cartesian semantics fall out naturally: every prefix row × every trailing-
MATCH row.

Out of scope (preserved as failfasts):
- ``optional_reentry`` + multi-row free-form: same null-fill gap as the
  scalar-only multi-row path; mirrors that lane's existing failfast.
- Carried-property bridge in trailing scope (#1275).

Refs #1285, #1263, #999, #989

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* test(cypher): retarget #1285 multi-row failfast + add multi-row + cuDF (#1285)

Three test changes for the multi-prefix-row free-form admit:

* ``..._failfast_rejects_simple_freeform_intermediate_reentry_match_on_multi_row_prefix``
  → ``..._executes_freeform_intermediate_reentry_match_on_multi_row_prefix``
  (retargeted to positive; pins per-row union behavior — 2 prefix rows × 1
  trailing pair = 2 result rows).
* ``..._executes_freeform_intermediate_reentry_match_on_multi_row_prefix_cartesian``
  (new) — 2 prefix rows × 2 trailing pairs = 4 result rows; locks the
  Cartesian product semantics.
* ``..._executes_freeform_intermediate_reentry_match_on_multi_row_prefix_on_cudf_when_available``
  (new) — cuDF parity for the multi-row admit.

Pre-existing simple (single-row) free-form tests stay untouched.

Refs #1285

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* test(cypher): lock #1285 multi-row free-form + OPTIONAL MATCH failfast

Wave 2 review surfaced an uncovered code path: the runtime guard at
``_execute_compiled_query_with_reentry`` rejects ``optional_reentry +
multi-row free-form`` (mirror of the scalar-only multi-row guard from
#1047) but no test asserted the failfast wording.

Adds ``test_string_cypher_failfast_rejects_freeform_multi_row_prefix_with_optional_reentry``,
following the sibling pattern from
``test_issue_1047_multi_row_scalar_prefix_with_optional_reentry_raises``.

Refs #1285

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* docs(changelog): #1285 multi-prefix-row free-form intermediate MATCH admit

Records the runtime per-row union path that replaces the single-row-only
failfast from #1279, plus the OPTIONAL MATCH failfast preserved from the
scalar-only multi-row guard.

Refs #1285, #1263, #999, #989

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
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