Skip to content

quack: two-world frontend — describe once, resolve once, canonicalize once, execute numeric - #1328

Merged
AdaWorldAPI merged 4 commits into
mainfrom
ccr-0455e606-wmtsor
Oct 5, 2026
Merged

AdaWorldAPI merged 4 commits into
mainfrom
ccr-0455e606-wmtsor

Conversation

@AdaWorldAPI

@AdaWorldAPI AdaWorldAPI commented Oct 5, 2026 •

Copy link
Copy Markdown
Owner

This is a follow-up to #1327 and #1326. It is not an IAM change: no consumer's identity model or numeric substrate moves.

DEVELOPER WORLD
    names / strings / host or source types
          ↓
RESOLUTION MEMBRANE            Draft::bind(&dyn Binder)
    resolve semantics
    canonicalize representation
          ↓
RESOLVED NUMERIC WORLD
    fixed-width numeric identities
    canonical little-endian byte ABI where applicable
          ↓
QUACK                          semantic numeric IR (Query / Filter / Cmp)
          ↓
mask-risc / ndarray

What a developer writes

use lance_graph_quack::bind::{table, FieldRef};

let q = table("users").where_eq("smtp", "alice@x.de").count();   // text allowed here
let bound: Query = q.bind(&binder)?;                              // names + literals resolve ONCE
// From here on: no names, no strings, no catalog, no KV.
let program = lower(&bound)?;                                     // execute as often as needed

// Typed descriptors bind to the same thing:
const SMTP: FieldRef = FieldRef::new("users", "smtp");
table("users").where_(SMTP.eq("alice@x.de")).count()

bind produces exactly what a substrate expert would write by hand:

Query { filter: Filter::and([Filter::plane(Mask(0)), Filter::cmp(Col(0), Cmp::EqU32(id))]), agg: Agg::Count }

Byte order is never something the developer writes, and neither is ENDIAN LITTLE in a future CREATE TABLE users WIDTH 512.

Design

  • No ResolvedQuery. quack::Query is already the resolved form: Filter, Cmp, Col and Agg are all fixed-width. The new layer exists only in front of it.
  • Canonical resolved ABI. Binding removes textual and schema ambiguity, and physical fixed-width byte contracts are canonical little-endian. Quack's Query remains semantic numeric IR; endian handling belongs only at raw-byte and storage boundaries.
    • Cmp::EqU32(0x12345678) is a number and carries no Endian field.
    • EqU32Strided reads it from the bytes [0x78, 0x56, 0x34, 0x12].
    • Byte arrays (Dn128, facet pattern/care bytes) are order-neutral.
    • Register128 stays four little-endian u32 words.
  • quack::bind is the crate's only text-bearing module. tests/string_fence.rs checks that the executable IR and its lowering in lib.rs hold no text types. That scan covers over 1,000 lines and has a can-fire half.
    • The resolved-world invariant during population execution is stronger: no names, no strings, no catalog or label lookup, and no ambiguous byte order.
  • Binder is the consumer's resolution membrane: catalog, live plane and codebook. Its codebook never mints. Quack is ID-agnostic after binding. A report CAM ordinal, an IAM ValueId/KeyId and a SAP code all arrive as EqU32(u32), and their meaning stays with the binder.
  • Schema uses the same lifecycle: create_table("users").width(512) → Registrar → ResolvedTable { id, width }, with no name kept. No crate owns a table catalog or allocation yet, so Registrar has only a test implementation. This is the missing actuator.
  • Same shape as contract + mask-risc: resolve read-mode once per population; 16-byte facet match predicate #1326: SlabDeclaration → resolve_for_context → ResolvedReading. The shared rule is the lifecycle (external ambiguity → once → resolved semantic contract + canonical physical contract); the resolved types stay domain-specific. The full table and the byte-boundary audit are in the board entry.

Byte-boundary audit

Every production reader that turns raw bytes into an integer is already explicit little-endian:

  • the ndarray strided kernels: eq_u32_strided_to_mask and the group sum;
  • mask-risc's reference interpreter;
  • quack's aperture_facet_strided;
  • Register128, and the NodeGuid accessors.

NodeRow is all [u8; N], so its pointer casts are order-neutral.

There is one reinterpret: FacetCascade::as_bytes / ref_from_bytes, a compute lens over a native u32. It is not stored (NodeRow::edges is [u8; 16]), and a compile-time target_endian = "little" assertion fences it. It is reported, not changed. No production ABI is intentionally native-endian, so nothing persisted was rewritten.

Proofs (each disable-verified red, then restored)

test proves disable
bind::a_text_literal_binds_once_to_the_query_an_expert_writes 4 binder calls at bind, 0 on two executions; bound Query and Program == the hand-written ones drop the live plane
bind::a_typed_descriptor_binds_to_the_same_query FieldRef and names bind identically; the dead row is excluded (same)
bind::binding_fails_in_the_developers_vocabulary_and_never_mints UnknownTable / Field / Value, KindMismatch, OutOfRange, WrongTable wrong-table check off; i32 range check off
report/tests/quack_bind.rs report Catalog + CamLabels as a binder: 1 CAM lookup; after a label rename, the new label binds to the identical query and the old one is unknown bypass the codebook
dir-sim/tests/quack_bind.rs KeyId field: two spellings → one query; ValueId field: two spellings → two queries; the key query lowers to exactly key_eq_program(key) bypass the codebook
quack/tests/string_fence.rs no text in the executable IR add a String field to lib.rs
quack/tests/canonical_le.rs EqU32Strided(0x12345678) matches exactly the records holding [0x78,0x56,0x34,0x12], never the swapped spelling (strides 4 and 16, group path and tail); executor and reference agree; Register128::from_words bytes are read back. Literal byte fixtures, so it cannot pass merely on an LE host ndarray kernel → from_be_bytes; reference reader → from_be_bytes

The reference-reader disable first came back green. Equal LE and BE row counts let the count comparison tie, so the fixture is now asymmetric.

Totals: quack, report and dir-sim tests pass; contract register128 and mask-risc tests pass. fmt and clippy -D warnings are clean.

Deliberately not here

  • No SQL parser; SELECT COUNT(*) FROM users WHERE smtp = '…' would parse into the same Draft.
  • No Java migration. Target: View.where → Binder → quack::Query, which retires plan_lower. Answer parity is already proven by lowering_convergence.
  • No SAP redesign. The gap is that its forward text → code map is dropped at bind; keeping it cold per batch would give SAP a Binder::code.
  • No IAM change. users_with_key can become where_eq over the IAM binder in the test.
  • No Endian enum, no universal ABI scalar wrapper, no serialization layer, no shared dictionary implementation, no ContentId move, no persisted catalog.

🤖 Generated with Claude Code

https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg

A developer-facing binder in front of Quack's numeric IR:

    table("users").where_eq("smtp", "alice@x.de").count()
        .bind(&binder)  ->  Query { and([Plane(live), Cmp(Col, EqU32(id))]), Count }

- quack::bind (the crate's only text-bearing module): Draft / FieldRef /
  Literal / Op, the Binder trait (catalog + codebook, never mints), and
  BindError in the developer's vocabulary. quack::Query is already the
  resolved form, so no ResolvedQuery type is added.
- Schema follows the same lifecycle: create_table(..).width(..) ->
  Registrar -> ResolvedTable { id, width }. No crate owns a table catalog
  or allocation yet; Registrar has only a test implementation.
- tests/string_fence.rs: lib.rs (the executable IR and lowering) holds no
  text types; bind.rs is the only other module.
- Consumers keep their identity contracts behind the same frontend:
  report/tests/quack_bind.rs (CAM ordinal survives a label rename),
  dir-sim/tests/quack_bind.rs (ValueId exact vs KeyId comparison; the
  key-bound query lowers to exactly key_eq_program).
- Board entry: the two-world contract, the storage/query/schema
  lifecycle table, and the Java / SAP / IAM / SQL / DDL seams.

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

coderabbitai Bot commented Oct 5, 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 24 minutes.

Check out review usage here.

View limit details

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

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: b6be6396-2140-49a9-9ad1-8c864bba9d4c
📥 Commits

Reviewing files that changed from the base of the PR and between 11340e5 and b2c6d45.

📒 Files selected for processing (8)
  • .claude/board/entries/2026-10-05-quack-two-world-frontend.md
  • .claude/board/entries/README.md
  • crates/lance-graph-dir-sim/tests/quack_bind.rs
  • crates/lance-graph-quack/src/bind.rs
  • crates/lance-graph-quack/src/lib.rs
  • crates/lance-graph-quack/tests/canonical_le.rs
  • crates/lance-graph-quack/tests/string_fence.rs
  • crates/lance-graph-report/tests/quack_bind.rs
  • 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.

@cursor

cursor Bot commented Oct 5, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: 968ab1ee-c745-443e-b271-15e24c91ec6b)

claude added 3 commits October 5, 2026 05:52
Known-vector falsifier: Cmp::EqU32Strided(0x12345678) matches exactly the
records holding [0x78,0x56,0x34,0x12] and never the byte-swapped spelling,
through the executor (ndarray kernel) and the reference interpreter, for a
contiguous and a strided lane, in both the 16-lane group path and the tail.
Literal byte fixtures, so the test does not pass merely on an LE host.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg
The resolution membrane also fixes the physical representation: Quack's
Query stays semantic numeric IR (no Endian field, no ResolvedQuery), and
byte order exists only where a fixed-width integer meets raw bytes, where
it is little-endian. Byte arrays (Dn128, facet pattern/care) are order-
neutral. Documented in quack::bind and the board entry, with the byte-
boundary audit: every production reader is explicit LE; FacetCascade's
compute-lens reinterpret is fenced by a compile-time LE guard and reported,
not changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg
@AdaWorldAPI AdaWorldAPI changed the title quack: two-world frontend — describe once, resolve once, execute numeric quack: two-world frontend — describe once, resolve once, canonicalize once, execute numeric Oct 5, 2026

Copy link
Copy Markdown
Owner Author

test-with-coverage failed on d3f4255f, but not in a test: it failed in the Upload coverage to Codecov step, on a TLS handshake (ssl/tls alert handshake failure … SSL alert number 40). The coverage tests themselves passed. The step sets fail_ci_if_error: false, so the job still went red on a failure inside the upload action. This is the same Codecov TLS failure #1327 hit twice, and nothing in this PR touches the upload path. No fix exists on the repo side yet; the head has since moved to b2c6d456, and that run will show whether the failure recurs.


Generated by Claude Code

@AdaWorldAPI
AdaWorldAPI marked this pull request as ready for review October 5, 2026 06:01
@AdaWorldAPI
AdaWorldAPI merged commit 173af84 into main Oct 5, 2026
10 of 11 checks passed
AdaWorldAPI pushed a commit that referenced this pull request Oct 5, 2026
The upload step already sets fail_ci_if_error: false, but a TLS handshake
failure inside codecov-action itself throws before the uploader runs and
still failed the job (EPROTO, SSL alert 40, on #1327 and #1328) after every
test had passed. continue-on-error makes the step non-blocking as intended;
test failures still fail the cargo llvm-cov step above.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg
AdaWorldAPI pushed a commit that referenced this pull request Oct 5, 2026
… durable commit, storage supplies capabilities

Architecture-only follow-up to #1328. Records the read boundary (a backend
binds once before execution and never approximates Quack semantics), the
write boundary (Rubicon amortizes transient folds into one durable sparse
commit; 64k parallelism and Kanban are never storage requirements),
NodeGuid × Version vs backend physical history, the existing seams that
already bind before execution (quack::lower, mask-risc validate,
Activation::resolve_for_context), and non-binding backend mappings for
Lance/MOCA, RocksDB, Iceberg, DuckDB and S3.

Flags an open conflict: the proposed durable field-granular merge-on-read
write is not what contract::alpha implements (row-granular claims,
unclaimed = None never base, discardable). Not renamed or resolved here.

No code, no trait, no backend.

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