diff --git a/.claude/board/EPIPHANIES.md b/.claude/board/EPIPHANIES.md index 18fb6696..b1b38b53 100644 --- a/.claude/board/EPIPHANIES.md +++ b/.claude/board/EPIPHANIES.md @@ -1,3 +1,29 @@ +## 2026-06-24 — E-CLASSID-FMA-PATIENT-COLLISION — `CLASSID_FMA = 0x0901` aliased OGAR `patient`; retargeted to the Anatomy domain `0x0A01` + +**Status:** FINDING + FIX (2026-06-24). Surfaced by OGAR's NodeGuid canon audit +(`OGAR/docs/NODEGUID-CANON-AUDIT.md` F-1). `canonical_node.rs` pinned +`NodeGuid::CLASSID_FMA = 0x0000_0901` ("anatomy concept in the Health domain", +ISS-CLASSID-OGAR-DRIFT realign 2026-06-20). But `ogar_codebook.rs` has +`("patient", 0x0901)` — so **FMA anatomy and `patient` shared the identical +classid `0x0901`**, both in Health (`>>8 == 0x09`). A consumer could not tell an +FMA structural node from a patient node by classid; RBAC over `0x0901` (the +patient grants in `rbac.rs`) would also gate FMA reference data. + +**Root cause:** anatomy was placed in the Health PHI domain. But anatomy is +**public reference** (the femur exists; it is `part_of` the lower limb) — a +clinical *finding about* it is PHI; the structure itself is not. + +**Fix:** OGAR minted a new **Anatomy domain `0x0A`** this week +(`anatomical_structure 0x0A01` / `skeleton 0x0A02` / `bone 0x0A03` / `joint +0x0A04`); this contract mirrors it — `ConceptDomain::Anatomy`, `0x0A` routing, +the four concepts in `CODEBOOK` — and **retargets `CLASSID_FMA 0x0901 → 0x0A01`** +(`anatomical_structure`, the FMA root). `patient` stays at `0x0901`; the +collision is cleared. Constant-following consumers (the `ReadMode::FMA` registry, +`soa_graph` `DomainSpec`, q2 #50's `osint-bake/fma` which uses the constant) +inherit the fix with no change. Tests updated to assert `0x0A01` / Anatomy and to +forbid the `0x0901` alias. Cross-ref: OGAR PR #117 (the Anatomy mint + audit), +q2 PR #50 (independent `[mixin:instance]` convergence, F-6). + ## 2026-06-23 — E-CLASSRBAC-PROMOTED-TO-CONTRACT — the §11 trait-placement that lets ogar join the RBAC chain **Status:** FINDING (2026-06-23). The four-crate chain `contract ↔ rbac ↔ ogar ↔ diff --git a/crates/lance-graph-contract/src/canonical_node.rs b/crates/lance-graph-contract/src/canonical_node.rs index 422ebd8d..ef65c188 100644 --- a/crates/lance-graph-contract/src/canonical_node.rs +++ b/crates/lance-graph-contract/src/canonical_node.rs @@ -45,19 +45,24 @@ impl NodeGuid { // `canonical_concept_domain(classid_lo)` (see `crate::ogar_codebook`) routes on // `classid >> 8`. Realigned 2026-06-20 (ISS-CLASSID-OGAR-DRIFT): OSINT was // 0x0007 (OGAR Reserved domain) → 0x0700; FMA was 0x0008 (OGAR OCR block) → - // 0x0901 (anatomy concept in the Health domain). Migration: - // `.claude/plans/ogar-vocab-contract-codebook-migration-v1.md`. + // 0x0901. Re-realigned 2026-06-24 (ISS-CLASSID-OGAR-DRIFT cont.): FMA 0x0901 + // **collided with OGAR `patient` (0x0901)** — both Health. FMA now routes to + // the new **Anatomy** domain root 0x0A01 (`anatomical_structure`); anatomy is + // public reference, not Health PHI. Surfaced by OGAR `docs/NODEGUID-CANON-AUDIT.md` + // F-1. Migration: `.claude/plans/ogar-vocab-contract-codebook-migration-v1.md`. /// **OSINT / Palantir-Gotham** domain root (`0x07` = OSINT domain, `0x00` = /// root). The neo4j-emulation entity graph (people / orgs / systems / events, /// family-grouped). Resolves to [`ReadMode::OSINT`] (hot `Cognitive` value + /// `CoarseOnly` adjacency). pub const CLASSID_OSINT: u32 = 0x0000_0700; - /// **FMA anatomy** — the `anatomy` concept (`0x01`) in the **Health** domain - /// (`0x09`); `0x0900` is the Health root. The Foundational Model of Anatomy + /// **FMA anatomy** — `anatomical_structure` (`0x01`) in the **Anatomy** domain + /// (`0x0A`); `0x0A00` is the Anatomy root. The Foundational Model of Anatomy /// (~70k structural entities, family = body region, bones = stability anchors). - /// Resolves to [`ReadMode::FMA`] (cold `Compressed` reference + `CoarseOnly`). - pub const CLASSID_FMA: u32 = 0x0000_0901; + /// Anatomy is **public reference, not Health PHI** — moved off `0x0901` to + /// clear the collision with OGAR `patient`. Resolves to [`ReadMode::FMA`] + /// (cold `Compressed` reference + `CoarseOnly`). + pub const CLASSID_FMA: u32 = 0x0000_0A01; /// **Project-management** domain root (`0x01`) — OpenProject ↔ Redmine /// (work items, members, versions, …). OGAR codebook `0x01XX`. Resolves to /// [`ReadMode::PROJECT`]. @@ -1754,13 +1759,19 @@ mod tests { assert_eq!(fma.edge_codec, EdgeCodecFlavor::CoarseOnly); // The classids follow OGAR `0xDDCC` (ISS-CLASSID-OGAR-DRIFT realign): - // OSINT domain root `0x0700` (`>>8 == 0x07`); FMA = anatomy concept - // `0x0901` in the Health domain (`>>8 == 0x09`). Never the pre-realign - // 0x0007 (OGAR Reserved) / 0x0008 (OGAR OCR) values. + // OSINT domain root `0x0700` (`>>8 == 0x07`); FMA = `anatomical_structure` + // `0x0A01` in the **Anatomy** domain (`>>8 == 0x0A`) — re-realigned off + // `0x0901` to clear the OGAR `patient` collision. Never the pre-realign + // 0x0007 / 0x0008, nor the colliding 0x0901. assert_eq!(NodeGuid::CLASSID_OSINT, 0x0000_0700); - assert_eq!(NodeGuid::CLASSID_FMA, 0x0000_0901); + assert_eq!(NodeGuid::CLASSID_FMA, 0x0000_0A01); + assert_ne!( + NodeGuid::CLASSID_FMA, + 0x0000_0901, + "must not alias `patient`" + ); assert_eq!(NodeGuid::CLASSID_OSINT >> 8, 0x07, "OSINT domain high byte"); - assert_eq!(NodeGuid::CLASSID_FMA >> 8, 0x09, "Health domain high byte"); + assert_eq!(NodeGuid::CLASSID_FMA >> 8, 0x0A, "Anatomy domain high byte"); assert_eq!( NodeGuid::new(NodeGuid::CLASSID_OSINT, 1, 2, 3, 0xAB, 0xCD).read_mode(), ReadMode::OSINT diff --git a/crates/lance-graph-contract/src/ogar_codebook.rs b/crates/lance-graph-contract/src/ogar_codebook.rs index 7d862314..e8119c59 100644 --- a/crates/lance-graph-contract/src/ogar_codebook.rs +++ b/crates/lance-graph-contract/src/ogar_codebook.rs @@ -52,11 +52,15 @@ pub enum ConceptDomain { Osint, /// `0x08XX` — OCR (optical character recognition / document extraction). Ocr, - /// `0x09XX` — Health (clinical / patient / care; FMA anatomy lives here). + /// `0x09XX` — Health (clinical / patient / care). Health, + /// `0x0AXX` — Anatomy (FMA reference ontology; bones / skeleton / joints). + /// Public structural reference, distinct from `Health` PHI — the FMA + /// anatomy domain (`anatomical_structure` / `skeleton` / `bone` / `joint`). + Anatomy, /// `0x0BXX` — Auth (identity / authz: AuthStore, Zitadel, Zanzibar, Ory Keto). Auth, - /// Any high-byte slot not yet assigned a domain (`0x03XX`–`0x06XX`, `0x0AXX`, `0x0CXX`+). + /// Any high-byte slot not yet assigned a domain (`0x03XX`–`0x06XX`, `0x0CXX`+). Unassigned, } @@ -73,6 +77,7 @@ pub fn canonical_concept_domain(id: u16) -> ConceptDomain { 0x07 => ConceptDomain::Osint, 0x08 => ConceptDomain::Ocr, 0x09 => ConceptDomain::Health, + 0x0A => ConceptDomain::Anatomy, 0x0B => ConceptDomain::Auth, _ => ConceptDomain::Unassigned, } @@ -302,6 +307,15 @@ pub const CODEBOOK: &[(&str, u16)] = &[ ("treatment", 0x0905), ("visit", 0x0906), ("vital_sign", 0x0907), + // ── 0x0AXX — Anatomy domain (FMA reference ontology; public, not PHI) ── + // FMA anatomy lives HERE, not in Health 0x09 — reference structure is + // public, a clinical finding *about* it is PHI. `CLASSID_FMA` retargets to + // `anatomical_structure` (0x0A01), clearing the prior 0x0901 = `patient` + // collision. Mirrors OGAR `ogar-vocab` ConceptDomain::Anatomy. + ("anatomical_structure", 0x0A01), + ("skeleton", 0x0A02), + ("bone", 0x0A03), + ("joint", 0x0A04), // ── 0x0BXX — Auth domain (identity / authz; OGAR's 0x0B AuthStore family) ── ("auth_store", 0x0B01), ("auth_zitadel", 0x0B02), @@ -378,6 +392,7 @@ mod tests { assert_eq!(canonical_concept_domain(0x0700), ConceptDomain::Osint); assert_eq!(canonical_concept_domain(0x0801), ConceptDomain::Ocr); assert_eq!(canonical_concept_domain(0x0901), ConceptDomain::Health); + assert_eq!(canonical_concept_domain(0x0A01), ConceptDomain::Anatomy); assert_eq!(canonical_concept_domain(0x0B01), ConceptDomain::Auth); assert_eq!(canonical_concept_domain(0x0500), ConceptDomain::Unassigned); } @@ -400,8 +415,9 @@ mod tests { ); assert_eq!( classid_concept_domain(NodeGuid::CLASSID_FMA), - ConceptDomain::Health, - "FMA anatomy lives in the Health domain (0x09XX)" + ConceptDomain::Anatomy, + "FMA anatomy lives in the Anatomy domain (0x0AXX), not Health — \ + cleared the 0x0901 = `patient` collision" ); assert_eq!( classid_concept_domain(NodeGuid::CLASSID_DEFAULT),