Repository navigation
Cypher/GFQL: replace bounded reentry hidden-column handshake with an explicit ReentryPlan #987
Description
Activity
Related broader architecture track: #989 covers the general row-carrier / seeded-row IR direction beyond the bounded-reentry-specific cleanup in this issue.
- added 4 commits that reference this issue
on May 2, 2026 Step 1 landed —
ReentryPlandataclass introducedPR #1248 squashed to
masteras80d80849cintroduces theReentryPlan+CarriedAliasdataclass atgraphistry/compute/gfql/cypher/reentry_plan.py, exposed viacompiled_query.reentry_planand threaded through_map_terminal_reentry_query+_attach_graph_context.The plan is now the source of truth at the compile/runtime boundary for whole-row aliases (one
is_reentry_alias=True, others as carried), top-level scalar carries, and the scalar-only prefix shape.Status of the steps in this issue
- Step 1 — introduce
ReentryPlan: ✅ landed (feat(cypher): ReentryPlan + multi-whole-row prefix WITH (#989 slices 4.1+4.3a) #1248). - Step 2 — replace hidden-column-handshake protocol with explicit plan field: 🟡 partially started. New code paths read from
ReentryPlan, but the legacyscalar_reentry_alias/scalar_reentry_columnsfields onCompiledCypherExecutionExtrasand the tuple return from_bounded_reentry_carry_columnsstill co-exist. Removing them is queued for the next slice once Cypher/GFQL rejects multi-stage MATCH ... WITH ... MATCH ... WITH ... MATCH read queries #999 IC3 work (slice 4.3d) lands and the runtime fully consults the plan. - Step 3 — move bounded-reentry runtime stitching out of
gfql_unified.py: ❌ not started. Pure-move refactor; deferred until plan adoption is complete to avoid moving code twice. - Steps 4–5 — scalar-field removal, cleanup: ❌ not started.
Issue stays open; next slice on this lane will follow once slice 4.3d (cross-reentry-boundary carry forwarding for #999 IC3) lands and exercises the plan end-to-end.
- Step 1 — introduce
- added a commit that references this issue
on May 7, 2026 Step 3 landed — bounded-reentry runtime stitching extracted
PR #1331 squashed to master as
62d3f389b(2026-05-07). Pure-move refactor: bounded-reentry data-frame execution helpers move out ofgraphistry/compute/gfql_unified.pyinto a dedicatedgraphistry/compute/gfql/cypher/reentry/execution.pymodule so the compile-time contract (ReentryPlanfrom #1248) and the data-frame stitching live next to each other._entity_projection_meta_entryco-located withWholeRowProjectionMetainresult_postprocess.py(shared by connected-OPTIONAL-MATCH and reentry).gfql_unified.pyshrinks 1987 → 1544 (-443 LOC), comfortably hitting the issue's "low-hundreds LOC reduction" success criterion. Re-imports preservegfql_unified._compiled_query_reentry_stateetc. for tests reaching into the private surface.Status of the steps in this issue
- Step 1 —
ReentryPlandataclass: ✅ landed in feat(cypher): ReentryPlan + multi-whole-row prefix WITH (#989 slices 4.1+4.3a) #1248. - Step 2 — replace hidden-column-handshake protocol: ✅ effectively landed via Refactor #1294: retire dual-contract reentry extras path #1297 (retired dual-contract reentry extras), GFQL monolith shrinkdown S3: split lowering.py projection + reentry concerns #1303 (lowering split for projection + reentry), fix(cypher): restore free-form reentry plan contract post-#1260 #1314 (free-form reentry plan contract restored).
- Step 3 — move runtime stitching into a dedicated reentry module: ✅ this PR (refactor(cypher): extract bounded-reentry runtime helpers (#987 Step 3) #1331).
- Steps 4–5 — explicit row-order semantics, lowering split: largely covered by GFQL monolith shrinkdown S3: split lowering.py projection + reentry concerns #1303 + Refactor #1295: extract bounded reentry glue from lowering.py #1299 + this PR. The explicit
ReentryPlanalready encodes ordering as part of the contract; lowering.py is split acrosscypher/lowering.py,cypher/projection_planning.py, and thecypher/reentry/subpackage.
Out-of-scope follow-ups (not blocking close)
- Rename
cypher/reentry/runtime.py→compile_runtime.py(or similar) to disambiguate vs. the new data-frame-sideexecution.py. Pre-existing naming debt. - Move
_execute_compiled_query_with_reentry(the dispatcher itself) out ofgfql_unified.py. Would require introducing a callback indirection due to recursive_execute_compiled_query. A future slice if the dispatcher itself stabilizes.
Closing — the bounded-reentry contract is now readable from one place (compile:
reentry_plan.py+cypher/reentry/; runtime stitching:cypher/reentry/execution.py).- Step 1 —
- added a commit that references this issue
on May 7, 2026 Closing as complete based on landed follow-through slices across the reentry cleanup/refactor sequence.
Key landed receipts:
- Refactor #1294: retire dual-contract reentry extras path #1297 (dual-contract extras retirement)
- Refactor #1295: extract bounded reentry glue from lowering.py #1299 (bounded reentry glue extraction)
- GFQL monolith shrinkdown S3: split lowering.py projection + reentry concerns #1303 (lowering concern split)
- fix(cypher): restore free-form reentry plan contract post-#1260 #1314 (free-form reentry contract restoration)
- refactor(cypher): extract bounded-reentry runtime helpers (#987 Step 3) #1331 (bounded-reentry runtime helper extraction; issue-scoped Step 3)
Any remaining non-blocking naming hygiene (e.g., compile-time module naming disambiguation) can be tracked as a separate follow-up chore issue and is not gating #987 closure.
Merge update (May 7, 2026):
- Follow-through cleanup is merged via PR #989 follow-through: retire post-#1260 lowering reentry shims #1334: #989 follow-through: retire post-#1260 lowering reentry shims #1334
- This delivered the post-Meta: GFQL monolith shrinkdown (lowering.py + CompiledCypher cleanup) #1260 reentry delegator-shim retirement and related lowering/runtime cleanup.
- RTD note: docs/readthedocs status used a temporary override on merge SHA due GitHub hidden pull-ref instability; see PR #989 follow-through: retire post-#1260 lowering reentry shims #1334 RCA comment for details and rollback follow-up once pull refs stabilize.
Problem
Bounded
MATCH ... WITH ... MATCH ...reentry currently works, but the internal design is hard to reason about.Today the same concept is spread across multiple mechanisms:
start_nodes_queryingraphistry/compute/gfql/cypher/lowering.py__cypher_reentry_*columns and expression rewrites ingraphistry/compute/gfql/cypher/lowering.py_cypher_entity_projection_metaside-channel metadata_compiled_query_reentry_state()stitching logic ingraphistry/compute/gfql_unified.pyThat makes the compiler/runtime contract implicit instead of explicit. A senior compiler / graph language / GPU engineer joining the project would have to reconstruct the model from several places at once.
Why This Matters
lowering.pyandgfql_unified.pyare longer and conceptually denser than they need to beProposed Refactor
Treat bounded reentry as a first-class plan/runtime concept rather than a protocol assembled from side channels.
Recommended steps:
Introduce an explicit
ReentryPlan(orSeededMatchPlan) dataclass.Replace the current hidden-property rewrite protocol.
__cypher_reentry_*property accessesMove runtime stitching into a dedicated reentry module.
gfql_unified.pyas dispatch/orchestrationMake row-order and seed-row semantics explicit.
Split
lowering.pyby concern where useful.Non-Goals
WITHsemantics hereSuccess Criteria
lowering.py+gfql_unified.pyis plausible from collapsing duplicate protocol layersContext
Current bounded-reentry hardening/validation work is in PR #975.
This issue is the follow-on cleanup/refactor lane, not a request to reopen that PR scope.