feat(contract): NodeGuid — structured 128-bit instance identity (identity-architecture Phase A)#480
Conversation
Express the cold-path write (mailbox SoA -> SPO nodes + CausalEdge64 edges) as a codegen_spine::TripletProjection, so roundtrip_eq becomes the commit's own gate: every commit is a substrate proof. Separates the three layers (exact-LE members / container envelope / lossy codec) onto two witnesses (roundtrip_eq vs rank-correlation), and decouples substrate hardening from the [ABSENT] KausalSpec DO-enforcement runtime. Grounded against: codegen_spine.rs, soa_envelope.rs (zero impls today), soa_view.rs (MailboxSoaView/Owner), lance_membrane.rs (sole-writer commit_event + CommitFilter/MembraneGate), ndarray causal_diff.rs (NARS 10b x1023 -> truth_tolerance 1/1023). Phase plan P0-P4 unblocked; P5 (SurrealQL read glove) BLOCKED(C) on surrealdb fork coords. https://claude.ai/code/session_014A4JuRCqKP2yNENrQ9Ha7H
Grounded map of the structured 128-bit identity (UUIDv8 = HHTL nibble-address formalized) against the existing substrate, from first-hand reads + two cross-repo sweeps. Four findings: (1) the 128-bit identity space is empty (no committed u128/Uuid/[u8;16]-as-id); (2) every GUID FIELD already exists as a committed scalar -> compose, do not re-invent (SchemaPtr + NiblePath + StructuralSignature + EdgeRef); (3) the cross-store transport is already solved by EntityKey(&[u8]) -- smb-bridge key_to_filter already length-branches; (4) the cold path has no stable structured identity today (node_id:u32 + String props, SpoStore u64 dn_hash) -- the identity fills a real gap. 6-layer exists inventory + 7 build gaps (N1-N7 unblocked, N8 surreal BLOCKED on fork coords) + phased plan A-H. Substrate is ~80% present; the work is composition/wiring, not green-field. One open decision: SchemaPtr.entity_type vs NiblePath-prefix as the class carrier. https://claude.ai/code/session_014A4JuRCqKP2yNENrQ9Ha7H
…ENTITY-1) The workspace's first stable binary instance identity: a UUIDv8 (RFC 9562) that formalizes + namespaces the HHTL nibble-address. Composed entirely from existing committed scalars (the 128-bit id space was empty per the Agent A sweep): SchemaPtr.packed convention (ns8|entity_type16|kind8) + a truncated NiblePath routing prefix + a 22-bit shape_hash + a 24-bit local, with the UUIDv8 version/variant at their RFC-fixed positions and a layout-version stamp. Eineindeutigkeit (ratified): entity_type:u16 is the canonical exact class identity; the NiblePath prefix is the bijective DERIVED view (a truncated prefix can't be the identity -- deep classes collide past it; the prefix is_ancestor_of the full path, tested). Five readings: resolve / route / witness / ground-truth / dispatch-to-store (as_bytes -> EntityKey). - new: identity.rs (NodeGuid + 15 tests: field-isolation matrix, UUIDv8 version/variant gates, ancestor-prefix invariant, canonical-UUID Display). - new: NiblePath::from_packed (inverse of packed()). - 599 contract lib tests (+15), clippy -D warnings clean, fmt clean. - board: LATEST_STATE inventory, EPIPHANIES E-IDENTITY-WHITEBOX-1, AGENT_LOG, decision-1 RESOLVED in the exists-vs-needs plan. https://claude.ai/code/session_014A4JuRCqKP2yNENrQ9Ha7H
📝 WalkthroughWalkthroughPhase A of the identity architecture is now shipped: a 128-bit structured ChangesIdentity Architecture & NodeGuid Feature
Code Formatting & Hygiene Refactoring
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.claude/board/AGENT_LOG.md:
- Around line 1-8: Update the new AGENT_LOG.md entry for "2026-06-09 —
D-IDENTITY-1 (Phase A) + 2 cross-repo sweeps — identity-architecture" to include
the commit SHA used for this run; locate the header line and append the short or
full commit identifier after the date or outcome (so the entry contains D-ids,
commit, tests, outcome as required), ensuring the commit SHA is recorded in the
same sentence that describes the orchestration/outcome for traceability.
In @.claude/board/LATEST_STATE.md:
- Line 58: A blank line inside a blockquote is triggering MD028; fix the
blockquote continuity in .claude/board/LATEST_STATE.md by removing the empty
line inside the quoted block or ensuring the separator line also starts with '>'
so the quote remains continuous; locate the broken blockquote (the paragraph
break around the "Proposed fix" area) and either delete the blank line or prefix
it with '>' to preserve a single continuous blockquote.
In @.claude/plans/cognitive-write-roundtrip-substrate-v1.md:
- Around line 44-58: The Markdown has unlabeled fenced code blocks containing
the ASCII diagram (starting with "WRITE (project = encode) ... roundtrip_eq:
in(s,p,o) == out(s,p,o) ... PASS ⟹ substrate sound") which triggers MD040;
update each fenced block to include an explicit language identifier (e.g.,
change ``` to ```text) so markdownlint accepts them and rendering is consistent;
locate the two unlabeled blocks around the ASCII diagram and the later block
(the one that spans the "roundtrip_eq" section) and add the same language tag to
each.
In @.claude/plans/identity-architecture-exists-vs-needs-v1.md:
- Around line 193-195: Remove the stale "One open [DECISION]: D1 vs D2
(SchemaPtr-entity_type vs NiblePath-prefix as the class carrier) —
recommendation: both (exact + routing prefix)." statement; locate the exact
sentence that begins "One open [DECISION]: D1 vs D2" and delete it or replace it
with a short canonical status line indicating the decision is resolved/landed in
Phase A to match the resolution recorded earlier (the paragraph that marks the
decision as resolved and landed). Ensure only the resolved status remains to
avoid conflicting plan state.
- Around line 50-105: The markdown has multiple tables that lack surrounding
blank lines causing MD058; for each table (the initial Types table under the top
section, and the Layer 1 through Layer 6 tables labeled "Layer 1 — edge /
handoff carriers", "Layer 2 — cold-path stores", "Layer 3 — resolution", "Layer
4 — commit + witness", "Layer 5 — cross-store transport", and "Layer 6 —
round-trip / substrate-hardening") add one blank line immediately before the
table header line (the | Type | ... |) and one blank line immediately after the
table end (after the last |---| row/data row block) so each table is separated
from surrounding text; update the blocks containing symbols like NiblePath,
SchemaPtr, EpisodicEdges64, MetadataStore, RegistryClassView, SoaEnvelope,
EntityKey, and TripletProjection to follow this spacing convention.
In `@crates/lance-graph-contract/src/hhtl.rs`:
- Around line 191-211: Add focused #[cfg(test)] unit tests next to the
NiblePath::from_packed implementation to cover its rejection and sentinel paths:
assert None is returned when depth > MAX_DEPTH, assert None is returned when
path has non-zero bits above 4*depth (e.g., pack a value with a high nibble set
while depth is smaller), and assert that from_packed(0, 0) returns
Some(NiblePath::EMPTY) (or equal to NiblePath { path:0, depth:0 }). Place tests
in a new mod tests block in the same file and reference the public constructor
NiblePath::from_packed, the MAX_DEPTH constant, and EMPTY sentinel to validate
behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 3acaa399-3998-4a9b-919b-3b61a47af0c4
📒 Files selected for processing (29)
.claude/board/AGENT_LOG.md.claude/board/EPIPHANIES.md.claude/board/LATEST_STATE.md.claude/plans/cognitive-write-roundtrip-substrate-v1.md.claude/plans/identity-architecture-exists-vs-needs-v1.mdcrates/lance-graph-contract/examples/cognitive_cycle.rscrates/lance-graph-contract/examples/savant_dispatch.rscrates/lance-graph-contract/src/atoms.rscrates/lance-graph-contract/src/callcenter/mod.rscrates/lance-graph-contract/src/callcenter/ogit_uris.rscrates/lance-graph-contract/src/callcenter/role_keys.rscrates/lance-graph-contract/src/cognition/advance.rscrates/lance-graph-contract/src/counterfactual.rscrates/lance-graph-contract/src/episodic_edges.rscrates/lance-graph-contract/src/escalation.rscrates/lance-graph-contract/src/head2head.rscrates/lance-graph-contract/src/hhtl.rscrates/lance-graph-contract/src/identity.rscrates/lance-graph-contract/src/lib.rscrates/lance-graph-contract/src/mul.rscrates/lance-graph-contract/src/nars.rscrates/lance-graph-contract/src/pearl_junction.rscrates/lance-graph-contract/src/plan.rscrates/lance-graph-contract/src/recipes.rscrates/lance-graph-contract/src/savants.rscrates/lance-graph-contract/src/scheduler.rscrates/lance-graph-contract/src/soa_envelope.rscrates/lance-graph-contract/src/transaction/ctx.rscrates/lance-graph-contract/src/witness_table.rs
| ## 2026-06-09 — D-IDENTITY-1 (Phase A) + 2 cross-repo sweeps — identity-architecture | ||
|
|
||
| **Orchestrator:** Opus main thread (autoattended). **Outcome:** Shipped Phase A. | ||
| - **Sweep A** (Opus general-purpose): lance-graph + ndarray identity-type inventory → the 128-bit identity space is EMPTY (only `[u8;16]` is `atoms::I4x32`, a style vector); every GUID field already exists as a committed scalar → compose-don't-reinvent. | ||
| - **Sweep B** (Opus general-purpose): MedCare-rs + smb-office-rs store keys → `EntityKey(&[u8])` already carries any-length keys (smb-bridge `key_to_filter` length-branches on Mongo+Lance); transport solved. MedCare needs one `external_ref` (or reuse DMS `sha256`); smb maps directly. | ||
| - **Phase A:** `lance_graph_contract::identity::NodeGuid` (UUIDv8, composed from SchemaPtr⊕NiblePath⊕StructuralSignature⊕local) + `NiblePath::from_packed`. 599 contract lib tests (+15), clippy `-D` clean, fmt clean. | ||
|
|
||
| Plans: `identity-architecture-exists-vs-needs-v1.md`, `cognitive-write-roundtrip-substrate-v1.md`. Epiphany: E-IDENTITY-WHITEBOX-1. |
There was a problem hiding this comment.
Add the commit SHA to the new AGENT_LOG entry for traceability.
The new run entry captures D-id/tests/outcome, but it should also include the commit identifier so the shipped scope is audit-linkable from this log entry.
As per coding guidelines, “Every agent run gets one append-only entry in AGENT_LOG.md (D-ids, commit, tests, outcome).”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.claude/board/AGENT_LOG.md around lines 1 - 8, Update the new AGENT_LOG.md
entry for "2026-06-09 — D-IDENTITY-1 (Phase A) + 2 cross-repo sweeps —
identity-architecture" to include the commit SHA used for this run; locate the
header line and append the short or full commit identifier after the date or
outcome (so the entry contains D-ids, commit, tests, outcome as required),
ensuring the commit SHA is recorded in the same sentence that describes the
orchestration/outcome for traceability.
Source: Coding guidelines
| ## Current Contract Inventory (lance-graph-contract) | ||
|
|
||
| > **2026-06-09 — ADDED (D-IDENTITY-1, Phase A of identity-architecture)**: `lance_graph_contract::identity::{NodeGuid([u8;16]), IDENTITY_LAYOUT_VERSION}` — the workspace's first **stable binary instance identity**: a structured 128-bit UUIDv8 (RFC 9562) = the HHTL nibble-address **formalized + namespaced**. **Composed from existing committed scalars, never re-invented** (Agent A sweep confirmed the 128-bit id space was empty): octets carry `namespace:u8 | entity_type:u16 | kind:u8` (the `SchemaPtr.packed` convention) ⊕ a truncated `NiblePath` routing prefix (`PREFIX_NIBBLES=4`) ⊕ a 22-bit `shape_hash` (truncated `StructuralSignature`) ⊕ a 24-bit `local`, with UUIDv8 version(=8)/variant(=0b10) at their RFC-fixed positions + an `IDENTITY_LAYOUT_VERSION` stamp. **Eineindeutigkeit**: `entity_type` is the canonical exact class identity; the `NiblePath` prefix is the bijective DERIVED view (a *truncated* prefix can't be the identity — deep classes collide past it; the prefix `is_ancestor_of` the full path). Five readings: resolve (`entity_type`) / route (`niblepath`) / witness (frozen bytes + merkle) / ground-truth (`shape_hash` drift) / dispatch-to-store (`as_bytes` → `EntityKey`). Also added `hhtl::NiblePath::from_packed` (inverse of `packed`). Zero-dep; 599 contract lib tests (+15: field-isolation matrix, UUIDv8 gates, ancestor-prefix invariant, Display=canonical-UUID); clippy `-D warnings` clean; fmt clean. Plans: `identity-architecture-exists-vs-needs-v1.md` (exists-vs-needs map + phases A→H), `cognitive-write-roundtrip-substrate-v1.md`. Epiphany: E-IDENTITY-WHITEBOX-1. | ||
|
|
There was a problem hiding this comment.
Remove the blank blockquote break that triggers MD028.
Line 58 is a blank line inside a blockquote (MD028). Keep the quote block continuous (or use > on the separator line) to avoid markdown lint warnings.
Proposed fix
-
+>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| > |
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 58-58: Blank line inside blockquote
(MD028, no-blanks-blockquote)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.claude/board/LATEST_STATE.md at line 58, A blank line inside a blockquote
is triggering MD028; fix the blockquote continuity in
.claude/board/LATEST_STATE.md by removing the empty line inside the quoted block
or ensuring the separator line also starts with '>' so the quote remains
continuous; locate the broken blockquote (the paragraph break around the
"Proposed fix" area) and either delete the blank line or prefix it with '>' to
preserve a single continuous blockquote.
Source: Linters/SAST tools
| ``` | ||
| WRITE (project = encode) READ-BACK (decompile = decode) | ||
| Vec<Triple> Vec<Triple> | ||
| │ intern (s,p,o) → ids (dict) ▲ dict reverse: ids → (s,p,o) | ||
| │ quantize (f,c) → NARS bits │ dequantize NARS → (f,c) | ||
| ▼ │ | ||
| CausalEdge64 u64 + class_id u16 ──────────┘ | ||
| │ lay out via MAILBOX_COLUMNS | ||
| ▼ | ||
| SoaEnvelope LE bytes ── commit_event (sole-writer, tick version) ──► Lance | ||
|
|
||
| roundtrip_eq: in(s,p,o) == out(s,p,o) [exact, truth_tolerance ignored for identity] | ||
| in(f,c) ≈ out(f,c) [tol = 1/1023, the CausalEdge64 NARS grid] | ||
| PASS ⟹ substrate sound for this cycle | FAIL ⟹ the brittle contract, NAMED | ||
| ``` |
There was a problem hiding this comment.
Add language identifiers to fenced code blocks (MD040).
Lines 44 and 90 use unlabeled fenced blocks; add explicit language tags (for example text) to satisfy markdownlint and improve rendering consistency.
Also applies to: 90-97
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 44-44: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.claude/plans/cognitive-write-roundtrip-substrate-v1.md around lines 44 -
58, The Markdown has unlabeled fenced code blocks containing the ASCII diagram
(starting with "WRITE (project = encode) ... roundtrip_eq: in(s,p,o) ==
out(s,p,o) ... PASS ⟹ substrate sound") which triggers MD040; update each fenced
block to include an explicit language identifier (e.g., change ``` to ```text)
so markdownlint accepts them and rendering is consistent; locate the two
unlabeled blocks around the ASCII diagram and the later block (the one that
spans the "roundtrip_eq" section) and add the same language tag to each.
Source: Linters/SAST tools
| | Type | Width | Role | Status | Evidence | | ||
| |---|---|---|---|---| | ||
| | `NiblePath{path:u64,depth:u8}` | 72 | HHTL tree address (basin/child/is_ancestor_of, 16ⁿ) | **[G]** | hhtl.rs | | ||
| | `SchemaPtr{packed:u32=[ns:8\|entity_type:16\|kind:8], ctx:u32}` | 64 | schema/type pointer | **[G]** | namespace.rs:119 | | ||
| | `NamespaceId(u8)` | 8 | OGIT namespace ordinal | **[G]** | namespace.rs:24 | | ||
| | `ClassId = u16` | 16 | per-row shape discriminator ("never a content hash") | **[G]** | class_view.rs:53 | | ||
| | `EntityTypeId = u16` | 16 | per-row object-type (Palantir) | **[G]** | ontology.rs:81 | | ||
| | `FieldMask(u64)` + `inherit` | 64 | presence bitmask, parent-OR-delta | **[G]** | class_view.rs:69,136 | | ||
| | `StructuralSignature` (shape_hash) | hash | "deterministic hash over property-id set" | **[G] type / [H] live-wire** | odoo_blueprint::class_signature | | ||
| | `EdgeRef{family:u8,local:u16}` | 24 | episodic HHTL family+local address | **[G]** | episodic_edges.rs:34 | | ||
|
|
||
| ### Layer 1 — edge / handoff carriers (the LE "sound members") | ||
| | Type | Width | Role | Status | | ||
| |---|---|---|---| | ||
| | `EpisodicEdges64(u64)` = 4×EdgeRef, MRU promote/evict, `to_le_bytes` | 64 | AriGraph episodic edges | **[G]** episodic_edges.rs | | ||
| | `CausalEdge64(u64)` (NARS 10+10 ×1023) | 64 | baton/causal edge payload | **[G]** ndarray causal_diff.rs:153 | | ||
| | Baton `(target:u16, edge:u64)` | 80 | inter-mailbox handoff | **[G]** collapse_gate.rs:235 | | ||
| | `MailboxId=u32`, `MailboxRow{mailbox_ref:u32,row_idx:u32}` | 32/64 | mailbox + row address | **[G]** | | ||
|
|
||
| ### Layer 2 — cold-path stores (TODAY: thin + inconsistent) | ||
| | Store | Key | Status | | ||
| |---|---|---| | ||
| | `MetadataStore`: `NodeRecord{node_id:u32, label:String, properties:HashMap<String,String>}`, `EdgeRecord{source:u32,target:u32,edge_type:String}` | u32 + **STRING label/props (legacy Cypher)** | **[G]** metadata.rs:60,86 | | ||
| | `SpoStore`: `HashMap<u64 dn_hash, SpoRecord>` | u64 **content-hash** (not stable id) | **[G]** spo/store.rs:38 | | ||
| | ndarray `CogRecord{meta,cam,btree,embed}` | **no id** ("id is external dn_hash") | **[G]** cogrecord.rs:56 | | ||
| | `WitnessId(u64)` (arigraph witness) | 64 opaque handle | **[G]** witness_corpus.rs:63 | | ||
|
|
||
| ### Layer 3 — resolution (class-from-address) | ||
| | Surface | Status | | ||
| |---|---| | ||
| | `RegistryClassView: ClassView` (fields/template/dolce_category_id) | **[G] resolve / [H] field-enum deferred** class_resolver.rs | | ||
| | `OntologyRegistry`: `resolve_uri`, `enumerate_first_with_entity_type_id(u16)`, `resolve_iri_in` | **[G]** registry.rs | | ||
|
|
||
| ### Layer 4 — commit + witness (the membrane) | ||
| | Surface | Status | | ||
| |---|---| | ||
| | `SoaEnvelope` trait + `ColumnDescriptor` (container-LE geometry) | **[G] trait / [H] ZERO impls** soa_envelope.rs | | ||
| | `MailboxSoaView`/`MailboxSoaOwner` (read airgap + Rubicon `try_advance_phase`) | **[G]** soa_view.rs | | ||
| | `commit_event` sole-writer + `ExternalMembrane::project` + `CommitFilter`/`MembraneGate` | **[G]** lance_membrane.rs:315 | | ||
| | `CognitiveEventRow` (scalar audit event — VSA stripped) | **[G]** external_intent.rs:113 | | ||
| | `MerkleRoot(u64)` ×3 (audit/SPO/unified) + `AuditSink` (jsonl/lance) | **[G]** audit_sink/, merkle.rs | | ||
| | `SlaPolicy`, `TenantScope` | **[G] types** sla.rs | | ||
|
|
||
| ### Layer 5 — cross-store transport (the consumer boundary) | ||
| | Surface | Status | | ||
| |---|---| | ||
| | `EntityKey<'a>(pub &'a [u8])` — opaque length-agnostic key | **[G]** repository.rs:12 | | ||
| | `EntityStore`/`EntityWriter`/`Batch` traits | **[G]** repository.rs | | ||
| | `smb-bridge`: implements both for Mongo+Lance, `key_to_filter` length-branch | **[G]** smb-bridge/mongo.rs:79, lance.rs:92 | | ||
| | MedCare-rs: MySQL i64 PKs; DMS `sha256`(NOT NULL)+`storage_key`; imports EntityKey | **[G]** dms.rs:14, graph_contract.rs:31 | | ||
| | smb-office-rs: Mongo `ObjectId`(12B) + `String` refs; actively impls repository | **[G]** base.rs:92 | | ||
|
|
||
| ### Layer 6 — round-trip / substrate-hardening | ||
| | Surface | Status | | ||
| |---|---| | ||
| | `TripletProjection` trait + `roundtrip_eq` → `RoundTripFailure` | **[G]** codegen_spine.rs:107 | |
There was a problem hiding this comment.
Fix markdown table spacing to clear MD058 warnings.
Lines 50, 62, 70, 78, 84, 94, and 103 need blank lines around tables per markdownlint (MD058).
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 50-50: Tables should be surrounded by blank lines
(MD058, blanks-around-tables)
[warning] 62-62: Tables should be surrounded by blank lines
(MD058, blanks-around-tables)
[warning] 70-70: Tables should be surrounded by blank lines
(MD058, blanks-around-tables)
[warning] 78-78: Tables should be surrounded by blank lines
(MD058, blanks-around-tables)
[warning] 84-84: Tables should be surrounded by blank lines
(MD058, blanks-around-tables)
[warning] 94-94: Tables should be surrounded by blank lines
(MD058, blanks-around-tables)
[warning] 103-103: Tables should be surrounded by blank lines
(MD058, blanks-around-tables)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.claude/plans/identity-architecture-exists-vs-needs-v1.md around lines 50 -
105, The markdown has multiple tables that lack surrounding blank lines causing
MD058; for each table (the initial Types table under the top section, and the
Layer 1 through Layer 6 tables labeled "Layer 1 — edge / handoff carriers",
"Layer 2 — cold-path stores", "Layer 3 — resolution", "Layer 4 — commit +
witness", "Layer 5 — cross-store transport", and "Layer 6 — round-trip /
substrate-hardening") add one blank line immediately before the table header
line (the | Type | ... |) and one blank line immediately after the table end
(after the last |---| row/data row block) so each table is separated from
surrounding text; update the blocks containing symbols like NiblePath,
SchemaPtr, EpisodicEdges64, MetadataStore, RegistryClassView, SoaEnvelope,
EntityKey, and TripletProjection to follow this spacing convention.
Source: Linters/SAST tools
| - **One open [DECISION]:** D1 vs D2 (SchemaPtr-entity_type vs NiblePath-prefix as | ||
| the class carrier) — recommendation: both (exact + routing prefix). | ||
|
|
There was a problem hiding this comment.
Remove the stale “open decision” statement to avoid plan-state conflict.
Line 193 says there is “One open [DECISION]”, but Lines 158-165 already mark that decision as resolved and landed in Phase A. Keep one canonical status to prevent downstream planning drift.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.claude/plans/identity-architecture-exists-vs-needs-v1.md around lines 193 -
195, Remove the stale "One open [DECISION]: D1 vs D2 (SchemaPtr-entity_type vs
NiblePath-prefix as the class carrier) — recommendation: both (exact + routing
prefix)." statement; locate the exact sentence that begins "One open [DECISION]:
D1 vs D2" and delete it or replace it with a short canonical status line
indicating the decision is resolved/landed in Phase A to match the resolution
recorded earlier (the paragraph that marks the decision as resolved and landed).
Ensure only the resolved status remains to avoid conflicting plan state.
| /// 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. | ||
| /// | ||
| /// 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, | ||
| /// trailing high bits must be zero). `from_packed(0, 0)` is [`EMPTY`](NiblePath::EMPTY). | ||
| #[must_use] | ||
| pub const fn from_packed(path: u64, depth: u8) -> Option<Self> { | ||
| if depth > MAX_DEPTH { | ||
| return None; | ||
| } | ||
| // `path` must fit in `depth` nibbles (4·depth bits); higher bits must be 0. | ||
| // At MAX_DEPTH (16 nibbles = 64 bits) the whole u64 is usable — skip the | ||
| // shift (a `>> 64` would be UB). | ||
| let used_bits = 4 * depth as u32; | ||
| if used_bits < 64 && (path >> used_bits) != 0 { | ||
| return None; | ||
| } | ||
| Some(Self { path, depth }) | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
Add focused unit tests for from_packed edge cases.
Line 191 adds a new public constructor, but there are no direct tests here for its rejection paths (depth > MAX_DEPTH, non-zero high bits) and the (0, 0) sentinel mapping. Add focused unit tests in this module.
Proposed test additions
#[cfg(test)]
mod tests {
use super::*;
use crate::class_view::FieldMask;
+
+ #[test]
+ fn from_packed_validates_depth_and_high_bits() {
+ assert_eq!(NiblePath::from_packed(0, 0), Some(NiblePath::EMPTY));
+ assert_eq!(NiblePath::from_packed(0x12, 2), Some(NiblePath::root(0x1).child(0x2)));
+ assert_eq!(NiblePath::from_packed(0, MAX_DEPTH + 1), None);
+ // depth=2 => only low 8 bits are allowed; high bits must be zero.
+ assert_eq!(NiblePath::from_packed(0x1_12, 2), None);
+ }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@crates/lance-graph-contract/src/hhtl.rs` around lines 191 - 211, Add focused
#[cfg(test)] unit tests next to the NiblePath::from_packed implementation to
cover its rejection and sentinel paths: assert None is returned when depth >
MAX_DEPTH, assert None is returned when path has non-zero bits above 4*depth
(e.g., pack a value with a high nibble set while depth is smaller), and assert
that from_packed(0, 0) returns Some(NiblePath::EMPTY) (or equal to NiblePath {
path:0, depth:0 }). Place tests in a new mod tests block in the same file and
reference the public constructor NiblePath::from_packed, the MAX_DEPTH constant,
and EMPTY sentinel to validate behavior.
Source: Coding guidelines
Follow-up on merged PR #480 (D-IDENTITY-1 Phase A). Substantive: - hhtl: focused from_packed edge-case test — depth>MAX_DEPTH and high-bits-set rejection, (0,0)->EMPTY sentinel, the MAX_DEPTH boundary that exercises the used_bits<64 guard (avoids >>64 UB), and packed o from_packed identity. - plan: the stale "one open DECISION" line conflicted with the RESOLVED block above it -> now a RESOLVED status line (decision landed in NodeGuid). - plan: add the no-content-drift-for-existing invariant — an existing entity's identity is a derived function of its ontology-mapped class, so it cannot drift; the sole drift surface is an ontology cache not mapped from its authoritative source, which is exactly what the shape_hash witness guards. - AGENT_LOG: add the commit SHA to the D-IDENTITY-1 entry (traceability). Cosmetic (markdownlint, the two new plan docs): - MD040: language tag on the MAP1/MAP5 fenced diagrams. - MD058: blank lines around the Layer 0-6 inventory tables. - Skipped MD028 in LATEST_STATE: the blank line between board entries is the file's append-only style; "fixing" one entry diverges it from all priors. Tooling: - README: native GitHub Actions status badges (Rust Tests / Style Check / Build). Chose native badge URLs over the marketplace ci-badges action — zero-config, no workflow step or write-back token, no maintenance surface. 600 contract lib tests (+1); clippy -D warnings --all-targets clean; fmt clean. https://claude.ai/code/session_014A4JuRCqKP2yNENrQ9Ha7H
…supervision-edge = template specialization Cross-session convergence: a parallel OGAR/SurrealDB session pulled #480 and independently re-derived the OGAR<->lance-graph membrane as the registry mint of (entity_type, NiblePath) per class -- exactly DECISION-2 (OGAR mirror) from #481. New synthesis: NiblePath::is_ancestor_of (one HHTL bit-shift on the GUID prefix) is three relations at once -- OWL subClassOf, OTP supervision edge, and north-star template specialization. The template hierarchy IS the routing/supervision hierarchy IS the subclass hierarchy; no separate routing structure to maintain. https://claude.ai/code/session_014A4JuRCqKP2yNENrQ9Ha7H
chore(contract): address #480 CodeRabbit review + CI status badges
…allization) Crystallizes the operator-pinned canonical GUID (OGAR/CLAUDE.md) into lance-graph's policy-side contract before it dilutes: - The canon cited (hex dash-groups = classid-HEEL-HIP-TWIG-[basin+id]; key(128)+value(3968)=4096; 3x4 uniform, tier = nibble >> 2; RFC 9562 = wrapper concern; NodeGuid #480 audited against the canon group-by-group, never the reverse — Phase B question: groups 3-4 yield all eight nibbles to HIP/TWIG). - Policy ownership table: registry mint (Phase B), per-class 4^4-hierarchical codebooks on the registry shelf (prefix-scoped, longest-prefix wins), PrefixShapeTable registration into ndarray's router, quorum certificate type landing in contract::quorum (the #411 scaffold), escalation via the shipped RouteAction precedent. - Anti-eigenvalue-theater rules contract-side: quorum certificate with measured-tau (I-NOISE-FLOOR-JIRAK; Pflug-10 anchors 0.9973/0.965), named typed metrics (no-umbrella; raw-XOR-u64 ordering is the named anti-pattern), escalate-never-silently-accept, ShapeId is a register key (I-VSA-IDENTITIES Test 0). - Probes shared with the ndarray counterpart: ROUTE-1, QUORUM-1, PHI-1, PYR-1, CODEBOOK-44, HILBERT-L4 (blocker). Board hygiene: EPIPHANIES E-CANON-GUID-1 prepended in the same commit. Docs-only; no .rs touched. https://claude.ai/code/session_01PBTGaPCSnnt6u3pjXpbLwY
What
The workspace's first stable binary instance identity:
lance_graph_contract::identity::NodeGuid([u8; 16])— a structured UUIDv8 (RFC 9562) that formalizes + namespaces the HHTL nibble-address. This is Phase A (the keystone) of the identity architecture mapped in.claude/plans/identity-architecture-exists-vs-needs-v1.md.Composed from existing committed scalars — nothing re-invented. An up-front cross-repo sweep (lance-graph + ndarray) confirmed the 128-bit identity space was empty (the only
[u8; 16]in the contract wasatoms::I4x32, a style vector). So the GUID is assembled from fields that already exist:The eineindeutigkeit decision (decision-1, now RESOLVED in the plan)
The class is carried two ways, bound by an enforced bijection, decided pre-production so it's baked in with zero migration debt:
entity_type:u16is the canonical, exact class identity — fixed-width, no truncation.NiblePathis the bijective derived view — the GUID carries only a coarse 4-nibble prefix as a routing cache (niblepath_of(entity_type)truncated). A truncated prefix cannot be the identity (two deep classes collide past it), so the exact identity must be the dense scalar; the prefixis_ancestor_ofthe full path (tested).entity_typeIS the dense encoding of the address, bijective withNiblePath.Full enforcement of the bijection (registry mints the unique pair + a build-time round-trip test) is Phase B — this PR lands the byte-layout half (the GUID prefix-consistency invariant + field-isolation matrix).
In this PR
identity.rs(new) —NodeGuid+IDENTITY_LAYOUT_VERSION, 15 tests: field-isolation matrix (write each field, assert all others unchanged), UUIDv8 version/variant gates,prefix is_ancestor_of fullinvariant, canonical-UUIDDisplay.hhtl::NiblePath::from_packed— the inverse ofpacked(), needed to derive the routing prefix.identity-architecture-exists-vs-needs-v1.md(the exists-vs-needs map + phases A→H) andcognitive-write-roundtrip-substrate-v1.md.LATEST_STATE.mdinventory entry,EPIPHANIES.md→E-IDENTITY-WHITEBOX-1,AGENT_LOG.md, decision-1 marked RESOLVED in the plan.Gates
clippy -D warnings --all-targetscargo fmt --checkLocked / invariants
NodeGuidis defined but nothing constructs it in a persisted path yet (that's Phase B/C/F), so there is no migration risk; pre-production, baked in clean.Honest note for the reviewer
The feature commit incidentally swept ~20 contract files to fmt-clean.
cargo fmtreformats the whole crate, and the contract crate had pre-existing fmt drift (CI only fmt-gateslance-graph+deepnsm, never the zero-dep contract). Every large diff (mul.rs/recipes.rs/savants.rs) is pure rustfmt — line-wrapping previously-over-long single-line struct literals; verified viagit show -w, semantic-preserving, 599 tests green. It's benign and leaves the crate fmt-clean, but the diff is larger than the logical change. Happy to split intofeat+stylecommits if preferred.Not in scope (deferred to later phases)
B (registry bijection enforcement + build-time round-trip test) · C (
SoaEnvelopeMailboxSoA impl) · D–H (per the plan).NodeGuidis the substrate those phases build on.https://claude.ai/code/session_014A4JuRCqKP2yNENrQ9Ha7H
Generated by Claude Code
Summary by CodeRabbit
New Features
NodeGuidfor 128-bit UUIDv8-based node identity with composed fields (namespace, entity type, kind, path prefix, shape hash, local index).from_packedconstructor toNiblePathfor reconstructing paths from their packed representation.Documentation