Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .claude/board/AGENT_LOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## 2026-06-20 (cont.⁶) — SoA-as-graph domain foundation for q2 (OSINT/Gotham 0x0007 + FMA 0x0008)

**Main thread (Opus), autoattended.** Operator: "prepare everything so q2 can render nodes/edges + family nodes + HHTL CLAM hop adjacency, neo4j-emulation; OSINT OGAR class is 0x0007; also FMA anatomy 70k as body with bones as stability anchor — rendering is wired in the q2 session, here just the basic domain + SoA-as-graph." Grounded with two parallel Explore agents (q2 wiring + lance-graph ontology/callcenter/polyglot) BEFORE building — consult-don't-guess paid off twice: (a) `graph_render.rs` ALREADY is the Neo4j/Gotham surface (`GraphSnapshot`/`RenderNode`/`RenderEdge`, consumer = q2 cockpit) → reused, not duplicated; (b) `NiblePath::from_guid_prefix` ALREADY is the canonical GUID→path lowering → de-duped symbiont's third copy onto it.

NEW `contract::soa_graph` (zero-dep, q2-consumable): `project_snapshot(&[NodeRow], &DomainSpec) -> GraphSnapshot` projects the 32-byte head (NodeGuid+EdgeBlock) into the Gotham surface — family nodes (by u24 family), member→family + in-family (identity-low-byte) + out-of-family (family-low-byte) edges. `nearest_anchor` ranks every node to its closest stability-anchor family by the NEW `NiblePath::family_hop_count` (CLAM tree distance = `2·(16−lcp)` on the fixed-depth lowering). `DomainSpec` (domain-agnostic data) + two registered consts: `OSINT_GOTHAM` (classid `0x0007`) + `FMA_ANATOMY` (`0x0008`). Registered both in `BUILTIN_READ_MODES`: `ReadMode::OSINT` (Cognitive/CoarseOnly, hot entity graph) + `ReadMode::FMA` (Compressed/CoarseOnly, cold structural reference). All structure is HEAD-ONLY (anchors = `family` ids, not value-slab entity-types) → the whole projection is zero value decode, falsifiably (`projection_is_head_only_zero_value_decode` poisons the slab, asserts invariant).

**Rendering deferred to q2** (per operator). **Callcenter DataFusion/gremlin POC + the heavier OntologyRegistry ClassView labels = next slices** (named, not built). `cargo test -p lance-graph-contract` **698/698** (7 new: soa_graph ×5, family_hop_count, osint/fma classids); `cargo test --manifest-path crates/symbiont` **12/12** (symbiont `hhtl_path_of` converged onto `from_guid_prefix`, its 2 semantics tests updated 12→16-nibble). clippy `-D warnings` clean. EPIPHANIES `E-ANCHOR-IS-A-HEAD-FIELD-NOT-A-VALUE-TYPE`. Pushed to main.

## 2026-06-20 (cont.⁵) — §2.4 key-only neo4j render green (zero value decode, falsifiable)

**Main thread (Opus), autoattended.** Operator picked superpower §2.4 from the post-reconciliation menu. Read the canon surface in full first (`canonical_node.rs` NodeGuid/EdgeBlock/NodeRow + ValueTenant carve; `soa_view.rs` the `hhtl_path_at`/`edge_block_at`/`identity_plane_at` deferred key facets; `hhtl.rs` NiblePath) — not a scent-skim.
Expand Down
17 changes: 17 additions & 0 deletions .claude/board/EPIPHANIES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
## 2026-06-20 — E-ANCHOR-IS-A-HEAD-FIELD-NOT-A-VALUE-TYPE — graph STRUCTURE (domain, family grouping, hierarchy, stability anchors, adjacency) must key off the 32-byte HEAD (classid / family / HHTL path), never the value slab; only then does the whole neo4j/Gotham view — and "FMA bones as stability anchor" — stay zero-value-decode at memory-scan speed

**Status:** FINDING (perennial; shipped `contract::soa_graph` + `NiblePath::family_hop_count`, 2026-06-20).

When a domain wants a "type" or "category" to drive graph structure or layout, the System-1 reflex is to read an entity-type field — which lives in the **value slab** (`ValueTenant::EntityType`, bytes 32..512). That quietly defeats the entire `E-GUID-IS-THE-GRAPH` thesis: the moment structure depends on a value field, the "render from the head" scan has to decode the value, and the 8 MiB of slabs go hot. The fix is an ontology discipline, not a trick: **every structural axis is already in the 32-byte head**, so put the category there.

The head carries three orthogonal structural axes, and the OSINT/Gotham + FMA graph uses all three with zero value decode:
1. **Domain = `classid`** (bytes 0..4, head). OSINT/Gotham = `0x0007`, FMA-anatomy = `0x0008`. The `classid → ReadMode` registry resolves how to read the 128+128 — itself a head-only lookup.
2. **Family grouping = `family`** (u24, bytes 10..13, head). `soa_graph::project_snapshot` emits one family node per distinct `family`; "use family nodes" is a head field, not a join.
3. **Hierarchy + adjacency = the HHTL path** (`classid_lo·HEEL·HIP·TWIG`, head). `NiblePath::family_hop_count` (CLAM tree distance) ranks nearest-anchor with no value read.

The keystone: **a stability anchor is a FAMILY (head), not an entity-type (value).** "FMA bones as the skeleton the soft tissue hangs off" is expressible as `DomainSpec::anchor_families: &[u32]` — a list of head `family` ids — so `nearest_anchor` computes the bone-distance layout signal entirely from keys. Had we modelled "bone" as a value-slab entity-type, a 70k-node anatomy render would decode 70k×480 B just to find the skeleton. Because anchor-ness is a head field, the same projector serves the Gotham entity graph and the FMA body with one zero-value-decode sweep.

The general rule: **value slab = content (fingerprints, qualia, energy); head = structure (identity, domain, family, hierarchy, anchors).** If a graph/layout/routing decision is reaching into the value slab, the category is in the wrong register — lift it to classid/family/HHT. Cross-ref: `E-GUID-IS-THE-GRAPH`, `E-ZERO-DECODE-IS-FALSIFIABLE-BY-POISON` (the test that catches a regression here — `soa_graph::tests::projection_is_head_only_zero_value_decode` poisons the slab and asserts the snapshot is invariant), `E-BASIN-IS-A-NODE` (the family/basin tree this projects), OGAR `CLAUDE.md` P0 "the key prerenders nodes with zero value decode."

---

## 2026-06-20 — E-ZERO-DECODE-IS-FALSIFIABLE-BY-POISON — a "we never read region X" claim (zero-value-decode key render, cold-column skip, head-only scan) is not a comment, it is a TEST: poison region X with a sentinel, run the op, assert the output is byte-identical to the un-poisoned run; if it touched X, the bytes diverge

**Status:** FINDING (perennial; shipped `symbiont/key_render.rs::tests::render_ignores_value_slab`, 2026-06-20).
Expand Down
2 changes: 2 additions & 0 deletions .claude/board/LATEST_STATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

---

> **2026-06-20 — branch work (`claude/jirak-math-theorems-harvest-rfii13`)** — **SoA-as-graph domain foundation for the OSINT/Gotham + FMA consumers (q2 renders the pixels).** New zero-dep `contract::soa_graph`: `project_snapshot(&[NodeRow], &DomainSpec) -> graph_render::GraphSnapshot` projects the canonical 32-byte head (NodeGuid + EdgeBlock) into the EXISTING Gotham/neo4j surface (`graph_render` — reused, not duplicated) — family nodes (by u24 `family`), member/in-family/out-of-family edges, all **zero value decode**. `nearest_anchor` ranks nodes to their nearest stability-anchor family by the new `NiblePath::family_hop_count` (CLAM tree distance). Two domains registered: `OSINT_GOTHAM` (classid **`0x0007`**) + `FMA_ANATOMY` (**`0x0008`**, bones = anchor families) in `BUILTIN_READ_MODES` (`ReadMode::OSINT` Cognitive/CoarseOnly hot; `ReadMode::FMA` Compressed/CoarseOnly cold). Anchor-ness is a HEAD field (`family`), never a value type — so "FMA bones as stability anchor" stays head-only (`E-ANCHOR-IS-A-HEAD-FIELD-NOT-A-VALUE-TYPE`). De-duped the GUID→NiblePath lowering: symbiont's `hhtl_path_of` now delegates to canonical `from_guid_prefix` (third copy collapsed). 698 contract + 12 symbiont tests green, clippy clean. **Deferred (named):** q2 rendering (q2 session), Callcenter DataFusion/gremlin POC, OntologyRegistry ClassView labels. Refs: AGENT_LOG 2026-06-20 (cont.⁶), EPIPHANIES `E-ANCHOR-IS-A-HEAD-FIELD-NOT-A-VALUE-TYPE`.
>
> **2026-06-20 — branch work (`claude/happy-hamilton-0azlw4`)** — **UNICHARSET `other_case` transcoded + byte-parity proven (E-CPP-PARITY-5), the fifth leaf.** `UniCharSet` now parses the case-pair id (the token right after the script) into `other_cases: Vec<i32>`, applying the load-time clamp (`unicharset.cpp:901`: a value `>= size`, incl. the absent default, folds to the id itself). Exposes `get_other_case` + `dump_other_case`, mirroring `unicharset.h:703` (out-of-range id → `INVALID_UNICHAR_ID` -1). **Byte-identical 112/112** on real `eng.lstm-unicharset` vs tesseract's own `get_other_case` (self-validating oracle, `other_case` mode; 60/112 self, 52 real pairs, e.g. `C`→`c`). Last field cleanly reachable by token-offset; direction/mirror/bbox need the multi-tier parser (next, larger leaf). Additive, zero-dep; +4 contract tests (23 unicharset total), clippy `-D warnings` + fmt clean; reproducible via `examples/unicharset_dump.rs other_case`. Consumed by `tesseract-core::CharSet::get_other_case` (+1 boundary test, 6/6). No Core gap. EPIPHANIES `E-CPP-PARITY-5`.
>
> **2026-06-20 — branch work (`claude/happy-hamilton-0azlw4`)** — **UNICHARSET script table transcoded + byte-parity proven (E-CPP-PARITY-4), the fourth leaf — first to transcode an INTERNING side-table.** `UniCharSet` now parses the per-line script name (the token after the optional bbox/stats CSV), interns it via an `add_script`-equivalent (`unicharset.cpp:1063`, insertion-order dedup) into `scripts: Vec<String>` with `null_script` ("NULL") seeded at sid 0 (the `unichar_insert` set_script, `unicharset.cpp:680`; so `null_sid_ == 0` always), and stores `script_ids: Vec<i32>`. Exposes `get_script` / `get_script_table_size` / `script_from_script_id` / `script_of` / `dump_script`, mirroring `unicharset.h:681` (out-of-range → `null_sid_` 0). **Byte-identical 112/112** on real `eng.lstm-unicharset` vs tesseract's own `get_script` (same self-validating oracle, `script` mode; oracle table = `["NULL","Common","Latin"]` confirmed empirically before writing the Rust). Mixed-tier safe (eng id 0 is tier-5 no-CSV, others tier-1 CSV). Additive, zero-dep; +4 contract tests (19 unicharset total), clippy `-D warnings` + fmt clean; reproducible via `examples/unicharset_dump.rs script`. Consumed by `tesseract-core::CharSet::{get_script,script_of}` (+1 boundary test, 5/5). No Core gap. EPIPHANIES `E-CPP-PARITY-4`. Next leaf: the full column tier-parser (unlocks other_case/mirror/direction/bbox).
Expand Down
10 changes: 10 additions & 0 deletions .claude/plans/unified-soa-rubikon-integration-v1.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ No copies, no per-subsystem mirror (R1 "one SoA never transformed").
16384 nodes / 32768 edges from 512 KiB of heads, 7680 KiB of value slabs COLD;
zero-value-decode proven by the `0xFF`-poison falsifiable probe. `SymbiontBoard`
now materialises the contract's `edge_block_at`/`hhtl_path_at` key facets.
- ✅ **SoA-as-graph domain foundation for q2 (OSINT/Gotham + FMA)** —
`contract::soa_graph` projects the head into the EXISTING `graph_render`
Gotham/neo4j surface (`GraphSnapshot`): family nodes (u24 `family`),
member/in-family/out-of-family edges, `nearest_anchor` via the new
`NiblePath::family_hop_count` (CLAM hop adjacency). Domains `OSINT_GOTHAM`
(`classid 0x0007`) + `FMA_ANATOMY` (`0x0008`, bones = anchor families)
registered in `BUILTIN_READ_MODES`. All structure head-only (anchor = `family`,
not value type → `E-ANCHOR-IS-A-HEAD-FIELD`). **Rendering deferred to the q2
session; Callcenter DataFusion/gremlin POC + OntologyRegistry ClassView labels
are the named next slices.**

---

Expand Down
60 changes: 60 additions & 0 deletions crates/lance-graph-contract/src/canonical_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ impl NodeGuid {
/// Reserved canonical default basin (implicit fallback; no neighborhood grouping).
pub const FAMILY_DEFAULT: u32 = 0x00_0000;

/// OGAR class for the **OSINT / Palantir-Gotham** domain — the neo4j-emulation
/// entity graph (people / orgs / systems / events, family-grouped). Resolves
/// to [`ReadMode::OSINT`] (hot `Cognitive` value + `CoarseOnly` adjacency edges).
pub const CLASSID_OSINT: u32 = 0x0000_0007;
/// OGAR class for the **FMA anatomy** domain — the Foundational Model of
/// Anatomy (~70k structural entities, family = body region, bones = stability
/// anchors). Resolves to [`ReadMode::FMA`] (cold `Compressed` reference value +
/// `CoarseOnly` part-of adjacency).
pub const CLASSID_FMA: u32 = 0x0000_0008;

/// Construct from the six canonical groups. `family`/`identity` use their low 3 bytes.
///
/// Panics (incl. const-eval) when `family` or `identity` exceed 24 bits — the
Expand Down Expand Up @@ -659,6 +669,25 @@ impl ReadMode {
edge_codec: EdgeCodecFlavor::CoarseOnly,
};

/// The **OSINT / Palantir-Gotham** read-mode ([`NodeGuid::CLASSID_OSINT`]):
/// a *hot* entity graph — [`ValueSchema::Cognitive`] (Meta + Qualia +
/// Fingerprint + Energy + Plasticity + EntityType, for live NARS reasoning)
/// over [`EdgeCodecFlavor::CoarseOnly`] adjacency (the 12 in-family + 4
/// out-of-family slots read literally as the neo4j-emulation edges).
pub const OSINT: ReadMode = ReadMode {
value_schema: ValueSchema::Cognitive,
edge_codec: EdgeCodecFlavor::CoarseOnly,
};

/// The **FMA anatomy** read-mode ([`NodeGuid::CLASSID_FMA`]): a *cold*
/// structural reference graph — [`ValueSchema::Compressed`] (Fingerprint +
/// Helix + Turbovec + EntityType; no hot lifecycle columns, it is static
/// reference data) over [`EdgeCodecFlavor::CoarseOnly`] part-of adjacency.
pub const FMA: ReadMode = ReadMode {
value_schema: ValueSchema::Compressed,
edge_codec: EdgeCodecFlavor::CoarseOnly,
};

/// Both axes are layout-preserving (a preset/flavor re-interprets reserved
/// bytes, never a stride change), so adopting any read-mode needs no
/// `ENVELOPE_LAYOUT_VERSION` bump.
Expand All @@ -680,6 +709,11 @@ static BUILTIN_READ_MODES: LazyLock<HashMap<u32, ReadMode>> = LazyLock::new(|| {
let mut m = HashMap::new();
// The canon default class materialises the POC-Full slab (see ReadMode::DEFAULT).
m.insert(NodeGuid::CLASSID_DEFAULT, ReadMode::DEFAULT);
// OSINT/Gotham (hot entity graph) + FMA anatomy (cold structural reference) —
// the two registered graph domains (see `soa_graph`). Both read edges as
// CoarseOnly adjacency; they differ in the value schema (hot vs cold).
m.insert(NodeGuid::CLASSID_OSINT, ReadMode::OSINT);
m.insert(NodeGuid::CLASSID_FMA, ReadMode::FMA);
m
});

Expand Down Expand Up @@ -1224,4 +1258,30 @@ mod tests {
assert!(rm.value_schema.tenant_bytes() <= VALUE_SLAB_LEN);
assert!(rm.is_layout_preserving());
}

#[test]
fn osint_and_fma_classids_resolve_to_their_read_modes() {
// The two registered graph domains (see `soa_graph`): OSINT/Gotham is a
// hot entity graph (Cognitive value), FMA anatomy is a cold structural
// reference (Compressed value); both read edges as CoarseOnly adjacency.
let osint = classid_read_mode(NodeGuid::CLASSID_OSINT);
assert_eq!(osint, ReadMode::OSINT);
assert_eq!(osint.value_schema, ValueSchema::Cognitive);
assert_eq!(osint.edge_codec, EdgeCodecFlavor::CoarseOnly);

let fma = classid_read_mode(NodeGuid::CLASSID_FMA);
assert_eq!(fma, ReadMode::FMA);
assert_eq!(fma.value_schema, ValueSchema::Compressed);
assert_eq!(fma.edge_codec, EdgeCodecFlavor::CoarseOnly);

// The classids are the OGAR-confirmed 0x0007 (OSINT) and 0x0008 (FMA);
// both are layout-preserving and carrier-method-consistent.
assert_eq!(NodeGuid::CLASSID_OSINT, 0x0000_0007);
assert_eq!(NodeGuid::CLASSID_FMA, 0x0000_0008);
assert_eq!(
NodeGuid::new(NodeGuid::CLASSID_OSINT, 1, 2, 3, 0xAB, 0xCD).read_mode(),
ReadMode::OSINT
);
assert!(osint.is_layout_preserving() && fma.is_layout_preserving());
}
}
35 changes: 35 additions & 0 deletions crates/lance-graph-contract/src/hhtl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,21 @@ impl NiblePath {
Self::from_packed(path, MAX_DEPTH)
}

/// **Family hop count** — the CLAM tree distance to `other`: the number of
/// edges between the two nodes through their lowest common ancestor in the
/// 16ⁿ tree. `(self.depth − common) + (other.depth − common)` where `common =
/// `[`common_prefix_depth`](NiblePath::common_prefix_depth). Identical path =
/// 0, parent/child = 1, siblings = 2; disjoint subtrees = the full ascent +
/// descent. This is the operator's "HHTL CLAM via family-nodes hop count as
/// adjacency" metric — pure key arithmetic, O(depth), **zero value decode**.
///
/// Symmetric: `a.family_hop_count(b) == b.family_hop_count(a)`.
#[must_use]
pub const fn family_hop_count(self, other: Self) -> u8 {
let common = self.common_prefix_depth(other);
(self.depth - common) + (other.depth - common)
}

/// Is this path a descendant-or-equal of `other`? — the symmetric form of
/// [`is_ancestor_of`]. `self.is_descendant_of(other)` is equivalent to
/// `other.is_ancestor_of(self)` BUT the form is sometimes more natural at
Expand Down Expand Up @@ -658,6 +673,26 @@ mod tests {
assert_eq!(NiblePath::EMPTY.common_ancestor(a), None);
}

#[test]
fn family_hop_count_is_clam_tree_distance() {
let a = NiblePath::root(0x1).child(0x2).child(0x3).child(0x4);
// identical path = 0 hops
assert_eq!(a.family_hop_count(a), 0);
// siblings (share parent (1)(2)(3), differ in leaf) = 2 hops
let sib = NiblePath::root(0x1).child(0x2).child(0x3).child(0x9);
assert_eq!(a.family_hop_count(sib), 2);
assert_eq!(sib.family_hop_count(a), 2); // symmetric
// parent = 1 hop
let parent = NiblePath::root(0x1).child(0x2).child(0x3);
assert_eq!(a.family_hop_count(parent), 1);
// cousins: share (1)(2), differ from depth 3 down → (4-2)+(4-2) = 4
let cousin = NiblePath::root(0x1).child(0x2).child(0x7).child(0x8);
assert_eq!(a.family_hop_count(cousin), 4);
// disjoint basins: no common prefix → full ascent + descent
let other = NiblePath::root(0xF).child(0xE);
assert_eq!(a.family_hop_count(other), 4 + 2);
}

// ── NiblePath::prefix — single-shot ancestor view ─────────────────────────

#[test]
Expand Down
4 changes: 4 additions & 0 deletions crates/lance-graph-contract/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ pub mod sensorium;
pub mod sigma_propagation;
pub mod sla;
pub mod soa_envelope;
pub mod soa_graph;
pub mod soa_view;
pub mod splat;
pub mod tax;
Expand All @@ -123,5 +124,8 @@ pub use episodic_edges::{EdgeRef, EpisodicEdges64};
pub use head2head::{CompetitionOutcome, Head2Head, WinnerCriterion};
pub use kanban::{ExecTarget, KanbanColumn, KanbanMove, RubiconTransitionError};
pub use scheduler::{DatasetVersion, NextPhaseScheduler, VersionScheduler};
pub use soa_graph::{
nearest_anchor, project_snapshot, AnchorHop, DomainSpec, FMA_ANATOMY, OSINT_GOTHAM,
};
pub use soa_view::{MailboxSoaOwner, MailboxSoaView};
pub use view_angle::ViewAngle;
Loading
Loading