Skip to content

Commit fe58dfc

Browse files
committed
chore(contract): address #480 CodeRabbit review + add CI status badges
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
1 parent 62bca5e commit fe58dfc

5 files changed

Lines changed: 63 additions & 5 deletions

File tree

.claude/board/AGENT_LOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
## 2026-06-09 — D-IDENTITY-1 review-fix (#480 CodeRabbit) + CI badges
2+
3+
**Follow-up PR** off merged `main`. Addressed CodeRabbit #480: `from_packed` edge-case test (depth>MAX, high-bit reject, `(0,0)` sentinel, MAX_DEPTH `>>64`-guard boundary, `packed∘from_packed` identity); stale "open DECISION" line → RESOLVED; AGENT_LOG SHA (`947c1e4`); MD040/MD058 in the two plan docs. **Skipped** MD028 (LATEST_STATE) — the blank-between-entries IS the append-only style. Added the **no-content-drift-for-existing** invariant to the plan (sole drift surface = ontology cache not mapped from its authoritative source). Native CI badges (rust-test/style/build) → README. 600 contract lib tests (+1), clippy/fmt clean.
4+
15
## 2026-06-09 — D-IDENTITY-1 (Phase A) + 2 cross-repo sweeps — identity-architecture
26

3-
**Orchestrator:** Opus main thread (autoattended). **Outcome:** Shipped Phase A.
7+
**Orchestrator:** Opus main thread (autoattended). **Outcome:** Shipped Phase A — commit `947c1e4`, PR #480 (merged `62bca5e`).
48
- **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.
59
- **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.
610
- **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.

.claude/plans/cognitive-write-roundtrip-substrate-v1.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ u16 ⊕ `MetaWord` u32) and deliberately EXCLUDES the lossy CAM-PQ fingerprint
4141

4242
### MAP 1 — the round-trip (the whole point)
4343

44-
```
44+
```text
4545
WRITE (project = encode) READ-BACK (decompile = decode)
4646
Vec<Triple> Vec<Triple>
4747
│ intern (s,p,o) → ids (dict) ▲ dict reverse: ids → (s,p,o)
@@ -87,7 +87,7 @@ u16 ⊕ `MetaWord` u32) and deliberately EXCLUDES the lossy CAM-PQ fingerprint
8787

8888
### MAP 5 — THINK/DO (Semantik/Pragmatik) both round-trip as triples
8989

90-
```
90+
```text
9191
Class (shape, subClassOf) ──► triples: (subj rdf:type ogit:ObjectType), (field depends_on …) THINK
9292
ActionDef (DO, object_class→Class, OdooMethodKind, KausalSpec) ──► triples: (amount_total emitted_by _compute_amount) DO
9393
│ │

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

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ against what must be built, and phases the integration.
4747
## WHAT EXISTS — grounded inventory (6 layers, file:line)
4848

4949
### Layer 0 — address / discriminator scalars (the GUID's fields)
50+
5051
| Type | Width | Role | Status | Evidence |
5152
|---|---|---|---|---|
5253
| `NiblePath{path:u64,depth:u8}` | 72 | HHTL tree address (basin/child/is_ancestor_of, 16ⁿ) | **[G]** | hhtl.rs |
@@ -59,6 +60,7 @@ against what must be built, and phases the integration.
5960
| `EdgeRef{family:u8,local:u16}` | 24 | episodic HHTL family+local address | **[G]** | episodic_edges.rs:34 |
6061

6162
### Layer 1 — edge / handoff carriers (the LE "sound members")
63+
6264
| Type | Width | Role | Status |
6365
|---|---|---|---|
6466
| `EpisodicEdges64(u64)` = 4×EdgeRef, MRU promote/evict, `to_le_bytes` | 64 | AriGraph episodic edges | **[G]** episodic_edges.rs |
@@ -67,6 +69,7 @@ against what must be built, and phases the integration.
6769
| `MailboxId=u32`, `MailboxRow{mailbox_ref:u32,row_idx:u32}` | 32/64 | mailbox + row address | **[G]** |
6870

6971
### Layer 2 — cold-path stores (TODAY: thin + inconsistent)
72+
7073
| Store | Key | Status |
7174
|---|---|---|
7275
| `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 |
@@ -75,12 +78,14 @@ against what must be built, and phases the integration.
7578
| `WitnessId(u64)` (arigraph witness) | 64 opaque handle | **[G]** witness_corpus.rs:63 |
7679

7780
### Layer 3 — resolution (class-from-address)
81+
7882
| Surface | Status |
7983
|---|---|
8084
| `RegistryClassView: ClassView` (fields/template/dolce_category_id) | **[G] resolve / [H] field-enum deferred** class_resolver.rs |
8185
| `OntologyRegistry`: `resolve_uri`, `enumerate_first_with_entity_type_id(u16)`, `resolve_iri_in` | **[G]** registry.rs |
8286

8387
### Layer 4 — commit + witness (the membrane)
88+
8489
| Surface | Status |
8590
|---|---|
8691
| `SoaEnvelope` trait + `ColumnDescriptor` (container-LE geometry) | **[G] trait / [H] ZERO impls** soa_envelope.rs |
@@ -91,6 +96,7 @@ against what must be built, and phases the integration.
9196
| `SlaPolicy`, `TenantScope` | **[G] types** sla.rs |
9297

9398
### Layer 5 — cross-store transport (the consumer boundary)
99+
94100
| Surface | Status |
95101
|---|---|
96102
| `EntityKey<'a>(pub &'a [u8])` — opaque length-agnostic key | **[G]** repository.rs:12 |
@@ -100,6 +106,7 @@ against what must be built, and phases the integration.
100106
| smb-office-rs: Mongo `ObjectId`(12B) + `String` refs; actively impls repository | **[G]** base.rs:92 |
101107

102108
### Layer 6 — round-trip / substrate-hardening
109+
103110
| Surface | Status |
104111
|---|---|
105112
| `TripletProjection` trait + `roundtrip_eq``RoundTripFailure` | **[G]** codegen_spine.rs:107 |
@@ -190,14 +197,25 @@ content), never VSA-bundled.
190197
green-field invention. The largest is N6 (cold-path string→identity migration).
191198
- **[BLOCKED(C)]:** N8 only (surrealdb fork coords — human gate; lance-graph P0
192199
"STOP and ask").
193-
- **One open [DECISION]:** D1 vs D2 (SchemaPtr-entity_type vs NiblePath-prefix as
194-
the class carrier) — recommendation: both (exact + routing prefix).
200+
- **[DECISION] RESOLVED (Phase A):** carry BOTH — `entity_type:u16` is the exact
201+
canonical class; the `NiblePath` prefix is the bijective *derived* routing view
202+
(full statement in the "DECISION — RESOLVED" block above). Landed in `NodeGuid`.
195203

196204
## Guards (iron rules this plan must not violate)
197205

198206
- **I-VSA-IDENTITIES:** the GUID is a register key that POINTS TO content; never
199207
VSA-bundle it, never intern open content (only the closed vocabulary). Identities
200208
intern; scanned papers / free text stay in consumer stores (Layer 5).
209+
- **No content-drift for existing entities — ontology-cache provenance is the ONLY
210+
drift surface.** An existing entity's identity is a *derived function* of its
211+
ontology-mapped class (`entity_type` / `NiblePath` / `shape_hash` all fall out of
212+
the registry mapping), so a mapped entity **cannot** drift from its content. The
213+
one residual drift surface is an **ontology cache that was not mapped from the
214+
authoritative source** (hand-filled / stale / regenerated out-of-band) — that,
215+
not per-instance divergence, is exactly what the `shape_hash` witness reading of
216+
`NodeGuid` guards. ⇒ the registry MUST treat the authoritative ontology as its
217+
mapped source (the Phase B bijection is *seeded from* that source, never from a
218+
hand-filled cache).
201219
- **Compose, don't parallel (Agent A #2):** N1 MUST subsume `SchemaPtr` +
202220
`EdgeRef`, not re-pack ns/class/family beside them.
203221
- **I-LEGACY-API-FEATURE-GATED:** N6's string→identity layout reclaim needs a

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Lance Graph
22

3+
[![Rust Tests](https://github.com/AdaWorldAPI/lance-graph/actions/workflows/rust-test.yml/badge.svg)](https://github.com/AdaWorldAPI/lance-graph/actions/workflows/rust-test.yml) [![Style Check](https://github.com/AdaWorldAPI/lance-graph/actions/workflows/style.yml/badge.svg)](https://github.com/AdaWorldAPI/lance-graph/actions/workflows/style.yml) [![Build](https://github.com/AdaWorldAPI/lance-graph/actions/workflows/build.yml/badge.svg)](https://github.com/AdaWorldAPI/lance-graph/actions/workflows/build.yml)
4+
35
Lance Graph is a Cypher-capable graph query engine built in Rust with Python bindings for building high-performance, scalable, and serverless multimodal knowledge graphs.
46

57
This repository contains:

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,40 @@ mod tests {
322322
assert_eq!(NiblePath::EMPTY.basin(), None);
323323
}
324324

325+
#[test]
326+
fn from_packed_validates_depth_high_bits_and_roundtrips() {
327+
// (0, 0) is the EMPTY sentinel.
328+
assert_eq!(NiblePath::from_packed(0, 0), Some(NiblePath::EMPTY));
329+
330+
// A well-formed (path, depth) reconstructs exactly what `child` builds.
331+
assert_eq!(
332+
NiblePath::from_packed(0x12, 2),
333+
Some(NiblePath::root(0x1).child(0x2)),
334+
);
335+
336+
// depth > MAX_DEPTH is rejected.
337+
assert_eq!(NiblePath::from_packed(0, MAX_DEPTH + 1), None);
338+
339+
// Bits set above the 4·depth route nibbles are an inconsistent pack.
340+
// depth = 2 ⇒ only the low 8 bits may be set; 0x112 has a 9th.
341+
assert_eq!(NiblePath::from_packed(0x112, 2), None);
342+
343+
// Boundary: at MAX_DEPTH the whole u64 is usable (the `used_bits < 64`
344+
// guard skips a `>> 64` UB), so even all-ones round-trips.
345+
let max = NiblePath::from_packed(u64::MAX, MAX_DEPTH);
346+
assert_eq!(max.map(NiblePath::packed), Some((u64::MAX, MAX_DEPTH)));
347+
348+
// packed ∘ from_packed is identity on every valid path.
349+
for p in [
350+
NiblePath::EMPTY,
351+
NiblePath::root(0x3),
352+
NiblePath::root(0x3).child(0x5).child(0xA),
353+
] {
354+
let (path, depth) = p.packed();
355+
assert_eq!(NiblePath::from_packed(path, depth), Some(p));
356+
}
357+
}
358+
325359
#[test]
326360
fn depth_caps_at_max_and_rejects_out_of_range_nibble() {
327361
// Fill to MAX_DEPTH, then one more child is a no-op (not a wrap/overflow).

0 commit comments

Comments
 (0)