Skip to content

feat(gfql): fix gfql_remote WHERE loss + add Let/Cypher support#962

Merged
lmeyerov merged 15 commits intomasterfrom
feat/gfql-remote-v2
Mar 17, 2026
Merged

feat(gfql): fix gfql_remote WHERE loss + add Let/Cypher support#962
lmeyerov merged 15 commits intomasterfrom
feat/gfql-remote-v2

Conversation

@lmeyerov
Copy link
Contributor

@lmeyerov lmeyerov commented Mar 17, 2026

Fix gfql_remote() WHERE loss (P0 correctness bug), add Let/Cypher support (P1/P2), dual-field backward compat.

P0: chain_remote.py silently dropped WHERE clauses — wrong results for cross-step filters. Fixed by adding gfql_query field with full Chain envelope.

P1: Accept ASTLet/Let dict, serialize as {"type":"Let"} in new gfql_query field. Old servers get empty gfql_operations (safe no-op).

P2: Accept Cypher strings, compile locally via compile_cypher(), send as Chain or Let wire format. Supports MATCH...RETURN, GRAPH { }, GRAPH g = ... USE g ..., CALL .write().

Backward compat: Always sends both gfql_operations (flat array for old servers) and gfql_query (full typed envelope for new servers).

Files: chain_remote.py (+95/-20), ComputeMixin.py (+35/-15), test_chain_remote_v2.py (+174 new, 18 tests), CHANGELOG.md (+7).

Tests: 18 new unit tests, 767 total pass.

lmeyerov and others added 15 commits March 16, 2026 21:37
P0: Fix silent WHERE clause loss in gfql_remote(). chain_remote.py was
unwrapping the Chain envelope and discarding the `where` field, causing
queries with same-path constraints to return wrong results remotely.
Now sends both `gfql_operations` (flat array for old servers) and
`gfql_query` (full Chain/Let envelope for new servers).

P1: Accept ASTLet and Let dict input for DAG queries. Serialized as
{"type": "Let", ...} in gfql_query. Old servers receive empty
gfql_operations (safe no-op).

P2: Accept Cypher strings. Compiled locally via compile_cypher(), then
serialized as Chain or Let wire format. Supports MATCH...RETURN,
GRAPH { }, and GRAPH g = ... USE g ... forms.

10 new unit tests covering all input types and backward compat.
759 total tests pass (including full GFQL/Cypher regression suite).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…_remote

Audit fixes:
- DRY: Consolidate two near-identical Let serializers into
  _binding_to_json/_final_to_json/_compiled_to_let_json
- Fix: Update chain_remote_generic type signature to include str/ASTLet
- Fix: Replace unreachable else with TypeError raise
- Add: UserWarning when sending Let queries (old servers get no-op)
- Add: Tests for invalid Cypher string, invalid type, empty chain,
  Let warning emission
- Remove: Dead _capture_request_body helper

14 unit tests pass, 763 total pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix: CALL procedure params now serialized (was hardcoded to {})
  via new _procedure_call_to_json helper
- Fix: gfql_remote docstring :param name matches actual parameter
- Add: Tests for GRAPH { CALL .write() } and GRAPH { USE g1 CALL ... }
  via gfql_remote (the motivating pipeline use case)
- Add: Test for standalone CALL .write() in GRAPH constructor
- Clean: Remove unused Dict import in tests

16 unit tests pass, 24 total remote tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Verifies that procedure arguments like {damping: 0.85} survive
through the _procedure_call_to_json serialization path and appear
in the wire format. This was the P0-class bug from audit round 2
where params were hardcoded to {}.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…er WHERE e2e

- Fix: _binding_to_json now uses getattr for defensive attribute access,
  consistent with _final_to_json
- Add: End-to-end test for Cypher string with cross-step WHERE
  (MATCH ... WHERE a.team = b.team RETURN ...) — the exact P0 scenario
  proving WHERE survives through compile_cypher -> chain.to_json ->
  request body
- Fix: Strengthen test_graph_constructor_sends_chain to verify
  gfql_operations has content
- Fix: Strengthen test_standalone_call_write_sends_chain to verify
  gfql_query type is Chain

18 unit tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace all Any-typed parameters and getattr defensive access with
concrete types from the lowering module:
- _procedure_call_to_json: Any -> CompiledCypherProcedureCall
- _binding_to_json: Any -> CompiledGraphBinding
- _final_to_json: Any -> Union[CompiledCypherQuery, CompiledCypherGraphQuery]
- _compiled_to_let_json: same Union type

Move imports from lazy (inside function body) to module-level since
there are no circular import issues. Direct attribute access replaces
getattr now that types guarantee the attributes exist.

18 unit tests pass, 767 total pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…h (-23 lines)

- _procedure_call_to_json + _binding_to_json + _final_to_json → single
  _step_to_json(chain, procedure_call, use_ref) that handles all cases
- _compiled_to_let_json now uses dict comprehension + _step_to_json
- CompiledCypherGraphQuery and CompiledCypherQuery Cypher dispatch
  branches collapsed (both check graph_bindings/use_ref identically)
- Removed unused CompiledGraphBinding import

26 insertions, 49 deletions. 18 tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…(-165 lines)

Extract mock setup and request body capture into a shared _send(query)
helper. Every test now calls _send() and asserts on the returned body
dict, eliminating repeated mock_post/mock_plottable/body extraction
boilerplate.

339 -> 174 lines (49% reduction), same 18 tests, same coverage.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ruff F821 flagged 'ASTLet' as undefined name in type annotation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove duplicate type annotation on request_body else branch
- Widen chain_remote/chain_remote_shape type sigs to match
  chain_remote_generic (include ASTLet, str)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…_remote

Show that gfql_remote() now accepts Cypher strings, GRAPH constructors,
and multi-stage pipelines alongside native chain syntax. Update method
reference from deprecated chain_remote to gfql_remote.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
gfql_remote("MATCH (n) WHERE n.x > \$cutoff RETURN n", params={"cutoff": 10})
now works — params are passed to compile_cypher() during local compilation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The mock_server_hypergraph function didn't accept the new params
keyword added to chain_remote, causing test failure.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@lmeyerov lmeyerov merged commit 4b1b803 into master Mar 17, 2026
99 checks passed
@lmeyerov lmeyerov deleted the feat/gfql-remote-v2 branch March 17, 2026 08:48
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