From de677c82edce9ba4a6d0a1c983757895583940e8 Mon Sep 17 00:00:00 2001 From: AdaWorldAPI Date: Thu, 22 Oct 2026 16:37:48 +0200 Subject: [PATCH 1/2] probe(dr-math): typed borrowed DR64 ClassView-style binding refusals --- crates/dr-math-binding-probe/Cargo.toml | 12 ++ crates/dr-math-binding-probe/README.md | 18 +++ crates/dr-math-binding-probe/src/lib.rs | 151 ++++++++++++++++++ crates/dr-math-binding-probe/tests/binding.rs | 87 ++++++++++ 4 files changed, 268 insertions(+) create mode 100644 crates/dr-math-binding-probe/Cargo.toml create mode 100644 crates/dr-math-binding-probe/README.md create mode 100644 crates/dr-math-binding-probe/src/lib.rs create mode 100644 crates/dr-math-binding-probe/tests/binding.rs diff --git a/crates/dr-math-binding-probe/Cargo.toml b/crates/dr-math-binding-probe/Cargo.toml new file mode 100644 index 000000000..4969346c0 --- /dev/null +++ b/crates/dr-math-binding-probe/Cargo.toml @@ -0,0 +1,12 @@ +# D-DRMC-1: isolated, non-production structural binding probe. +[package] +name = "dr-math-binding-probe" +version = "0.0.0" +edition = "2024" +publish = false +description = "Borrowed DR64 / ClassView-style typed structure and source witness admission probe; never executes population rows." + +[workspace] + +[dependencies] +lance-graph-contract = { path = "../lance-graph-contract" } diff --git a/crates/dr-math-binding-probe/README.md b/crates/dr-math-binding-probe/README.md new file mode 100644 index 000000000..fbdfac08b --- /dev/null +++ b/crates/dr-math-binding-probe/README.md @@ -0,0 +1,18 @@ +# D-DRMC-1: DR64 typed borrow admission probe + +A **lab-only** carrier/binder seam next to Quack's existing developer-name-to-numeric resolution, not a replacement for `lance-graph-quack::bind::Binder`. Uses `lance_graph_contract::class_view::ClassId` and `morton8x8` directly; does not add types to the zero-dependency contract, modify the shared Quack binding code or implement another bulk executor. + +Run from the crate's directory (own `[workspace]`): `cargo test`. + +## Proved by these tests (once executed) + +- A DR64 register is borrowed as exactly `&[u64; 64]` (512 bytes), not copied or hydrated. +- `Identity×Identity` and `Identity×Property` are different **semantic method signatures** even with identical physical bits. +- Method ordinal matters within the same ClassView class. +- K3/NULL and BAG are refused for a bare Boolean bitmap; no FALSE/UNKNOWN collapse and no lost multiplicity. +- A stale source, relationship map, coordinate frame, focus or scope is refused separately. +- Morton 8×8 arithmetic addresses `(x,y)` precisely but does not establish relation truth. + +**Explicit non-proofs:** `BindingWitness` values are caller-supplied (not signed or independently attested), `MethodSignature` is a fixture rather than registry-resolved ClassView method metadata, no optimized fold or real foreign relation binder executes, no ACL policy is evaluated. Production admission requires the existing source owner, D-RMAP-1A table bindings, D-RFT/D-QSP method law, index seal and permission checks. A bitmap is not a universal row binding and cannot express all graph path multiplicities. + +Next stack: finite population claim/violation ledger, then compiler recipe eligibility. diff --git a/crates/dr-math-binding-probe/src/lib.rs b/crates/dr-math-binding-probe/src/lib.rs new file mode 100644 index 000000000..b6ca5f3e6 --- /dev/null +++ b/crates/dr-math-binding-probe/src/lib.rs @@ -0,0 +1,151 @@ +//! D-DRMC-1: borrowed typed DR64 operand admission, NOT an evaluator. +//! +//! This is a stand-alone probe of the ClassView -> Quack-style BIND contract. +//! It does not implement a ClassView registry, own RelationshipMap data, decide +//! mathematical equivalence, attest a source, or run any per-row operation. +//! Canonical code will remain in the existing owners; this lab tests the shape. +//! +//! The bitplane is precisely 64 rows of 64 participation bits (512 B). It +//! carries neither global ids nor SQL NULL, bag multiplicity or provenance. +#![forbid(unsafe_code)] + +use lance_graph_contract::class_view::ClassId; + +/// What one numeric coordinate means according to the resolved ClassView. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum AxisKind { + Identity, + Property, + Community, + Capability, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct Axis { + pub class: ClassId, + pub kind: AxisKind, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum Truth { + TwoValued, + ThreeValued, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum Population { + Distinct, + Bag, +} + +/// Per-method semantic law; *not* a single semantics choice for a whole class. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct MethodSignature { + pub owner_class: ClassId, + pub method_ordinal: u16, + pub row: Axis, + pub column: Axis, + pub truth: Truth, + pub population: Population, +} + +impl MethodSignature { + pub const fn presence( + owner_class: ClassId, + method_ordinal: u16, + row: Axis, + column: Axis, + ) -> Self { + Self { + owner_class, + method_ordinal, + row, + column, + truth: Truth::TwoValued, + population: Population::Distinct, + } + } +} + +/// Caller-provided *values*, not independently attested source identities. +/// Source owners must attest these fields before production use. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct BindingWitness { + pub source_revision: u64, + pub relation_map_revision: u64, + pub coordinate_revision: u64, + pub focus_revision: u64, + pub authorization_scope: u64, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Refusal { + MethodChanged, + AxisChanged, + SemanticsChanged, + ThreeValuedNeedsValidity, + BagNeedsMultiplicity, + SourceChanged, + MapChanged, + CoordinatesChanged, + FocusChanged, + ScopeChanged, +} + +/// Borrowed fixed-width register, with resolved type and current witness. +/// Never owns the payload or reinterprets bits into foreign keys. +#[derive(Clone, Copy, Debug)] +pub struct BoundDr64<'a> { + pub method: MethodSignature, + pub witness: BindingWitness, + pub rows: &'a [u64; 64], +} + +/// Validate *the declaration and equality of supplied witnesses*, not their +/// authenticity. A production binder must additionally check the sealed +/// source/index identity, ClassView registry, tenant ACL, and lifetime. +pub fn bind_dr64<'a>( + declared: MethodSignature, + requested: MethodSignature, + bound_at: BindingWitness, + observed_now: BindingWitness, + rows: &'a [u64; 64], +) -> Result, Refusal> { + if (declared.owner_class, declared.method_ordinal) + != (requested.owner_class, requested.method_ordinal) + { + return Err(Refusal::MethodChanged); + } + if (declared.row, declared.column) != (requested.row, requested.column) { + return Err(Refusal::AxisChanged); + } + if (declared.truth, declared.population) != (requested.truth, requested.population) { + return Err(Refusal::SemanticsChanged); + } + if declared.truth != Truth::TwoValued { + return Err(Refusal::ThreeValuedNeedsValidity); + } + if declared.population != Population::Distinct { + return Err(Refusal::BagNeedsMultiplicity); + } + if bound_at.source_revision != observed_now.source_revision { + return Err(Refusal::SourceChanged); + } + if bound_at.relation_map_revision != observed_now.relation_map_revision { + return Err(Refusal::MapChanged); + } + if bound_at.coordinate_revision != observed_now.coordinate_revision { + return Err(Refusal::CoordinatesChanged); + } + if bound_at.focus_revision != observed_now.focus_revision { + return Err(Refusal::FocusChanged); + } + if bound_at.authorization_scope != observed_now.authorization_scope { + return Err(Refusal::ScopeChanged); + } + Ok(BoundDr64 { + method: declared, + witness: observed_now, + rows, + }) +} diff --git a/crates/dr-math-binding-probe/tests/binding.rs b/crates/dr-math-binding-probe/tests/binding.rs new file mode 100644 index 000000000..a5ca32784 --- /dev/null +++ b/crates/dr-math-binding-probe/tests/binding.rs @@ -0,0 +1,87 @@ +use dr_math_binding_probe::{ + bind_dr64, Axis, AxisKind, BindingWitness, MethodSignature, Population, Refusal, Truth, +}; +use lance_graph_contract::morton8x8::Morton8x8; + +fn id() -> Axis { + Axis { class: 12, kind: AxisKind::Identity } +} +fn property() -> Axis { + Axis { class: 12, kind: AxisKind::Property } +} +fn witness() -> BindingWitness { + BindingWitness { + source_revision: 7, + relation_map_revision: 11, + coordinate_revision: 3, + focus_revision: 5, + authorization_scope: 100, + } +} +fn signature() -> MethodSignature { + MethodSignature::presence(12, 1, id(), property()) +} +fn bits() -> [u64; 64] { + let mut rows = [0u64; 64]; + rows[17] |= 1u64 << 29; + rows +} + +#[test] +fn bind_borrows_exact_bytes_no_materialization() { + let rows = bits(); + let bound = bind_dr64(signature(), signature(), witness(), witness(), &rows).unwrap(); + assert!(std::ptr::eq(bound.rows, &rows)); + assert_eq!(bound.rows[17], 1u64 << 29); + assert_eq!(std::mem::size_of_val(&rows), 512); +} +#[test] +fn physical_equal_identity_squared_and_identity_property_are_not_interchangeable() { + let rows = bits(); + let identity_squared = MethodSignature::presence(12, 1, id(), id()); + assert_eq!( + bind_dr64(signature(), identity_squared, witness(), witness(), &rows).unwrap_err(), + Refusal::AxisChanged + ); +} +#[test] +fn method_is_not_just_a_class_semantics() { + let rows = bits(); + let other_method = MethodSignature::presence(12, 2, id(), property()); + assert_eq!(bind_dr64(signature(), other_method, witness(), witness(), &rows).unwrap_err(), Refusal::MethodChanged); +} +#[test] +fn null_and_bag_require_separate_carriers() { + let rows = bits(); + let mut k3 = signature(); + k3.truth = Truth::ThreeValued; + assert_eq!(bind_dr64(k3, k3, witness(), witness(), &rows).unwrap_err(), Refusal::ThreeValuedNeedsValidity); + let mut bag = signature(); + bag.population = Population::Bag; + assert_eq!(bind_dr64(bag, bag, witness(), witness(), &rows).unwrap_err(), Refusal::BagNeedsMultiplicity); +} +#[test] +fn every_witness_component_is_a_separate_refusal() { + let rows = bits(); + let w = witness(); + let cases = [ + (BindingWitness { source_revision: 8, ..w }, Refusal::SourceChanged), + (BindingWitness { relation_map_revision: 12, ..w }, Refusal::MapChanged), + (BindingWitness { coordinate_revision: 4, ..w }, Refusal::CoordinatesChanged), + (BindingWitness { focus_revision: 6, ..w }, Refusal::FocusChanged), + (BindingWitness { authorization_scope: 101, ..w }, Refusal::ScopeChanged), + ]; + for (changed, expected) in cases { + assert_eq!(bind_dr64(signature(), signature(), w, changed, &rows).unwrap_err(), expected); + } +} +#[test] +fn cartesian_global_coordinate_is_not_a_foreign_relation_proof() { + let code = Morton8x8::from_xy(17, 29); + assert_eq!((code.x(), code.y()), (17, 29)); + // Cartesian addressability is exact; occupancy and semantics still + // require a DR relationship bit and a ClassView domain binding. + let rows = bits(); + let b = bind_dr64(signature(), signature(), witness(), witness(), &rows).unwrap(); + assert_eq!(b.rows[17] & (1u64 << 29), 1u64 << 29); +} From ca05b1d1e50117a33fdd6f0998757f0599d7f5c8 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Oct 2026 15:16:03 +0000 Subject: [PATCH 2/2] probe(dr-math): split frame, focus and scope; bind validity with R Coresearch D-DRMC items X1, X2, X3 (operator-chosen). - The 5-field caller-built BindingWitness is gone. The frame (source, relationship map, coordinates) is a Frame issued by a FrameStore and compared by identity of the issue, mirroring dir-rmap-probe's Witness (Weak + ptr_eq; that type is tied to dir-sim's VersionStore, so it is not reused). Two stores at equal revisions do not alias; a re-seal at equal numbers is FrameResealed. - Focus (motion) and Scope (policy) are separate types and separate guards; moving either leaves the frame valid. - Validity is a carrier of R: Operand { rows, validity }. A three-valued method needs it, a two-valued one refuses it, so no validity plane can be bound to a foreign method. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg --- crates/dr-math-binding-probe/README.md | 6 +- crates/dr-math-binding-probe/src/lib.rs | 190 ++++++++++++--- crates/dr-math-binding-probe/tests/binding.rs | 221 +++++++++++++++--- 3 files changed, 354 insertions(+), 63 deletions(-) diff --git a/crates/dr-math-binding-probe/README.md b/crates/dr-math-binding-probe/README.md index fbdfac08b..67a671d24 100644 --- a/crates/dr-math-binding-probe/README.md +++ b/crates/dr-math-binding-probe/README.md @@ -9,10 +9,10 @@ Run from the crate's directory (own `[workspace]`): `cargo test`. - A DR64 register is borrowed as exactly `&[u64; 64]` (512 bytes), not copied or hydrated. - `Identity×Identity` and `Identity×Property` are different **semantic method signatures** even with identical physical bits. - Method ordinal matters within the same ClassView class. -- K3/NULL and BAG are refused for a bare Boolean bitmap; no FALSE/UNKNOWN collapse and no lost multiplicity. -- A stale source, relationship map, coordinate frame, focus or scope is refused separately. +- A three-valued method binds only with its validity bits, which travel WITH the relation bits (`Operand`); a two-valued method refuses validity; BAG is refused. No FALSE/UNKNOWN collapse and no lost multiplicity. +- Three guards, three types: the **frame** (source, relationship map, coordinates) is a store-issued seal compared by identity, so two stores at equal revisions do not alias and a re-seal is a new frame; the **focus** (motion) and the **scope** (policy) are separate guards that leave the frame valid when they move. Each moved revision is its own refusal. - Morton 8×8 arithmetic addresses `(x,y)` precisely but does not establish relation truth. -**Explicit non-proofs:** `BindingWitness` values are caller-supplied (not signed or independently attested), `MethodSignature` is a fixture rather than registry-resolved ClassView method metadata, no optimized fold or real foreign relation binder executes, no ACL policy is evaluated. Production admission requires the existing source owner, D-RMAP-1A table bindings, D-RFT/D-QSP method law, index seal and permission checks. A bitmap is not a universal row binding and cannot express all graph path multiplicities. +**Explicit non-proofs:** `FrameStore` is a lab stand-in for the source owner (the seal proves issue identity, not that the revisions describe real data); focus and scope are caller-supplied values, `MethodSignature` is a fixture rather than registry-resolved ClassView method metadata, no optimized fold or real foreign relation binder executes, no ACL policy is evaluated. Production admission requires the existing source owner, D-RMAP-1A table bindings, D-RFT/D-QSP method law, index seal and permission checks. A bitmap is not a universal row binding and cannot express all graph path multiplicities. Next stack: finite population claim/violation ledger, then compiler recipe eligibility. diff --git a/crates/dr-math-binding-probe/src/lib.rs b/crates/dr-math-binding-probe/src/lib.rs index b6ca5f3e6..196afc4eb 100644 --- a/crates/dr-math-binding-probe/src/lib.rs +++ b/crates/dr-math-binding-probe/src/lib.rs @@ -6,9 +6,12 @@ //! Canonical code will remain in the existing owners; this lab tests the shape. //! //! The bitplane is precisely 64 rows of 64 participation bits (512 B). It -//! carries neither global ids nor SQL NULL, bag multiplicity or provenance. +//! carries neither global ids, bag multiplicity nor provenance. A three-valued +//! method carries its validity bits WITH the relation bits (see [`Operand`]). #![forbid(unsafe_code)] +use std::sync::{Arc, Weak}; + use lance_graph_contract::class_view::ClassId; /// What one numeric coordinate means according to the resolved ClassView. @@ -67,15 +70,128 @@ impl MethodSignature { } } -/// Caller-provided *values*, not independently attested source identities. -/// Source owners must attest these fields before production use. +/// The revisions that identify the reference: which source, which relation +/// map, which coordinate assignment. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct FrameRevisions { + pub source: u64, + pub relation_map: u64, + pub coordinates: u64, +} + +/// Lab stand-in for the owner of a frame. Only it issues [`Frame`]s, and a +/// frame compares by identity of that issue, never by value: two stores that +/// sit at the same numbers do not alias, and re-sealing equal numbers is a +/// new frame. The mechanism mirrors `dir-rmap-probe`'s `Witness` (a +/// `Weak` compared by `ptr_eq`); that type is tied to dir-sim's +/// `VersionStore`, so it is not reused here. +#[derive(Debug)] +pub struct FrameStore { + identity: Arc<()>, + current: Arc, +} + +impl FrameStore { + pub fn new(revisions: FrameRevisions) -> Self { + Self { + identity: Arc::new(()), + current: Arc::new(revisions), + } + } + + /// Seal the current frame. + pub fn seal(&self) -> Frame { + Frame { + store: Arc::downgrade(&self.identity), + seal: Arc::downgrade(&self.current), + at: *self.current, + } + } + + /// Move to new revisions; every frame sealed before is stale from now on. + pub fn advance(&mut self, revisions: FrameRevisions) { + self.current = Arc::new(revisions); + } + + pub fn revisions(&self) -> FrameRevisions { + *self.current + } + + /// `Ok` iff `frame` is this store's current seal. A frame of another store + /// is `ForeignFrame` even at equal numbers; a stale frame of this store + /// names the first revision that moved, or `FrameResealed` if none did. + pub fn check(&self, frame: &Frame) -> Result<(), Refusal> { + if !Weak::ptr_eq(&frame.store, &Arc::downgrade(&self.identity)) { + return Err(Refusal::ForeignFrame); + } + if Weak::ptr_eq(&frame.seal, &Arc::downgrade(&self.current)) { + return Ok(()); + } + let (then, now) = (frame.at, *self.current); + Err(if then.source != now.source { + Refusal::SourceChanged + } else if then.relation_map != now.relation_map { + Refusal::MapChanged + } else if then.coordinates != now.coordinates { + Refusal::CoordinatesChanged + } else { + Refusal::FrameResealed + }) + } +} + +/// A store-issued seal over the reference revisions. No public constructor: a +/// caller cannot build a frame from numbers, only receive one from +/// [`FrameStore::seal`]. +#[derive(Clone, Debug)] +pub struct Frame { + store: Weak<()>, + seal: Weak, + at: FrameRevisions, +} + +impl Frame { + /// The revisions this frame was sealed at (for reporting, never identity). + pub fn revisions(&self) -> FrameRevisions { + self.at + } + + /// Same issue of the same store. + pub fn same_issue(&self, other: &Frame) -> bool { + Weak::ptr_eq(&self.store, &other.store) && Weak::ptr_eq(&self.seal, &other.seal) + } +} + +/// Which 64x64 window of the frame is read. Motion, not reference: a new +/// focus over the same frame is not a new frame. #[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct BindingWitness { - pub source_revision: u64, - pub relation_map_revision: u64, - pub coordinate_revision: u64, - pub focus_revision: u64, - pub authorization_scope: u64, +pub struct Focus { + pub revision: u64, +} + +/// What the reader is authorized to see. Policy, checked as its own guard at +/// borrow time; never part of frame identity. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Scope { + pub id: u64, +} + +/// What a binding was made under. +#[derive(Clone, Debug)] +pub struct Guards { + pub frame: Frame, + pub focus: Focus, + pub scope: Scope, +} + +/// The borrowed relation bits and, for a three-valued method, which of them +/// are known. Validity is a carrier OF R (one bit per R bit, like an Arrow +/// validity bitmap or Quack's `BoundField.validity`), never a separately typed +/// plane with a method of its own. +#[derive(Clone, Copy, Debug)] +pub struct Operand<'a> { + pub rows: &'a [u64; 64], + pub validity: Option<&'a [u64; 64]>, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -84,32 +200,41 @@ pub enum Refusal { AxisChanged, SemanticsChanged, ThreeValuedNeedsValidity, + TwoValuedHasNoValidity, BagNeedsMultiplicity, + ForeignFrame, SourceChanged, MapChanged, CoordinatesChanged, + FrameResealed, FocusChanged, ScopeChanged, } -/// Borrowed fixed-width register, with resolved type and current witness. -/// Never owns the payload or reinterprets bits into foreign keys. -#[derive(Clone, Copy, Debug)] +/// Borrowed fixed-width register with its resolved type and guards. Never +/// owns the payload or reinterprets bits into foreign keys. +#[derive(Clone, Debug)] pub struct BoundDr64<'a> { pub method: MethodSignature, - pub witness: BindingWitness, + pub frame: Frame, + pub focus: Focus, + pub scope: Scope, pub rows: &'a [u64; 64], + pub validity: Option<&'a [u64; 64]>, } -/// Validate *the declaration and equality of supplied witnesses*, not their -/// authenticity. A production binder must additionally check the sealed -/// source/index identity, ClassView registry, tenant ACL, and lifetime. +/// Validate the declaration, then each guard on its own: the frame against +/// the store that issued it, then the focus, then the scope. Checks seal +/// identity and equality of focus and scope, not their authenticity; a +/// production binder also needs the ClassView registry, the tenant ACL and +/// the source owner. pub fn bind_dr64<'a>( + store: &FrameStore, declared: MethodSignature, requested: MethodSignature, - bound_at: BindingWitness, - observed_now: BindingWitness, - rows: &'a [u64; 64], + bound_at: &Guards, + now: (Focus, Scope), + operand: Operand<'a>, ) -> Result, Refusal> { if (declared.owner_class, declared.method_ordinal) != (requested.owner_class, requested.method_ordinal) @@ -122,30 +247,27 @@ pub fn bind_dr64<'a>( if (declared.truth, declared.population) != (requested.truth, requested.population) { return Err(Refusal::SemanticsChanged); } - if declared.truth != Truth::TwoValued { - return Err(Refusal::ThreeValuedNeedsValidity); + match (declared.truth, operand.validity) { + (Truth::ThreeValued, None) => return Err(Refusal::ThreeValuedNeedsValidity), + (Truth::TwoValued, Some(_)) => return Err(Refusal::TwoValuedHasNoValidity), + _ => {} } if declared.population != Population::Distinct { return Err(Refusal::BagNeedsMultiplicity); } - if bound_at.source_revision != observed_now.source_revision { - return Err(Refusal::SourceChanged); - } - if bound_at.relation_map_revision != observed_now.relation_map_revision { - return Err(Refusal::MapChanged); - } - if bound_at.coordinate_revision != observed_now.coordinate_revision { - return Err(Refusal::CoordinatesChanged); - } - if bound_at.focus_revision != observed_now.focus_revision { + store.check(&bound_at.frame)?; + if bound_at.focus != now.0 { return Err(Refusal::FocusChanged); } - if bound_at.authorization_scope != observed_now.authorization_scope { + if bound_at.scope != now.1 { return Err(Refusal::ScopeChanged); } Ok(BoundDr64 { method: declared, - witness: observed_now, - rows, + frame: bound_at.frame.clone(), + focus: now.0, + scope: now.1, + rows: operand.rows, + validity: operand.validity, }) } diff --git a/crates/dr-math-binding-probe/tests/binding.rs b/crates/dr-math-binding-probe/tests/binding.rs index a5ca32784..ef07254d2 100644 --- a/crates/dr-math-binding-probe/tests/binding.rs +++ b/crates/dr-math-binding-probe/tests/binding.rs @@ -1,21 +1,35 @@ use dr_math_binding_probe::{ - bind_dr64, Axis, AxisKind, BindingWitness, MethodSignature, Population, Refusal, Truth, + Axis, AxisKind, Focus, FrameRevisions, FrameStore, Guards, MethodSignature, Operand, + Population, Refusal, Scope, Truth, bind_dr64, }; use lance_graph_contract::morton8x8::Morton8x8; fn id() -> Axis { - Axis { class: 12, kind: AxisKind::Identity } + Axis { + class: 12, + kind: AxisKind::Identity, + } } fn property() -> Axis { - Axis { class: 12, kind: AxisKind::Property } + Axis { + class: 12, + kind: AxisKind::Property, + } } -fn witness() -> BindingWitness { - BindingWitness { - source_revision: 7, - relation_map_revision: 11, - coordinate_revision: 3, - focus_revision: 5, - authorization_scope: 100, +fn revisions() -> FrameRevisions { + FrameRevisions { + source: 7, + relation_map: 11, + coordinates: 3, + } +} +const FOCUS: Focus = Focus { revision: 5 }; +const SCOPE: Scope = Scope { id: 100 }; +fn guards(store: &FrameStore) -> Guards { + Guards { + frame: store.seal(), + focus: FOCUS, + scope: SCOPE, } } fn signature() -> MethodSignature { @@ -26,62 +40,217 @@ fn bits() -> [u64; 64] { rows[17] |= 1u64 << 29; rows } +fn two_valued(rows: &[u64; 64]) -> Operand<'_> { + Operand { + rows, + validity: None, + } +} #[test] fn bind_borrows_exact_bytes_no_materialization() { + let store = FrameStore::new(revisions()); let rows = bits(); - let bound = bind_dr64(signature(), signature(), witness(), witness(), &rows).unwrap(); + let bound = bind_dr64( + &store, + signature(), + signature(), + &guards(&store), + (FOCUS, SCOPE), + two_valued(&rows), + ) + .unwrap(); assert!(std::ptr::eq(bound.rows, &rows)); assert_eq!(bound.rows[17], 1u64 << 29); assert_eq!(std::mem::size_of_val(&rows), 512); } + #[test] fn physical_equal_identity_squared_and_identity_property_are_not_interchangeable() { + let store = FrameStore::new(revisions()); let rows = bits(); let identity_squared = MethodSignature::presence(12, 1, id(), id()); assert_eq!( - bind_dr64(signature(), identity_squared, witness(), witness(), &rows).unwrap_err(), + bind_dr64( + &store, + signature(), + identity_squared, + &guards(&store), + (FOCUS, SCOPE), + two_valued(&rows) + ) + .unwrap_err(), Refusal::AxisChanged ); } + #[test] fn method_is_not_just_a_class_semantics() { + let store = FrameStore::new(revisions()); let rows = bits(); let other_method = MethodSignature::presence(12, 2, id(), property()); - assert_eq!(bind_dr64(signature(), other_method, witness(), witness(), &rows).unwrap_err(), Refusal::MethodChanged); + assert_eq!( + bind_dr64( + &store, + signature(), + other_method, + &guards(&store), + (FOCUS, SCOPE), + two_valued(&rows) + ) + .unwrap_err(), + Refusal::MethodChanged + ); } + +/// Validity travels with R and is typed by R's method: a three-valued method +/// needs it, a two-valued one must not carry it. There is no second method a +/// validity plane could be bound to, so a "validity of a foreign method" +/// cannot be written. #[test] -fn null_and_bag_require_separate_carriers() { +fn validity_is_a_carrier_of_the_relation_typed_by_its_method() { + let store = FrameStore::new(revisions()); let rows = bits(); + let known = bits(); let mut k3 = signature(); k3.truth = Truth::ThreeValued; - assert_eq!(bind_dr64(k3, k3, witness(), witness(), &rows).unwrap_err(), Refusal::ThreeValuedNeedsValidity); + let bind = |m: MethodSignature, operand| { + bind_dr64(&store, m, m, &guards(&store), (FOCUS, SCOPE), operand) + }; + assert_eq!( + bind(k3, two_valued(&rows)).unwrap_err(), + Refusal::ThreeValuedNeedsValidity + ); + let with_validity = Operand { + rows: &rows, + validity: Some(&known), + }; + let bound = bind(k3, with_validity).unwrap(); + assert!(std::ptr::eq(bound.validity.unwrap(), &known)); + assert_eq!( + bind(signature(), with_validity).unwrap_err(), + Refusal::TwoValuedHasNoValidity + ); let mut bag = signature(); bag.population = Population::Bag; - assert_eq!(bind_dr64(bag, bag, witness(), witness(), &rows).unwrap_err(), Refusal::BagNeedsMultiplicity); + assert_eq!( + bind(bag, two_valued(&rows)).unwrap_err(), + Refusal::BagNeedsMultiplicity + ); } + +/// Each moved revision is its own refusal, and a re-seal at equal numbers is +/// still a new frame. #[test] -fn every_witness_component_is_a_separate_refusal() { +fn every_frame_revision_is_a_separate_refusal() { let rows = bits(); - let w = witness(); + let r = revisions(); let cases = [ - (BindingWitness { source_revision: 8, ..w }, Refusal::SourceChanged), - (BindingWitness { relation_map_revision: 12, ..w }, Refusal::MapChanged), - (BindingWitness { coordinate_revision: 4, ..w }, Refusal::CoordinatesChanged), - (BindingWitness { focus_revision: 6, ..w }, Refusal::FocusChanged), - (BindingWitness { authorization_scope: 101, ..w }, Refusal::ScopeChanged), + (FrameRevisions { source: 8, ..r }, Refusal::SourceChanged), + ( + FrameRevisions { + relation_map: 12, + ..r + }, + Refusal::MapChanged, + ), + ( + FrameRevisions { + coordinates: 4, + ..r + }, + Refusal::CoordinatesChanged, + ), + (r, Refusal::FrameResealed), ]; - for (changed, expected) in cases { - assert_eq!(bind_dr64(signature(), signature(), w, changed, &rows).unwrap_err(), expected); + for (moved, expected) in cases { + let mut store = FrameStore::new(r); + let bound_at = guards(&store); + store.advance(moved); + assert_eq!( + bind_dr64( + &store, + signature(), + signature(), + &bound_at, + (FOCUS, SCOPE), + two_valued(&rows) + ) + .unwrap_err(), + expected + ); } } + +/// The seal is store-issued identity, not equality of numbers: a frame of a +/// second store at the same revisions does not bind against the first. +#[test] +fn two_stores_at_equal_revisions_do_not_alias() { + let rows = bits(); + let first = FrameStore::new(revisions()); + let second = FrameStore::new(revisions()); + assert_eq!(first.revisions(), second.revisions()); + assert!(!first.seal().same_issue(&second.seal())); + assert!(first.seal().same_issue(&first.seal())); + assert_eq!( + bind_dr64( + &first, + signature(), + signature(), + &guards(&second), + (FOCUS, SCOPE), + two_valued(&rows) + ) + .unwrap_err(), + Refusal::ForeignFrame + ); +} + +/// Focus is motion and scope is policy: each is its own guard, and moving +/// either leaves the frame valid. +#[test] +fn focus_and_scope_are_guards_apart_from_the_frame() { + let store = FrameStore::new(revisions()); + let rows = bits(); + let bound_at = guards(&store); + let bind = |now| { + bind_dr64( + &store, + signature(), + signature(), + &bound_at, + now, + two_valued(&rows), + ) + }; + assert_eq!( + bind((Focus { revision: 6 }, SCOPE)).unwrap_err(), + Refusal::FocusChanged + ); + assert_eq!( + bind((FOCUS, Scope { id: 101 })).unwrap_err(), + Refusal::ScopeChanged + ); + assert_eq!(store.check(&bound_at.frame), Ok(())); + assert!(bind((FOCUS, SCOPE)).is_ok()); +} + #[test] fn cartesian_global_coordinate_is_not_a_foreign_relation_proof() { let code = Morton8x8::from_xy(17, 29); assert_eq!((code.x(), code.y()), (17, 29)); // Cartesian addressability is exact; occupancy and semantics still // require a DR relationship bit and a ClassView domain binding. + let store = FrameStore::new(revisions()); let rows = bits(); - let b = bind_dr64(signature(), signature(), witness(), witness(), &rows).unwrap(); + let b = bind_dr64( + &store, + signature(), + signature(), + &guards(&store), + (FOCUS, SCOPE), + two_valued(&rows), + ) + .unwrap(); assert_eq!(b.rows[17] & (1u64 << 29), 1u64 << 29); }