diff --git a/.claude/board/AGENT_LOG.md b/.claude/board/AGENT_LOG.md index e281827c..d83335c8 100644 --- a/.claude/board/AGENT_LOG.md +++ b/.claude/board/AGENT_LOG.md @@ -1,3 +1,7 @@ +## 2026-06-13 — #489 canonicalised: wire-in + self-describing Display + retire Phase-A wrapper + +**bardioc cross-session.** Operator pin: *"#489 is canonical."* Audited OGAR/CLAUDE.md P0 against `canonical_node.rs` group-by-group. Key (classid·HEEL·HIP·TWIG·family·identity = 8·4·4·4·6·6 hex) matches exactly; RFC-WAIVED matches ("No UUID ceremony"); 3×4 uniform tiers match (each u16; tier-of-level = `level >> 2`); 16-byte EdgeBlock at fixed offset = row-layout analogue of the zero-fallback ladder (default class's default ClassView reserves it, registry-resolved opt-out for non-default classes, "reserve-don't-reclaim" at row level). **One gap closed:** canon mandates *"every printed GUID is self-describing at sight"* via the dash-groups, wrapper had no `Display`. Added `impl Display for NodeGuid` emitting canonical `{classid:08x}-{heel:04x}-{hip:04x}-{twig:04x}-{family:06x}{identity:06x}` (LE in-memory bytes folded through the accessors so hex print is canon-ordered regardless). +2 Display tests. **Phase-A wrapper retired in the same PR** (operator: *"delete #480 from your mind"*): `identity.rs` deleted (UUIDv8 NodeGuid + RFC ceremony bits + IDENTITY_LAYOUT_VERSION + SHAPE_HASH_BITS/LOCAL_BITS — all canon-incompatible per *"wrappers adapt to the canon, never the reverse"*); `pub use identity::{NodeGuid, IDENTITY_LAYOUT_VERSION}` → `pub use canonical_node::{EdgeBlock, NodeGuid, NodeRow}`; `pub mod identity;` removed. Two stale doc references reworded: `hhtl.rs:192` (`from_packed` now a general HHTL utility, not identity::NodeGuid-specific), `lance-graph-ontology/src/registry.rs:405` (`niblepath_of` now points at the canon's `classid·HEEL·HIP·TWIG` resolution). `cargo test -p lance-graph-contract --lib`: **594/594 green** (−10 retired UUIDv8 tests, +2 Display, +8 wire-in canonical_node tests now visible); `cargo check -p lance-graph-ontology`: clean (5 pre-existing `oxrdf::Subject` deprecation warnings, untouched files); `cargo clippy -p lance-graph-contract --all-targets -- -D warnings`: clean. Anchored on #482 (GUID canon) + #489 (canonical_node) + OGAR/CLAUDE.md P0 (the canon itself, *"wrappers audited against this canon group-by-group — never the reverse"*). + ## 2026-06-11 — tombstone commit: emission artifacts removed per PR #477 follow-up **Main thread (Fable, session splat3d-cpu-simd-renderer).** Executed the PR #477 documented follow-up (the "what does NOT exist" table → source reality): removed `CollapseGateEmission` from `lance-graph-contract::collapse_gate` (+ lib.rs re-export; `MailboxId`/`MergeMode`/`GateDecision` survive), removed `MailboxSoA::emit()`, renamed `last_emission_cycle` → `last_active_cycle`, added in-place `consume_firing(row)` successor (same threshold + same-cycle-idempotency semantics, no carrier object), reworded 4 stale doc references (kanban/episodic_edges/witness_tombstone/mailbox_soa header), superseded the CLAUDE.md Baton-scoping block, fixed cycle-coherent-soa-snapshot-v1 D-SOA-SNAP-1/2 to generic `SnapshotProvider::Column` (closes #477 CodeRabbit Critical — contract stays zero-dep), closed TD-COLLAPSE-GATE-SMALLVEC-1 as moot. Verified #477 codex P2 (`verify_layout` ColumnOutOfBounds) already fixed on main with regression test. Tests: contract 594 (−8 emission, +2 gate/merge), driver 85 (emit tests → consume tests, +1 OOB), clippy clean, workspace check clean. Commit: in PR. diff --git a/crates/lance-graph-contract/src/canonical_node.rs b/crates/lance-graph-contract/src/canonical_node.rs index d2efaad2..853868d5 100644 --- a/crates/lance-graph-contract/src/canonical_node.rs +++ b/crates/lance-graph-contract/src/canonical_node.rs @@ -133,6 +133,30 @@ impl NodeGuid { } } +/// Canonical self-describing print: `classid-HEEL-HIP-TWIG-family·identity`. +/// +/// The dash-groups ARE the semantic delimiters — every printed GUID is +/// self-describing at sight (OGAR canon, P0). `{:08x}-{:04x}-{:04x}-{:04x}-{:06x}{:06x}` +/// renders the canonical 8-4-4-4-12 hex layout regardless of in-memory byte +/// order (the field accessors fold LE bytes into u32/u16/u24 first). +impl core::fmt::Display for NodeGuid { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let h = u16::from_le_bytes([self.0[4], self.0[5]]); + let p = u16::from_le_bytes([self.0[6], self.0[7]]); + let t = u16::from_le_bytes([self.0[8], self.0[9]]); + write!( + f, + "{:08x}-{:04x}-{:04x}-{:04x}-{:06x}{:06x}", + self.classid(), + h, + p, + t, + self.family(), + self.identity(), + ) + } +} + /// 16-byte canonical edge block: 12 in-family + 4 out-of-family. /// /// Canonical, not mandatory: the 16 bytes are ALWAYS reserved (zeroed when unused). @@ -233,4 +257,25 @@ mod tests { fn new_panics_on_identity_overflow() { let _ = NodeGuid::new(0, 0, 0, 0, 0, 0x0100_0000); } + + #[test] + fn display_is_canonical_self_describing() { + // Canon (OGAR P0): xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12 hex); + // groups = classid · HEEL · HIP · TWIG · family·identity. + let g = NodeGuid::new(0xDEAD_BEEF, 0x1111, 0x2222, 0x3333, 0x00_00AB, 0x00_00CD); + let s = g.to_string(); + assert_eq!(s, "deadbeef-1111-2222-3333-0000ab0000cd"); + assert_eq!(s.len(), 36, "8-4-4-4-12 + 4 hyphens"); + for i in [8usize, 13, 18, 23] { + assert_eq!(s.as_bytes()[i], b'-', "hyphen at {i}"); + } + } + + #[test] + fn display_zero_default_is_all_zeros() { + // Zero-fallback ladder visible at sight: classid + family == 0 prints + // as ...0...-...0... with identity-only discrimination. + let g = NodeGuid::local(0x00_00CD); + assert_eq!(g.to_string(), "00000000-0000-0000-0000-0000000000cd"); + } } diff --git a/crates/lance-graph-contract/src/hhtl.rs b/crates/lance-graph-contract/src/hhtl.rs index 84249c40..3ffe8349 100644 --- a/crates/lance-graph-contract/src/hhtl.rs +++ b/crates/lance-graph-contract/src/hhtl.rs @@ -189,8 +189,8 @@ impl NiblePath { } /// Reconstruct a path from its raw packed `(path, depth)` — the inverse of - /// [`packed`](NiblePath::packed). Used by `identity::NodeGuid` to round-trip - /// the routing-prefix it stores. + /// [`packed`](NiblePath::packed). General HHTL utility for round-tripping + /// a routing path through its packed `(u64, depth)` form. /// /// Returns `None` if `depth > MAX_DEPTH`, or if `path` has bits set above the /// `depth` nibbles (an inconsistent pack — leading nibbles must be the route, diff --git a/crates/lance-graph-contract/src/identity.rs b/crates/lance-graph-contract/src/identity.rs deleted file mode 100644 index dcca7836..00000000 --- a/crates/lance-graph-contract/src/identity.rs +++ /dev/null @@ -1,397 +0,0 @@ -//! # `identity` — the structured 128-bit node identity (UUIDv8). -//! -//! A [`NodeGuid`] is the HHTL nibble-address **formalized + namespaced** into a -//! standards-compliant UUIDv8 (RFC 9562). It is the workspace's first *stable -//! binary instance identity*: the cold path keys nodes by `node_id:u32` today, -//! the SPO hot path by a `u64` content `dn_hash` — neither is a stable, -//! globally-referenceable id. `NodeGuid` fills that gap. -//! -//! ## Compose, don't re-invent (the iron mandate) -//! -//! Every field is an existing committed scalar; `NodeGuid` is their composition, -//! never a parallel re-pack (which would duplicate the ratified `OD-CLASSID-WIDTH` -//! discriminator + `I-VSA-IDENTITIES`): -//! - `namespace:u8 | entity_type:u16 | kind:u8` = the `SchemaPtr.packed` u32 -//! convention (`lance-graph-ontology::namespace`). -//! - `niblepath_prefix` = a truncated [`NiblePath`](crate::hhtl::NiblePath) (the -//! HHTL tree address; full depth resolves from `entity_type`). -//! - `shape_hash` = a truncated `StructuralSignature` (the D-CLS shape witness). -//! - `local` = an instance index. -//! -//! ## Eineindeutigkeit — `entity_type` canonical, `NiblePath` the derived view -//! -//! `entity_type:u16` is the **exact, bijective** class identity (fixed-width, no -//! truncation; the registry mints it 1:1 with the class's `NiblePath`). The -//! `niblepath_prefix` in the GUID is a *derived routing cache* — coarse (≤4 -//! nibbles), equal to `niblepath_of(entity_type)` truncated to the prefix. A -//! *truncated* NiblePath CANNOT be the bijective identity (two deep classes -//! collide past the prefix — see `hhtl.rs`); so the exact identity is the dense -//! `entity_type`, and the prefix only accelerates `is_ancestor_of` routing. -//! -//! ## The five readings (register reads of one immutable key) -//! -//! - **resolve** `entity_type()` → `ClassView` (class-from-address, O(1)). -//! - **route** `niblepath()` → delegate switch (HHTL bit-shift). -//! - **witness** the frozen 16 bytes + the merkle chain (immutable, in place). -//! - **ground-truth** `shape_hash()` vs `resolve(addr).shape_hash_now` → drift. -//! - **dispatch-to-store** `as_bytes()` → `EntityKey` → consumer store. -//! -//! ## Immutability law -//! -//! Write-once. `entity_type` never updates (the lineage id — re-resolved from the -//! address for free). Drift *repair* is a new immutable version (Lance is -//! versioned), never an in-place mutation. `I-VSA-IDENTITIES` Test 0: a register -//! key that POINTS TO content, never VSA-bundled. - -use crate::hhtl::NiblePath; - -/// Layout version of [`NodeGuid`]'s byte geometry. Stamped into the GUID so a -/// future reader refuses to decode a layout it does not understand -/// (`I-LEGACY-API-FEATURE-GATED`). Bump on any field-bit change. -pub const IDENTITY_LAYOUT_VERSION: u8 = 1; - -/// RFC 9562 UUID version nibble — `8` = custom / application-defined layout. -const UUID_VERSION: u8 = 8; -/// RFC 9562 variant bits — `0b10`. -const UUID_VARIANT: u8 = 0b10; - -/// Bit-width of the `shape_hash` field (truncated `StructuralSignature`). -pub const SHAPE_HASH_BITS: u32 = 22; -/// Bit-width of the `local` instance-index field. -pub const LOCAL_BITS: u32 = 24; - -const SHAPE_HASH_MASK: u32 = (1u32 << SHAPE_HASH_BITS) - 1; -const LOCAL_MASK: u32 = (1u32 << LOCAL_BITS) - 1; - -/// A 128-bit immutable structured node identity (UUIDv8). -/// -/// Octet layout (canonical UUID byte order, octets 0-15): -/// ```text -/// 0 : namespace (u8) ─┐ -/// 1..=2 : entity_type (u16 BE) ├ SchemaPtr.packed u32 convention -/// 3 : kind (u8) ─┘ -/// 4..=5 : niblepath_prefix (u16 BE, ≤4 nibbles routing cache) -/// 6 : [hi nibble: UUID version=8][lo nibble: niblepath_depth 0..=4] -/// 7 : shape_hash[21:14] -/// 8 : [hi 2 bits: UUID variant=0b10][lo 6 bits: shape_hash[13:8]] -/// 9 : shape_hash[7:0] (shape_hash = 22 bits total) -/// 10..=12: local (u24 BE) -/// 13 : layout_version (u8) -/// 14..=15: spare (zero) -/// ``` -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -#[repr(C, align(16))] -pub struct NodeGuid([u8; 16]); - -impl NodeGuid { - /// Routing-cache prefix depth carried in the GUID (the full path resolves - /// from `entity_type`; this is the coarse, branchless-routing prefix). - pub const PREFIX_NIBBLES: u8 = 4; - - /// Compose a `NodeGuid` from its existing-field parts. `entity_type` is exact - /// (the bijective identity); `niblepath` is truncated to the routing prefix; - /// `shape_hash`/`local` are masked to their field widths. Sets the UUIDv8 - /// version/variant + the layout-version stamp. - pub fn new( - namespace: u8, - entity_type: u16, - kind: u8, - niblepath: NiblePath, - shape_hash: u32, - local: u32, - ) -> Self { - let (prefix, depth) = Self::truncate_prefix(niblepath); - let sh = shape_hash & SHAPE_HASH_MASK; - let loc = local & LOCAL_MASK; - let et = entity_type.to_be_bytes(); - let np = prefix.to_be_bytes(); - Self([ - namespace, - et[0], - et[1], - kind, - np[0], - np[1], - (UUID_VERSION << 4) | (depth & 0x0F), - ((sh >> 14) & 0xFF) as u8, - (UUID_VARIANT << 6) | ((sh >> 8) & 0x3F) as u8, - (sh & 0xFF) as u8, - ((loc >> 16) & 0xFF) as u8, - ((loc >> 8) & 0xFF) as u8, - (loc & 0xFF) as u8, - IDENTITY_LAYOUT_VERSION, - 0, - 0, - ]) - } - - /// Keep the top `PREFIX_NIBBLES` nibbles of a full path (root-first, so we - /// drop the lowest `depth - keep` nibbles). The result `is_ancestor_of` the - /// full path — a true coarse routing prefix. - fn truncate_prefix(path: NiblePath) -> (u16, u8) { - let (full, depth) = path.packed(); - let keep = if depth < Self::PREFIX_NIBBLES { - depth - } else { - Self::PREFIX_NIBBLES - }; - let drop = depth - keep; - let prefix = (full >> (4 * drop as u32)) as u16; - (prefix, keep) - } - - // ── accessors (the five readings start here) ── - - /// The raw 16 bytes — feed straight to `EntityKey(&[u8])` (dispatch-to-store). - #[must_use] - pub const fn as_bytes(&self) -> &[u8; 16] { - &self.0 - } - - /// Reconstruct from raw bytes (e.g. read back from a Lance column / EntityKey). - #[must_use] - pub const fn from_bytes(bytes: [u8; 16]) -> Self { - Self(bytes) - } - - /// OGIT namespace ordinal (`NamespaceId`). - #[must_use] - pub const fn namespace(&self) -> u8 { - self.0[0] - } - - /// The **exact, bijective** class identity (`entity_type_id` / `class_id`). - /// No truncation — this is the canonical class lineage id (resolve here). - #[must_use] - pub const fn entity_type(&self) -> u16 { - u16::from_be_bytes([self.0[1], self.0[2]]) - } - - /// `SchemaPtr` kind discriminator. - #[must_use] - pub const fn kind(&self) -> u8 { - self.0[3] - } - - /// Raw routing-prefix bits (≤4 nibbles). Prefer [`niblepath`](Self::niblepath). - #[must_use] - pub const fn niblepath_prefix(&self) -> u16 { - u16::from_be_bytes([self.0[4], self.0[5]]) - } - - /// Depth (nibble count) of the stored routing prefix (0..=4). - #[must_use] - pub const fn niblepath_depth(&self) -> u8 { - self.0[6] & 0x0F - } - - /// The routing prefix as a (truncated) [`NiblePath`] — the coarse delegate - /// switch. The EXACT class is [`entity_type`](Self::entity_type); this only - /// accelerates `is_ancestor_of`. - #[must_use] - pub fn niblepath(&self) -> Option { - NiblePath::from_packed(u64::from(self.niblepath_prefix()), self.niblepath_depth()) - } - - /// The shape_hash drift witness (truncated `StructuralSignature`, 22 bits). - #[must_use] - pub const fn shape_hash(&self) -> u32 { - ((self.0[7] as u32) << 14) | (((self.0[8] & 0x3F) as u32) << 8) | (self.0[9] as u32) - } - - /// The instance index (24 bits). - #[must_use] - pub const fn local(&self) -> u32 { - ((self.0[10] as u32) << 16) | ((self.0[11] as u32) << 8) | (self.0[12] as u32) - } - - /// The stamped [`IDENTITY_LAYOUT_VERSION`]. - #[must_use] - pub const fn layout_version(&self) -> u8 { - self.0[13] - } - - /// RFC 9562 version nibble (must be `8`). - #[must_use] - pub const fn version(&self) -> u8 { - self.0[6] >> 4 - } - - /// RFC 9562 variant bits (must be `0b10`). - #[must_use] - pub const fn variant(&self) -> u8 { - self.0[8] >> 6 - } - - /// Is this a well-formed UUIDv8 (version `8`, variant `0b10`)? The - /// self-explanatory-to-any-consumer property holds only when this is true. - #[must_use] - pub const fn is_valid_uuid_v8(&self) -> bool { - self.version() == UUID_VERSION && self.variant() == UUID_VARIANT - } -} - -impl core::fmt::Display for NodeGuid { - /// Canonical UUID string `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`. - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let b = &self.0; - write!( - f, - "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}", - b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15] - ) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - /// A deep example path: basin 0x2 → 0x5 → 0xA → 0x3 → 0x7 (depth 5, deeper - /// than the 4-nibble GUID prefix). - fn deep_path() -> NiblePath { - NiblePath::root(0x2) - .child(0x5) - .child(0xA) - .child(0x3) - .child(0x7) - } - - fn sample() -> NodeGuid { - NodeGuid::new(0x07, 0xABCD, 0x03, deep_path(), 0x2F_1A3, 0x12_3456) - } - - #[test] - fn fields_round_trip() { - let g = sample(); - assert_eq!(g.namespace(), 0x07); - assert_eq!( - g.entity_type(), - 0xABCD, - "entity_type is EXACT (no truncation)" - ); - assert_eq!(g.kind(), 0x03); - assert_eq!(g.shape_hash(), 0x2F_1A3 & SHAPE_HASH_MASK); - assert_eq!(g.local(), 0x12_3456 & LOCAL_MASK); - assert_eq!(g.layout_version(), IDENTITY_LAYOUT_VERSION); - } - - #[test] - fn uuid_v8_version_and_variant_are_reserved() { - let g = sample(); - assert_eq!(g.version(), 8, "UUIDv8"); - assert_eq!(g.variant(), 0b10, "RFC 9562 variant"); - assert!(g.is_valid_uuid_v8()); - // The reserved bits sit in octets 6 (hi nibble) and 8 (hi 2 bits). - let b = g.as_bytes(); - assert_eq!(b[6] >> 4, 8); - assert_eq!(b[8] >> 6, 0b10); - } - - #[test] - fn shape_hash_and_local_saturate_to_field_width() { - // Over-wide inputs are masked, never overflow into neighbours. - let g = NodeGuid::new(0, 0, 0, NiblePath::EMPTY, u32::MAX, u32::MAX); - assert_eq!(g.shape_hash(), SHAPE_HASH_MASK); - assert_eq!(g.local(), LOCAL_MASK); - // The version/variant survived the over-wide shape_hash. - assert!(g.is_valid_uuid_v8()); - } - - #[test] - fn field_isolation_each_field_is_independent() { - // The I-LEGACY-API field-isolation matrix: vary one field, assert all - // OTHERS are unchanged (and the UUIDv8 reserved bits never move). - let base = sample(); - let probes = [ - NodeGuid::new(0xFF, 0xABCD, 0x03, deep_path(), 0x2F_1A3, 0x12_3456), // namespace - NodeGuid::new(0x07, 0x0000, 0x03, deep_path(), 0x2F_1A3, 0x12_3456), // entity_type - NodeGuid::new(0x07, 0xABCD, 0xFF, deep_path(), 0x2F_1A3, 0x12_3456), // kind - NodeGuid::new(0x07, 0xABCD, 0x03, deep_path(), 0x00000, 0x12_3456), // shape_hash - NodeGuid::new(0x07, 0xABCD, 0x03, deep_path(), 0x2F_1A3, 0x000000), // local - ]; - for p in probes { - assert!(p.is_valid_uuid_v8(), "reserved version/variant bits intact"); - assert_eq!(p.layout_version(), IDENTITY_LAYOUT_VERSION); - } - // namespace probe: only namespace differs. - let p = probes[0]; - assert_ne!(p.namespace(), base.namespace()); - assert_eq!(p.entity_type(), base.entity_type()); - assert_eq!(p.kind(), base.kind()); - assert_eq!(p.shape_hash(), base.shape_hash()); - assert_eq!(p.local(), base.local()); - // entity_type probe: only entity_type differs. - let p = probes[1]; - assert_ne!(p.entity_type(), base.entity_type()); - assert_eq!(p.namespace(), base.namespace()); - assert_eq!(p.local(), base.local()); - assert_eq!(p.shape_hash(), base.shape_hash()); - } - - #[test] - fn niblepath_prefix_is_a_true_ancestor_of_the_full_path() { - // The routing cache is coarse but SOUND: the stored prefix is an - // ancestor-or-equal of the full path (so is_ancestor_of routing is valid). - let full = deep_path(); // depth 5 - let g = NodeGuid::new(0, 0, 0, full, 0, 0); - assert_eq!( - g.niblepath_depth(), - NodeGuid::PREFIX_NIBBLES, - "truncated to 4" - ); - let prefix = g.niblepath().expect("prefix reconstructs"); - assert!( - prefix.is_ancestor_of(full), - "the GUID prefix must be an ancestor of the full path" - ); - assert_eq!(prefix.basin(), full.basin(), "same DOLCE basin"); - } - - #[test] - fn shallow_path_is_carried_whole() { - // A path shallower than the prefix budget round-trips exactly. - let shallow = NiblePath::root(0x1).child(0x2); // depth 2 - let g = NodeGuid::new(0, 0, 0, shallow, 0, 0); - assert_eq!(g.niblepath_depth(), 2); - assert_eq!(g.niblepath(), Some(shallow)); - } - - #[test] - fn empty_path_is_the_no_route_sentinel() { - let g = NodeGuid::new(0, 0, 0, NiblePath::EMPTY, 0, 0); - assert_eq!(g.niblepath_depth(), 0); - assert_eq!(g.niblepath(), Some(NiblePath::EMPTY)); - } - - #[test] - fn as_bytes_from_bytes_round_trip() { - let g = sample(); - let g2 = NodeGuid::from_bytes(*g.as_bytes()); - assert_eq!(g, g2); - } - - #[test] - fn display_is_a_canonical_uuid_string() { - let g = sample(); - let s = g.to_string(); - assert_eq!(s.len(), 36, "8-4-4-4-12 + 4 hyphens"); - // Canonical UUID hyphen positions. - for i in [8usize, 13, 18, 23] { - assert_eq!(s.as_bytes()[i], b'-', "hyphen at {i}"); - } - // Version nibble is the 13th hex digit (group 3, first char) = '8'. - assert_eq!(s.chars().nth(14).unwrap(), '8'); - // entity_type 0xABCD is octets 1-2 → hex digits 2..6 = "abcd". - assert_eq!(&s[2..6], "abcd"); - // namespace 0x07 → digits 0..2, kind 0x03 → digits 6..8. - assert_eq!(&s[0..2], "07"); - assert_eq!(&s[6..8], "03"); - } - - #[test] - fn size_and_alignment_are_16() { - assert_eq!(core::mem::size_of::(), 16); - assert_eq!(core::mem::align_of::(), 16); - } -} diff --git a/crates/lance-graph-contract/src/lib.rs b/crates/lance-graph-contract/src/lib.rs index 0cd1ffde..8f333ed0 100644 --- a/crates/lance-graph-contract/src/lib.rs +++ b/crates/lance-graph-contract/src/lib.rs @@ -42,6 +42,7 @@ pub mod atoms; pub mod auth; pub mod callcenter; pub mod cam; +pub mod canonical_node; pub mod class_view; pub mod codegen_spine; pub mod cognitive_shader; @@ -62,7 +63,6 @@ pub mod hash; pub mod head2head; pub mod hhtl; pub mod high_heel; -pub mod identity; pub mod jit; pub mod kanban; pub mod literal_graph; @@ -106,11 +106,11 @@ pub mod world_map; pub mod world_model; // Re-exports for the most commonly used collapse_gate types. +pub use canonical_node::{EdgeBlock, NodeGuid, NodeRow}; pub use class_view::{ClassId, ClassProjection, ClassView, FieldMask, RenderRow}; pub use collapse_gate::{GateDecision, MailboxId, MergeMode}; pub use episodic_edges::{EdgeRef, EpisodicEdges64}; pub use head2head::{CompetitionOutcome, Head2Head, WinnerCriterion}; -pub use identity::{NodeGuid, IDENTITY_LAYOUT_VERSION}; pub use kanban::{ExecTarget, KanbanColumn, KanbanMove, RubiconTransitionError}; pub use scheduler::{DatasetVersion, NextPhaseScheduler, VersionScheduler}; pub use soa_view::{MailboxSoaOwner, MailboxSoaView}; diff --git a/crates/lance-graph-ontology/src/registry.rs b/crates/lance-graph-ontology/src/registry.rs index 7de85b88..5ebc64a0 100644 --- a/crates/lance-graph-ontology/src/registry.rs +++ b/crates/lance-graph-ontology/src/registry.rs @@ -401,8 +401,11 @@ impl OntologyRegistry { } } - /// The bijective derived view: `entity_type → NiblePath` - /// (`niblepath_of(entity_type)` in the identity plan / `NodeGuid` docs). + /// The bijective derived view: `entity_type → NiblePath`. + /// The exact class identity is `entity_type:u16`; the path is the canonical + /// HHTL tree address (the canon's `classid·HEEL·HIP·TWIG` prefix is resolved + /// from the class registry, not stored as a key field beyond what the + /// `NodeGuid` carries). pub fn niblepath_of(&self, entity_type_id: u16) -> Option { self.inner .read()