Skip to content

frontend parity: Gremlin and SQL meet at the same Quack Query (test-only witness + parity matrix) - #1311

Merged
AdaWorldAPI merged 3 commits into
mainfrom
ccr-bb6d2318-yje8ce
Oct 3, 2026
Merged

AdaWorldAPI merged 3 commits into
mainfrom
ccr-bb6d2318-yje8ce

Conversation

@AdaWorldAPI

Copy link
Copy Markdown
Owner

What this tests

Do independently designed query languages lower onto the same population algebra? This PR measures it for SQL ↔ Gremlin and reads the Cypher (#1306 plan) and SurrealQL (AST) sides against it.

No production code changes. No new operator in Quack, no new op in mask-risc, no upstream file touched.

Changes

  • crates/lance-graph-quack/tests/gremlin_parity.rs (test only):
    • typed Gremlin steps (V, has, out, in, where, dedup, values, count, sum, groupCount, and the refused ones);
    • a declared schema over the DuckDB fixture: billedTo and partOf are fk relations, tradesWith is M:N with line as its edge table, ownedBy is a 3-row company table built only to write a two-deep chain;
    • an adapter that lowers onto lance_graph_quack::Query, with typed refusals;
    • an independent row-at-a-time Gremlin oracle with bulk;
    • 13 tests.
  • .claude/plans/frontend-parity-witness-v1.md: inventory, parity matrix, Gremlin step→primitive table, SurrealQL AST harvest, gaps, falsifiers.
  • Board: entry, STATUS_BOARD D-FPW rows, INTEGRATION_PLANS, AGENT_LOG.

Results

  • Convergence below the frontend. The Gremlin traversal and its SQL equivalent produce == Query values, and execution matches DuckDB's committed answers: join_sum_country = 1237848 and join_group_count_country. The forward traversal (where(out(..))) and the reverse one (in(..)) land on the same anchor and the same atom multiset.
  • Anchor rule. A functional hop keeps the anchor and reads the target through the fk (EqU32Via, GroupKey::Via). One fan-out re-anchors on the table whose rows are the paths, either the child table or the edge table. So one M:N hop is one Program, and its bag counts are exact.
  • Input for plan: cypher-mask-lowering-v2 — mask engine beside upstream, one switch, refusals #1306. RF-BAG ("any non-DISTINCT count after a hop") is broader than the semantics require. After a functional hop, or one hop over an edge population, the bag count is exact.
  • Five gaps pass the two-independent-witness rule. None is built.
  • SurrealQL.
    • The shape fits: a hop is Part::Graph(Lookup), a mini-SELECT over a first-class edge table, and results are bag-flattened. That is the same geometry as the edge-population re-anchor.
    • It cannot be used as a dependency: sql::* is pub(crate), the public arena AST is unpublished, and the license is BSL 1.1. A fork-side lowering hook is D-FPW-3, and it is an operator decision.
  • DuckGQL / DuckPG. No code or plan by those names exists in the tree.

Verification

  • cargo test -p lance-graph-quack: all suites pass (36 + 39 + 13).
  • clippy --tests -D warnings is clean; fmt is clean.
  • Five disable runs each turned the suite red, then were restored:
    • dedup does not mark the set;
    • re-anchor drops carried predicates;
    • a bag emitted as a mask;
    • an edge hop drops the target-exists semijoin;
    • carry keeps a stale via atom.
  • One limitation: the edge-hop disable is caught only by the structural Query check. The fixture has no dangling fk, so execution cannot see it.
  • Adapter tax (debug build, printed, not asserted): adapter ≈ 1.9 µs, Quack lowering ≈ 1.0 µs, execution over 4,096 rows ≈ 167 µs.

Environment note: the workspace needs the ndarray sibling at ../ndarray. I cloned it read-only for the build; nothing in it changed.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Eo5eHGADmeKNE3xYhgdK76


Generated by Claude Code

claude added 3 commits October 3, 2026 12:37
…as SQL

Test-only adapter (no production code): a typed Gremlin step vocabulary
lowered onto quack::Query. Parity is checked at the Query value, below
the frontend, against DuckDB's committed answers and an independent
bag-semantics traverser oracle. The two non-lowerable shapes (functional
read two fks deep; a fan-out after a hop) are typed refusals.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Eo5eHGADmeKNE3xYhgdK76
…is refused

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Eo5eHGADmeKNE3xYhgdK76
Parity matrix across SQL/Quack, Cypher (#1306 plan), Gremlin (measured
witness) and SurrealQL (AST harvest). Five gaps pass the two-witness
rule; none is built. Input for #1306 D-CML-2: RF-BAG is broader than
the semantics require after a functional hop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Eo5eHGADmeKNE3xYhgdK76
@coderabbitai

coderabbitai Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

Billing warning: we have not been able to collect payment for this subscription for more than 72 hours. Please update the payment method or pay any pending invoices in Billing to avoid service interruption.


Comment @coderabbitai help to get the list of available commands.

@AdaWorldAPI
AdaWorldAPI marked this pull request as ready for review October 3, 2026 12:45
@AdaWorldAPI
AdaWorldAPI merged commit 2f2b67c into main Oct 3, 2026
11 of 12 checks passed
@coderabbitai

coderabbitai Bot commented Oct 3, 2026

Copy link
Copy Markdown

Autopilot could not be updated. Open Coding to check access and billing.

AdaWorldAPI pushed a commit that referenced this pull request Oct 3, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X1YcYMRSFvfczXoP748wtB

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d4444f29b7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1389 to +1392
V(Table::Line),
Out(Rel::BilledTo),
Values(Field::Country),
Sum,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Isolate foreign-value summation with an i32 field

When this test is used to justify the G4 “sum of a foreign value” gap, Country conflates foreign addressing with an unsupported value type: terminal only permits Sum for Kind::I32, so even V(Partner).values(Country).sum() cannot lower. Consequently this case does not show that the foreign hop is the missing capability. Add an i32 field on the target table and demonstrate that its local sum succeeds while the through-FK form is refused.

AGENTS.md reference: AGENTS.md:L24-L25

Useful? React with 👍 / 👎.

Comment on lines +405 to +406
st.value = Some(f);
Ok(())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject unsupported steps after values()

When values() is followed by anything other than sum(), this assignment leaves the vertex cursor unchanged, so a traversal such as [V(Line), Values(Amount), Emit] is accepted and lowered to Agg::Rows, returning vertices rather than property values. The purportedly independent oracle repeats the same mistake by recording Values without changing trav and returning elements for Emit, so the two witnesses can agree on an incorrect Gremlin result; either model value traversers or reject every unsupported continuation.

Useful? React with 👍 / 👎.

AdaWorldAPI pushed a commit that referenced this pull request Oct 3, 2026
… experiment

Analysis only, no code. The law that survives both witnesses: execute
over the population that already carries the multiplicity, reach other
data by functional reads, switch anchors instead of building target
populations, and introduce a phase boundary only when a later pass needs
per row a fold over several rows of an earlier pass. Revises three #1311
conclusions (second fan-out, the G4 witness, values() modelling) with
correction notes; one test-only falsifier is specified as the next step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Eo5eHGADmeKNE3xYhgdK76
AdaWorldAPI added a commit that referenced this pull request Oct 3, 2026
board: population-law cross-check — #1311 witness vs real-data fold experiment
AdaWorldAPI pushed a commit that referenced this pull request Oct 3, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X1YcYMRSFvfczXoP748wtB
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.

2 participants