Repository navigation
D-CMH-G1: Gremlin steps as composable ClassId methods — billable_hours calls is_billable, no new Rust - #1508
Conversation
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
|
Warning Review limit reachedYour organization has reached its usage spending cap. Adjust your spending cap in the billing tab. Next included review available in 11 minutes. View limit details
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 |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 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".
…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
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
The result
billable_hourscallsis_billable, a separately declared predicate method.tests/fixtures/lego.methods, loaded at run time into a binary compiled before the text existed. Neither method has any Rust.billable_hourslowers to exactly theProgramof the direct Quack querySUM(hours) WHERE billable = true. Its answers equal a row-loop oracle on Quack and R2IL at n ∈ {1, 64, 65, 4097}.is_billablemoves 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):MethodBody::Select { filter }folds nothing.Cond::Method(MethodRef::{Key | Label})names one.MethodTable::prepareresolves each reference once and inlines it into the caller's conjunction before binding. No call exists at run time.MethodRefNotPredicate.NotTerminal.MethodRefContract). A predicate with the same field names and literal in another class is not captured.src/gremlin.rs):V / has(eq,neq) / where(m()) / values / dedup / count / sumcompose 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/valuesaftervalues(f), which would change the multiset or readCOUNT(f)sumof verticessrc/decl.rs).addV,addE,property,drop,mergeV,mergeE,sideEffect,aggregate,store,sack,inject,io,call) areDeclError::Effect.Unsupportedby name.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
compose.rs::two_methods_reuse_one_predicate_method; text:gremlin.rs::the_lego_methods_load_from_text…,…a_later_declaration_reuses_an_earlier_predicatetests/fixtures/lego.methodsprograms()== hand conjunction / ==lower(direct Query)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_refusedMutations. Each of these is a typed refusal or a red oracle test, mapped in plan §3:
Disable runs (each red, then restored):
Vocabulary matrix (TinkerPop 3.7.7), plan §4
What to build next, by leverage:
(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 intocomposeand opens 9 COMPOSE rows. It is a binding, not a primitive.Cond::Or/Not, orderedCond,Fold::Min/Max/GroupCount, a mean law.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)avx512f=truemed / minprepare_with(resolve, inline, bind, lower)Programrun directlyA 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
…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