Skip to content

D-CMH-G1: Gremlin steps as composable ClassId methods — billable_hours calls is_billable, no new Rust - #1508

Merged
AdaWorldAPI merged 9 commits into
mainfrom
ccr-9f3b8075-74u47l-gremlin
Oct 12, 2026
Merged

AdaWorldAPI merged 9 commits into
mainfrom
ccr-9f3b8075-74u47l-gremlin

Conversation

@AdaWorldAPI

@AdaWorldAPI AdaWorldAPI commented Oct 12, 2026 •

Copy link
Copy Markdown
Owner

#1507 (runtime declarations) is merged; this PR now targets main, so the diff shows only G1.

The result

billable_work_entry::is_billable    … := V(billable_work_entry).has(billable, true)
billable_work_entry::billable_hours … := V(billable_work_entry).where(is_billable()).values(duration).sum()
billable_work_entry::billable_count … := V(billable_work_entry).where(is_billable()).dedup().count()
  • billable_hours calls is_billable, a separately declared predicate method.
  • All three are text in tests/fixtures/lego.methods, loaded at run time into a binary compiled before the text existed. Neither method has any Rust.
  • billable_hours lowers to exactly the Program of the direct Quack query SUM(hours) WHERE billable = true. Its answers equal a row-loop oracle on Quack and R2IL at n ∈ {1, 64, 65, 4097}.
  • Editing only is_billable moves both consumers, so the reuse is by reference, not by copy.

What is new vs. what shipped

Already shipped (not this PR):

This PR (crates/lance-graph-glove-parity):

  1. Predicate methods and references.
    • MethodBody::Select { filter } folds nothing.
    • Cond::Method(MethodRef::{Key | Label}) names one.
    • MethodTable::prepare resolves each reference once and inlines it into the caller's conjunction before binding. No call exists at run time.
    • A reference resolves only when both its label and the key behind it are unique.
  2. Result types kept apart.
    • Only a predicate can be named. A fold (scalar) or an action (effect) is MethodRefNotPredicate.
    • Calling a predicate is NotTerminal.
    • The target must share concept, seal, reading and effect with the caller (MethodRefContract). A predicate with the same field names and literal in another class is not captured.
  3. Typed Gremlin steps (src/gremlin.rs): V / has(eq,neq) / where(m()) / values / dedup / count / sum compose into a body under frontend parity: Gremlin and SQL meet at the same Quack Query (test-only witness + parity matrix) #1311's anchor rule (each live row is one vertex, bulk 1). Refused, each by name:
    • dedup / count / has / values after values(f), which would change the multiset or read COUNT(f)
    • sum of vertices
    • a foreign anchor
    • any hop: the glove declares no relation carrier yet
  4. Gremlin-shaped text (src/decl.rs).
    • 13 effect steps (addV, addE, property, drop, mergeV, mergeE, sideEffect, aggregate, store, sack, inject, io, call) are DeclError::Effect.
    • Other steps and predicates are Unsupported by name.
    • Commas and parentheses inside quoted literals are not syntax.
    • Every loaded declaration is Pure, so loading grants no authority.

Totals: no new mask-risc op, no R2IL op, no Quack variant, 0 domain-specific functions, 0 new physical instructions.

Acceptance

property evidence
a method references an independently declared predicate compose.rs::two_methods_reuse_one_predicate_method; text: gremlin.rs::the_lego_methods_load_from_text…, …a_later_declaration_reuses_an_earlier_predicate
no domain Rust tests/fixtures/lego.methods
same Quack program as the direct expression programs() == hand conjunction / == lower(direct Query)
refuses cycles, incompatible populations, unsupported multiplicity, cross-class capture a_broken_reference_is_refused (self, two-step, transitive cycles; seal; reading; fold or action target; unknown or ambiguous), a_label_reference_to_a_duplicated_key_is_refused, a_same_shaped_predicate_of_another_class_is_not_captured, steps_outside_the_anchor_contract_are_refused

Mutations. Each of these is a typed refusal or a red oracle test, mapped in plan §3:

  • relation direction
  • anchor
  • multiplicity
  • field identity
  • seal
  • reading
  • source generation
  • method reference
  • dedup precondition
  • effect class

Disable runs (each red, then restored):

  • inlining skipped
  • contract check neutralised
  • concept dropped from the contract
  • effect gate removed
  • ambiguous key resolved to its first hit
  • duplicate-key check behind a label removed
  • quote tracking in the argument scanners removed

Vocabulary matrix (TinkerPop 3.7.7), plan §4

  • 67 step-family rows:
    • DIRECT 15%
    • COMPOSE 33% (existing primitives, no method surface yet)
    • YET 28%
    • EFFECT 6%
    • OUT 18% (lists, strings, lambdas, randomness, OLAP)
  • 7 predicate rows.
  • Every row cites a test, a refusal or the missing capability.

What to build next, by leverage:

  1. A relation carrier in the glove: (fk | edge table, fields, direction, target). This moves frontend parity: Gremlin and SQL meet at the same Quack Query (test-only witness + parity matrix) #1311's anchor rule into compose and opens 9 COMPOSE rows. It is a binding, not a primitive.
  2. Small method surfaces over existing primitives (9 rows): Cond::Or/Not, ordered Cond, Fold::Min/Max/GroupCount, a mean law.
  3. The ordered-key family (FirstOfRun, order/range/limit, G5), a row answer type, G1/G2, binding environments, branching, fixpoint and paths, all recorded as YET.

Cost (examples/compose_cost.rs, 1,048,576 rows)

path native avx512f=true med / min portable v3 med / min
parse 3 declarations 4.9 / 3.7 µs 3.1 / 2.9 µs
prepare_with (resolve, inline, bind, lower) 2.5 / 2.0 µs 1.7 / 1.5 µs
warm composed call, Quack 755 / 667 µs 662 / 476 µs
warm composed call, R2IL 762 / 677 µs 663 / 472 µs
same Program run directly 749 / 676 µs 660 / 464 µs

A composed call runs the direct query's Program, so its warm cost equals direct execution within noise. Native is not faster than v3 here, so no SIMD gain is claimed. The machine is shared and noisy.

Not done

  • No relation hops in the method layer.
  • No writes of any kind.
  • …WHERE is_billable() inside a SQL-ish aggregate is still refused; the Gremlin form carries references.

Plan: .claude/plans/gremlin-composable-algebra-v1.md. Ledger updated.

🤖 Generated with Claude Code

https://claude.ai/code/session_011BMJnDBAfzcuQh5HeT39Rh

MethodBody::Select declares a selection and folds nothing; Cond::Method
names one by key or label. MethodTable::prepare resolves each reference
once and inlines the predicate's conditions into the caller's
conjunction before binding, so a caller lowers to exactly the program
of the hand-written conjunction and no call exists at run time.

Refused at preparation: unknown or ambiguous reference, a reference to a
fold or an action, a cycle, a predicate of another concept, basis,
reading or effect; a predicate's own refusal becomes its caller's. A
pure call of a predicate is CallError::NotTerminal.

tests/compose.rs (the Lego example): billable_hours and billable_count
both name is_billable; billable_hours' programs equal the direct twin's;
answers equal a row loop on both frontends at n in {1,64,65,4097};
editing only is_billable moves both consumers; chained references
resolve by label.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011BMJnDBAfzcuQh5HeT39Rh
gremlin::compose(concept, steps) turns V/has/where/values/dedup/count/sum
into a MethodBody over the concept's canonical fields; MethodTable then
prepares and lowers it like any method. The anchor contract (each live
row one vertex, bulk 1) decides what composes: dedup on vertices is the
identity; dedup, count, has or values after values(f) are refused; a
hop is refused because the glove declares no relation carrier.
Effectful steps have no Step variant.

tests/gremlin.rs: composed billable_hours/billable_count lower to the
direct Quack query's programs and equal a row loop on both frontends at
n in {1,64,65,4097}; dedup().count() equals count() on the anchor.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011BMJnDBAfzcuQh5HeT39Rh
A declaration body V(concept).step(args)... is parsed into typed Steps,
composed by gremlin::compose and copied into the declaration's owned
conditions and folds; where(label()) becomes a method reference
resolved at preparation. has(f, x), has(f, eq(x)), has(f, neq(x)),
values, dedup, count, sum, out/in/both (refused by the composer).
Effect steps (addV, addE, property, drop, mergeV, mergeE, sideEffect,
aggregate, store, sack, inject, io, call) are DeclError::Effect; other
Gremlin steps and predicates are Unsupported by name; every loaded
declaration is pure. A body with no fold is a predicate.

tests/gremlin.rs: tests/fixtures/lego.methods (is_billable,
billable_hours, billable_count) loads, prepares to the compiled
composition's programs and matches the oracle on both frontends; a
later file reuses the loaded predicate; cycles, missing references and
references to folds between loaded methods are refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011BMJnDBAfzcuQh5HeT39Rh
…aptured; compose cost

A predicate identical to is_billable in seal, reading, effect, field and
literal, differing only in concept, is refused as a reference
(MethodRefContract) and on its own (WrongConcept); with this table's
concept it is inlined. examples/compose_cost times loading the Lego file
and a warm composed call against the same program run directly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011BMJnDBAfzcuQh5HeT39Rh
TinkerPop 3.7.7 vocabulary classified per mechanism (67 step rows:
DIRECT 10, COMPOSE 22, YET 19, EFFECT 4, OUT 12; 7 predicate rows),
each citing a test, a refusal or the missing capability. Next lever: a
relation carrier in the glove (9 COMPOSE rows, a binding, not a
primitive), then the ordered-key family. Acceptance, mutation table,
disable runs and costs (native and portable v3) recorded.

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

coderabbitai Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab.

Next included review available in 11 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available. Your 60 included PR review attempts over the past 7 days set your current allowance at 1 review per hour.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: a70f9748-2bcd-40b6-aa4a-16ce90bab8a5

📥 Commits

Reviewing files that changed from the base of the PR and between b77d43a and 772bb8e.


📒 Files selected for processing (13)
  • .claude/board/INTEGRATION_PLANS.md
  • .claude/board/STATUS_BOARD.md
  • .claude/board/entries/2026-10-12-class-method-polyfill-ledger.md
  • .claude/plans/gremlin-composable-algebra-v1.md
  • crates/lance-graph-glove-parity/examples/compose_cost.rs
  • crates/lance-graph-glove-parity/src/decl.rs
  • crates/lance-graph-glove-parity/src/gremlin.rs
  • crates/lance-graph-glove-parity/src/lib.rs
  • crates/lance-graph-glove-parity/src/method.rs
  • crates/lance-graph-glove-parity/tests/common/mod.rs
  • crates/lance-graph-glove-parity/tests/compose.rs
  • crates/lance-graph-glove-parity/tests/fixtures/lego.methods
  • crates/lance-graph-glove-parity/tests/gremlin.rs

  • Autofix · 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 12, 2026 17:44
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-12T17:46:01.373556Z ceb110c Draft marked ready
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@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: ceb110c719

ℹ️ 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 thread crates/lance-graph-glove-parity/src/method.rs
Comment thread crates/lance-graph-glove-parity/src/decl.rs
…als whole

A label that names exactly one method could still resolve to a method whose
key another method carries. prepare refuses both holders of the key as
DuplicateKey, yet a third method inlined the predicate through its label.
resolve_ref now refuses that reference with the same DuplicateKey.

The Gremlin text form split arguments on every top-level comma and matched
parentheses inside quoted literals, so has(f, "North, America") read as three
arguments. Both scanners now skip quoted text.

Tests: a_label_reference_to_a_duplicated_key_is_refused (with a silence twin),
quoted_literals_keep_their_commas_and_parentheses; both red before the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011BMJnDBAfzcuQh5HeT39Rh
@AdaWorldAPI
AdaWorldAPI changed the base branch from ccr-9f3b8075-74u47l-decl to main October 12, 2026 17:50
@AdaWorldAPI
AdaWorldAPI merged commit 5217049 into main Oct 12, 2026
12 checks passed
AdaWorldAPI pushed a commit that referenced this pull request Oct 12, 2026
STATUS_BOARD: both sides prepended a section; kept D-SAG-1 then D-CMH-G1,
D-SAG-1-5 marked unblocked. G1's Cond::Method and MethodBody::Select made the
projection non-exhaustive; it now projects a reference as a declared CALLS
edge to the method the table resolves (D-SAG-1-5) and a predicate's filter
like any other. New test: callers found through Quack, and a reference cycle
recorded as the compiler's MethodCycle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011BMJnDBAfzcuQh5HeT39Rh
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