Skip to content

Commit 947c1e4

Browse files
committed
feat(contract): NodeGuid — structured 128-bit identity (Phase A, D-IDENTITY-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
1 parent cbd99e4 commit 947c1e4

28 files changed

Lines changed: 1650 additions & 283 deletions

.claude/board/AGENT_LOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
## 2026-06-09 — D-IDENTITY-1 (Phase A) + 2 cross-repo sweeps — identity-architecture
2+
3+
**Orchestrator:** Opus main thread (autoattended). **Outcome:** Shipped Phase A.
4+
- **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.
5+
- **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.
6+
- **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.
7+
8+
Plans: `identity-architecture-exists-vs-needs-v1.md`, `cognitive-write-roundtrip-substrate-v1.md`. Epiphany: E-IDENTITY-WHITEBOX-1.
9+
110
## [Opus 4.8, main thread] cesium-osm-substrate-v1 review fix — D-OSM-2 crate boundary (codex P2 on merged #473)
211

312
**Branch:** `claude/osm-pbf-consumer-boundary-fix` (off `main`). **New follow-up PR** (merged #473 review-fix, surfaced for visibility per user request; the other session owns the OGAR-side fixes).

.claude/board/EPIPHANIES.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,39 @@
1+
## 2026-06-09 — E-IDENTITY-WHITEBOX-1 — structured identity + round-trip converts the substrate from black-box to CI-falsifiable
2+
3+
**Status:** FINDING (Phase A landed: `identity::NodeGuid` composed, 15 tests green)
4+
**Confidence:** High
5+
6+
**The synthesis:** a structured 128-bit immutable identity (UUIDv8 = the HHTL
7+
nibble-address formalized + namespaced) PLUS the `roundtrip_eq` guarantee turn the
8+
substrate from a black box into a CI-falsifiable surface. Two structural witnesses:
9+
the **bijection** (`entity_type ↔ NiblePath`, proven eineindeutig at build time)
10+
and **`roundtrip_eq`** (a lossy projection fails CI, not code review). The
11+
round-trip whitens the PLUMBING (identity / LE byte-contract / member-vs-container)
12+
— the darkest, most-expensive-bug layer; it does NOT whiten semantics (needs
13+
ground-truth corpora) or the lossy codec (needs ρ-certification). Those keep their
14+
own witnesses; the trade is "vigilance over the substrate" → "a test over the substrate".
15+
16+
**Compose, don't re-invent (Agent A sweep):** the 128-bit identity space is empty
17+
(no committed `u128`/`Uuid`/`[u8;16]`-as-id), but every GUID field already exists as
18+
a committed scalar — `SchemaPtr.packed` (ns8|entity_type16|kind8) ⊕ `NiblePath`
19+
prefix ⊕ truncated `StructuralSignature` ⊕ local. `NodeGuid` is their composition.
20+
21+
**Eineindeutigkeit (ratified):** `entity_type:u16` is the canonical exact class
22+
identity; `NiblePath` is the bijective DERIVED view (a *truncated* prefix can't be
23+
the identity — deep classes collide past it, `hhtl.rs`). The registry mints the
24+
pair 1:1; a build-time bijection round-trip proves it; the GUID prefix-consistency
25+
invariant (`prefix == niblepath_of(entity_type)[..N]`) catches drift.
26+
27+
**Free side-effects:** the structured GUID is also (a) a KV key via the existing
28+
`EntityKey(&[u8])` (smb-bridge already length-branches it), and (b) a **quadkey** —
29+
Lance ORDER BY the NiblePath gives subtree range-scans = zone-map-pruned byte
30+
ranges, no index (ADR-024 "HHTL prefix establishes a frame").
31+
32+
**Landed:** `lance_graph_contract::identity::NodeGuid` (D-IDENTITY-1 / Phase A) +
33+
`NiblePath::from_packed`. 599 contract lib tests (+15), clippy `-D` clean, fmt clean.
34+
Plans: `identity-architecture-exists-vs-needs-v1.md` (exists-vs-needs map),
35+
`cognitive-write-roundtrip-substrate-v1.md` (the round-trip mechanism).
36+
137
## 2026-06-06 — E-DEINTERLACE-TWO-SCALES — deinterlace is one operation at two scales; no-cross-cycle-lag = byte-scale deinterlace
238

339
**Status:** FINDING (source-grounded; `temporal.rs` PR #468 confirms row-scale; byte-scale is a documented gap)

.claude/board/LATEST_STATE.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@
5454

5555
## Current Contract Inventory (lance-graph-contract)
5656

57+
> **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.
58+
5759
> **2026-05-31 — ADDED (D-EW64-1 + D-VIEW-1, episodic-RISC-spine)**: `episodic_edges::{EpisodicEdges64(u64), EdgeRef{family:u8,local:u16}}` — AriGraph episodic edges, 4x[4-bit family | 12-bit local]: family 0 = intra-basin (inherited, ~98.6% per #444), 1..=15 = cross-family index into the OGIT-class-inherited palette (~1.4%; identities inherited, never on the edge — I-VSA-IDENTITIES). Plus `view_angle::ViewAngle` (4-bit view-schema selector; presence bitmask doubles as attention mask, inherited). Zero-dep; 527 contract lib tests; clippy pedantic+nursery clean. Plan: episodic-risc-spine-v1.md.
5860
5961
> **2026-05-31 — ADDED (D-H2H-1, head2head superposition winner-select)**: `lance_graph_contract::head2head::{Head2Head (judge), WinnerCriterion (DissonanceMin≈infight / SupportSpread≈Raumgewinn / ConfidenceMax / Tempered=default), CompetitionOutcome}`. `Head2Head::select(&Blackboard) -> Option<CompetitionOutcome>` picks the winning competing-expert bid over the existing `a2a_blackboard` (confidence/dissonance/support) — pure read + arg-extremum, **no new identity, copies nothing** (select-don't-duplicate, `I-VSA-IDENTITIES`); `margin` = the dark-horse signal. The *selection* half of head2head superposition; parallel-mailbox *execution* is the CI-gated consumer side. Zero-dep; 516 contract lib tests (+7); clippy pedantic+nursery clean.

.claude/plans/identity-architecture-exists-vs-needs-v1.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -155,11 +155,13 @@ lineage id, re-resolved from the address for free; the GUID is write-once; drift
155155
mutation. `I-VSA-IDENTITIES` Test 0: the GUID is a register key (points to
156156
content), never VSA-bundled.
157157

158-
### ⚠ One open DECISION (yours to pin — both grounded, bijective)
159-
The class can be carried two ways; pick the **stored** form, resolve the other:
160-
- **(D1) `SchemaPtr.entity_type:u16`** — reuse the existing dense pointer (Agent A "compose existing"). Compact, exact.
161-
- **(D2) `NiblePath` prefix** — identity-IS-address (ADR-1374, your "nibble = the GUID class"). O(1) ancestry-routing without a cache hit.
162-
- **Recommendation:** store **SchemaPtr (exact) + a truncated NiblePath prefix (for routing)** — SchemaPtr resolves deep paths exactly; the prefix gives branchless `is_ancestor_of`. Costs ~42 bits for the prefix; worth it for probe-free routing.
158+
### ✅ DECISION — RESOLVED (eineindeutig / bijective, ratified 2026-06-09; Phase A landed)
159+
**Carry BOTH, bound by an enforced bijection** (`entity_type ↔ NiblePath`), pre-production so it's baked in with zero migration debt:
160+
- **Canonical, exact, in the GUID:** `entity_type:u16` — fixed-width, no truncation. A *truncated* NiblePath prefix CANNOT be bijective (two distinct deep paths collide past the prefix, `hhtl.rs`), so the exact identity MUST be the dense `entity_type`.
161+
- **Derived view:** `NiblePath` — the bijective radix encoding of the SAME class; full depth resolves from the registry; the GUID carries a coarse prefix (`PREFIX_NIBBLES = 4`) as a *derived routing cache* = `niblepath_of(entity_type)` truncated (the prefix `is_ancestor_of` the full path — tested).
162+
- **identity-IS-address holds:** `entity_type` IS the dense encoding of the address; bijective with `NiblePath` (ADR-1374 satisfied).
163+
- **Eineindeutigkeit enforced 3 ways:** (a) the registry mints `(entity_type, NiblePath)` as a unique pair [Phase B]; (b) a build-time bijection round-trip test proves it [Phase B]; (c) the GUID prefix-consistency invariant. (c) + the byte-layout field-isolation matrix landed in Phase A (`identity.rs`, 15 tests); (a)/(b) are Phase B (ontology, needs the registry).
164+
- **Landed (Phase A, D-IDENTITY-1):** `lance_graph_contract::identity::NodeGuid` + `NiblePath::from_packed`. Byte layout, UUIDv8 version/variant gates, field-isolation matrix, `prefix is_ancestor_of full` invariant — all green (599 contract tests, +15; clippy -D clean).
163165

164166
## Phased integration plan (A→H; each phase = one landable PR)
165167

crates/lance-graph-contract/examples/cognitive_cycle.rs

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,13 @@ use lance_graph_contract::recipes::recipe_by_code;
2222
const DEEP_THINK: [&str; 8] = ["RTE", "HTD", "TCP", "CR", "CDT", "MCP", "TCF", "CUR"];
2323

2424
fn gate_state(sd: f32) -> GateState {
25-
if sd < SD_FLOW { GateState::Flow } else if sd <= 0.35 { GateState::Hold } else { GateState::Block }
25+
if sd < SD_FLOW {
26+
GateState::Flow
27+
} else if sd <= 0.35 {
28+
GateState::Hold
29+
} else {
30+
GateState::Block
31+
}
2632
}
2733

2834
fn main() {
@@ -35,14 +41,23 @@ fn main() {
3541
ctx.beliefs = vec![(7, 0.90, 0.8), (7, 0.10, 0.7)]; // topic 7 asserted true AND false
3642

3743
println!("== cognitive cycle: one Think, run to rest ==\n");
38-
println!("start: gate={:?} F={:.2} conf={:.2} candidates={} beliefs={}\n",
39-
gate_state(ctx.sd), ctx.free_energy, ctx.confidence, ctx.candidates.len(), ctx.beliefs.len());
44+
println!(
45+
"start: gate={:?} F={:.2} conf={:.2} candidates={} beliefs={}\n",
46+
gate_state(ctx.sd),
47+
ctx.free_energy,
48+
ctx.confidence,
49+
ctx.candidates.len(),
50+
ctx.beliefs.len()
51+
);
4052

4153
// The active-inference loop: keep thinking while there is surprise (gate != FLOW).
4254
let mut round = 0;
4355
while gate_state(ctx.sd) != GateState::Flow && round < 5 {
4456
round += 1;
45-
println!("── round {round} (gate {:?}) ───────────────────────", gate_state(ctx.sd));
57+
println!(
58+
"── round {round} (gate {:?}) ───────────────────────",
59+
gate_state(ctx.sd)
60+
);
4661

4762
// Pipe the context through the recipe. Each step is `ctx |> tactic`.
4863
for code in DEEP_THINK {
@@ -54,7 +69,11 @@ fn main() {
5469
code,
5570
format!("{:?}", rec.bucket),
5671
rec.name,
57-
if out.fired { out.note } else { "· gated off (FLOW)" },
72+
if out.fired {
73+
out.note
74+
} else {
75+
"· gated off (FLOW)"
76+
},
5877
out.delta_conf,
5978
);
6079
}
@@ -63,18 +82,35 @@ fn main() {
6382
// (In the wired system this falls out of the codec sweep; here we make it explicit.)
6483
ctx.sd *= 0.55;
6584
ctx.free_energy *= 0.5;
66-
println!(" → after round: gate={:?} SD={:.3} F={:.3} conf={:.2}\n",
67-
gate_state(ctx.sd), ctx.sd, ctx.free_energy, ctx.confidence);
85+
println!(
86+
" → after round: gate={:?} SD={:.3} F={:.3} conf={:.2}\n",
87+
gate_state(ctx.sd),
88+
ctx.sd,
89+
ctx.free_energy,
90+
ctx.confidence
91+
);
6892
}
6993

7094
if gate_state(ctx.sd) == GateState::Flow {
71-
println!("== rest == the shader stopped because gate reached Flow (SD={:.3} < FLOW {SD_FLOW}).", ctx.sd);
95+
println!(
96+
"== rest == the shader stopped because gate reached Flow (SD={:.3} < FLOW {SD_FLOW}).",
97+
ctx.sd
98+
);
7299
} else {
73-
println!("== rest == round cap reached ({round} rounds) before FLOW; gate={:?}, SD={:.3}.",
74-
gate_state(ctx.sd), ctx.sd);
100+
println!(
101+
"== rest == round cap reached ({round} rounds) before FLOW; gate={:?}, SD={:.3}.",
102+
gate_state(ctx.sd),
103+
ctx.sd
104+
);
75105
}
76-
println!("final: conf={:.2}, {} candidate(s) survived pruning, {} beliefs.",
77-
ctx.confidence, ctx.candidates.len(), ctx.beliefs.len());
106+
println!(
107+
"final: conf={:.2}, {} candidate(s) survived pruning, {} beliefs.",
108+
ctx.confidence,
109+
ctx.candidates.len(),
110+
ctx.beliefs.len()
111+
);
78112
println!("\nKey: Gate-bucket tactics (TCP/CDT/TCF/CUR) skip while in FLOW — the markers,");
79-
println!("not a scheduler, decide what fires. Same `Tactic` behaviour, 34 hot-swappable units.");
113+
println!(
114+
"not a scheduler, decide what fires. Same `Tactic` behaviour, 34 hot-swappable units."
115+
);
80116
}

crates/lance-graph-contract/examples/savant_dispatch.rs

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ struct Situation {
2626
/// Pattern-match the kind → the reasoning approach (the Elixir multi-clause `case`).
2727
fn approach(kind: ReasoningKind) -> &'static str {
2828
match kind {
29-
ReasoningKind::CustomerCategory => "classify against the family codebook (deductive lookup)",
29+
ReasoningKind::CustomerCategory => {
30+
"classify against the family codebook (deductive lookup)"
31+
}
3032
ReasoningKind::PostingAnomaly => "abduce the most likely cause from the evidence trail",
3133
ReasoningKind::NextBestAction => "induce the action with the highest expected value",
3234
ReasoningKind::InvoiceCompleteness => "check required-field coverage, score the gaps",
@@ -40,10 +42,21 @@ fn approach(kind: ReasoningKind) -> &'static str {
4042

4143
fn dispatch(s: &Savant) {
4244
println!(" savant {} (#{}, lane {})", s.name, s.id, s.lane);
43-
println!(" family {}", s.family.map(|f| format!("0x{f:02X}")).unwrap_or_else(|| "None (needs alignment axiom)".into()));
44-
println!(" tuple kind={:?} · infer={:?} · semiring={:?} · style={:?}", s.kind, s.inference, s.semiring, s.style);
45+
println!(
46+
" family {}",
47+
s.family
48+
.map(|f| format!("0x{f:02X}"))
49+
.unwrap_or_else(|| "None (needs alignment axiom)".into())
50+
);
51+
println!(
52+
" tuple kind={:?} · infer={:?} · semiring={:?} · style={:?}",
53+
s.kind, s.inference, s.semiring, s.style
54+
);
4555
// The InferenceType resolves O(1) to the runtime query strategy.
46-
println!(" → strategy {:?} (InferenceType::default_strategy)", s.query_strategy());
56+
println!(
57+
" → strategy {:?} (InferenceType::default_strategy)",
58+
s.query_strategy()
59+
);
4760
println!(" → approach {}", approach(s.kind));
4861
println!(" → output NARS (frequency, confidence) suggestion — woa-rs applies it behind its AXIS-A guard\n");
4962
}
@@ -52,11 +65,32 @@ fn main() {
5265
println!("== Odoo savant delegation: AXIS-A guard (woa-rs) → AXIS-B reason (lance-graph) ==\n");
5366

5467
let inbox = [
55-
Situation { headline: "€1,200 payment arrived — does it fully reconcile the partner's open invoices?", ambiguous: true, savant: "PaymentToInvoiceMatcher" },
56-
Situation { headline: "3rd identical bill from this vendor, unmodified — auto-post it?", ambiguous: true, savant: "AutopostRecommender" },
57-
Situation { headline: "new B2B partner in AT — which fiscal position (tax mapping)?", ambiguous: true, savant: "FiscalPositionResolver" },
58-
Situation { headline: "journal sequence jumps 1042 → 1044 — is 1043 a deleted posted entry?", ambiguous: true, savant: "SequenceGapAnomalyDetector" },
59-
Situation { headline: "invoice with a perfectly matching single open item", ambiguous: false, savant: "ReconcileMatchSelector" },
68+
Situation {
69+
headline:
70+
"€1,200 payment arrived — does it fully reconcile the partner's open invoices?",
71+
ambiguous: true,
72+
savant: "PaymentToInvoiceMatcher",
73+
},
74+
Situation {
75+
headline: "3rd identical bill from this vendor, unmodified — auto-post it?",
76+
ambiguous: true,
77+
savant: "AutopostRecommender",
78+
},
79+
Situation {
80+
headline: "new B2B partner in AT — which fiscal position (tax mapping)?",
81+
ambiguous: true,
82+
savant: "FiscalPositionResolver",
83+
},
84+
Situation {
85+
headline: "journal sequence jumps 1042 → 1044 — is 1043 a deleted posted entry?",
86+
ambiguous: true,
87+
savant: "SequenceGapAnomalyDetector",
88+
},
89+
Situation {
90+
headline: "invoice with a perfectly matching single open item",
91+
ambiguous: false,
92+
savant: "ReconcileMatchSelector",
93+
},
6094
];
6195

6296
for s in &inbox {
@@ -70,5 +104,7 @@ fn main() {
70104
}
71105

72106
println!("Same delegation tuple for all 25 savants; dispatch is data, not branches.");
73-
println!("Pattern-match on ReasoningKind picks the approach; the family's StyleCluster colours it.");
107+
println!(
108+
"Pattern-match on ReasoningKind picks the approach; the family's StyleCluster colours it."
109+
);
74110
}

crates/lance-graph-contract/src/atoms.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,10 @@ mod tests {
277277
fn catalogue_is_locked_33_in_order() {
278278
assert_eq!(CANONICAL_ATOMS.len(), 33);
279279
for (i, a) in CANONICAL_ATOMS.iter().enumerate() {
280-
assert_eq!(a.dim as usize, i, "lane dim must equal its index (locked order)");
280+
assert_eq!(
281+
a.dim as usize, i,
282+
"lane dim must equal its index (locked order)"
283+
);
281284
assert!(!a.name.is_empty());
282285
}
283286
}

crates/lance-graph-contract/src/callcenter/mod.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,7 @@
2626
pub mod ogit_uris;
2727
pub mod role_keys;
2828

29-
pub use ogit_uris::{
30-
savant_ogit_uri, savant_ogit_uri_by_name, SAVANT_OGIT_BASE, SAVANT_OGIT_URIS,
31-
};
29+
pub use ogit_uris::{savant_ogit_uri, savant_ogit_uri_by_name, SAVANT_OGIT_BASE, SAVANT_OGIT_URIS};
3230
pub use role_keys::{
3331
savant_role_key, savant_role_key_by_name, SAVANT_ROLE_KEYS, SAVANT_SLICE_END,
3432
SAVANT_SLICE_START, SAVANT_SLICE_WIDTH,

0 commit comments

Comments
 (0)