Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
33 changes: 33 additions & 0 deletions .claude/board/EPIPHANIES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,36 @@
## 2026-06-26 — E-V3-BASINS-ARE-MEREOLOGY-NOT-LABELS — the 6 V3 basins (+ relative location) are a structural ADDRESS (mereology / HHTL X;Y coordinates), never flat labels

**Status:** FINDING `[H]` (operator directive 2026-06-26; impl in the FMA-V3 +
CPIC-V3 mint, branch `claude/fma-cpic-v3-mint`). Sharpens `E-FACET-8-8-ALWAYS`
on the *consumer-projection* side and is a direct application of
`I-VSA-IDENTITIES` Test-0 (register-laziness).

**The claim.** The V3 identity's 6 `(part_of:is_a)` tiles — HEEL·HIP·TWIG·LEAF
(routing) + family·identity (basin tail) — plus the *relative location within
each basin* are a **substantial address** (6 × 16 bits of hierarchical
coordinate). Spending that address on **labels** (a flat type tag a `HashMap` /
`enum` would carry) wastes it — the exact register-laziness `I-VSA-IDENTITIES`
Test-0 forbids ("does this thing have a natural name/ID? then use the register,
not the rich carrier"). The basins must instead carry **mereology / membership**:
*where a thing sits in the part-of hierarchy* (AST node position, basin
location), readable equivalently as HHTL `(X;Y)` coordinates per tile (`part_of`
= X = hi byte, `is_a` = Y = lo byte).

**The canonical case — genes (Genetics / CPIC-V3).** A gene's identity is its
**genomic position**: `genome → chromosome → region → locus → gene` is the
part-of cascade; the **human genome is the fixed schema view** the position is
taken against (hence `ValueSchema::Compressed` — a fixed reference frame, not a
hot lifecycle). Phase 2 then carries gene **expression as the coordinate
*value*** on top of that positional address. "All expression" rides the basins
as `(X;Y)`, the genome is the codebook.

**Litmus.** Before assigning meaning to a V3 basin: *is this a coordinate
(where-it-is) or a name (what-it's-called)?* Name → it belongs in the content
store / ClassView, not the basin. Coordinate / membership → the basin is exactly
right, and the 6-tier address is the payoff. Cross-ref `E-FACET-8-8-ALWAYS`
(content-blind 8:8, consumer projects), `I-VSA-IDENTITIES` Test-0, CANON
"basin location" (`family` = basin), `CLASSID_CPIC_V3` doc-comment.

## 2026-06-25 — E-FACET-8-8-ALWAYS — the homogeneous facet is ALWAYS 8:8 (content-blind, consumer-projected); it amortizes to a 2bit×2bit Morton tile cascade

**Status:** FINDING `[H]` (operator-locked 2026-06-25; impl PR #613). Refines
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 @@ -140,6 +140,8 @@ Membrane consumers can now pull BOTH halves of a render `classid` BBB-safely fro

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

> **2026-06-26 — ADDED (Phase 1 COMPLETE — FMA-V3 + CPIC-V3 mints + Genetics domain)**: the two remaining V3 identity classes that close Phase 1. `NodeGuid::{CLASSID_FMA_V3 = 0x1000_0A01, CLASSID_CPIC_V3 = 0x1000_0E00}` + `ReadMode::{FMA_V3, CPIC_V3}` (both `{V3, Compressed, CoarseOnly}`) + `BUILTIN_READ_MODES` entries, all gated `guid-v3-tail` — mirroring the OSINT-V3 (#613) pattern: the `0x1000` gen-marker in the HIGH u16, canon domain preserved in the LOW u16 so `classid_concept_domain` still routes (`0x0A01 → Anatomy`, `0x0E00 → Genetics`). **NEW Genetics domain `0x0E`** in `ogar_codebook::ConceptDomain` (+ `0x0E => Genetics` route, parity test pins `0x0E00 → Genetics`) — **operator-allocated 2026-06-26** (`0x0D` was already HR); mirror target `ogar_vocab::ConceptDomain::Genetics` (OGAR catches up under the drift guard). **Genetics framing (operator directive, `I-VSA-IDENTITIES` Test-0):** the 6 V3 basins are genomic **mereology, not labels** — a gene's identity is its *position* in the part-of hierarchy (genome → chromosome → region → locus → gene), readable as HHTL `(X;Y)` coordinates per `(part_of:is_a)` tile; the human genome is the **fixed schema view** (hence `Compressed`, a fixed reference frame), and Phase 2 shapes gene **expression as the coordinate value**. The Phase-1 V3 set (OSINT + FMA + CPIC) is now **complete → unblocks the atomic Canon:Custom flip + Phase 2** (plan §2.3 sequencing). Confirm test `read_mode_fma_v3_and_cpic_v3_route_their_domains` (gated `guid-v3-tail`): both route their domain, resolve `tail_variant == V3`, distinct classids. Additive, layout-preserving, default-V1; **739** lib green default / **750** `guid-v3-tail`, clippy `--all-targets -D warnings` + fmt clean. Plan `soa-value-tenant-migration-v2.md` §2.2 (FMA/CPIC rows wired; Genetics `0x0E`). Branch `claude/fma-cpic-v3-mint`.
>
> **2026-06-25 — ADDED (Phase 1 identity→V3, the `mint_for` tail-variant carrier)**: `lance_graph_contract::canonical_node::NodeGuid::mint_for(tail_variant, classid, heel, hip, twig, leaf, family, identity)` (`const`, feature `guid-v2-tail`) — the **key-side symmetric spine** of `soa-value-tenant-migration-v2.md` §2.1: a consumer mints its identity BY ITS CLASSID's tail (`mint_for(classid_read_mode(c).tail_variant, …)`), never hardcoding `new` vs `new_v2` — the exact analog of the Phase-2 value-side `to_node_row(classid_read_mode(c).value_schema, …)`, same `classid_read_mode(c)` lookup, sibling field. Migrating a class's identity to V3 becomes a one-line `tail_variant` flip in the registry, zero consumer rewrite ("extend the one `ReadMode`, never a public `new_v3`"). Dispatch: `V1 → new` (u24·u24 tail; `leaf` ignored — V1 has no LEAF tier), `V2 | V3 → new_v2` (the shared `leaf·family·identity` 3×u16 tail — V3 differs only in how the bytes are *read*, the `(part_of:is_a)` tile, not how they are *stored*, so it mints through the same constructor). **No silent truncation** (the footgun v2 removes): the V2/V3 arm `assert!`s `family`/`identity` fit `u16`, mirroring `new`'s own 24-bit guard. **`Cargo.toml`: `guid-v3-tail = ["guid-v2-tail"]`** — V3's mint path dispatches to `new_v2`, so the tail constructor must exist whenever a V3 classid can be minted (honest gating per `I-LEGACY-API-FEATURE-GATED`). **End-to-end confirm** (`mint_for_osint_v3_is_end_to_end_routable`, gated `guid-v3-tail`): mint OSINT-V3 via the carrier → `read_mode().tail_variant == V3` → `from_guid_prefix_v3` routes non-empty at depth 16 (the full HEEL·HIP·TWIG·LEAF cascade) **while** the v1 `from_guid_prefix` still returns `None` (the Codex-P2 EMPTY-fold is gone, both directions proven) → `decode_v2` reads the tiers back; plus `mint_for_dispatches_to_the_right_constructor_per_tail` (gated `guid-v2-tail`: V1==`new`, V2==V3==`new_v2`). Additive, zero-dep, latent-default-V1 (zero re-mint of the V1/V2 corpus, RESERVE-DON'T-RECLAIM); 737 lib green default / 744 `guid-v2-tail` / 747 `guid-v3-tail`, clippy `--all-targets -D warnings` + fmt clean. Plan: `soa-value-tenant-migration-v2.md` §2 (Phase 1). Branch `claude/identity-v3-mint`.
>
> **2026-06-25 — MODULARIZED (follow-up to #613) — `lance_graph_contract::facet`**: extracted `FacetTier` / `FacetCascade` from `canonical_node` into a dedicated, reusable `facet` module (a *reading*, NOT part of the locked node layout — the cleaner factoring; `canonical_node` re-exports both for the historical path). **Reusable lane API rounded out:** `as_u128`/`from_u128` (single-register view), `rows()` (the 4 dword rows `{domain}{schema}` / `HEEL:HIP` / `TWIG:LEAF` / `family:identity`), `prefix_distance`/`shared_prefix_tiles` (the **granularity-free LCP redout** — `vpxor`+`tzcnt`; 8:8 vs nibble is a free `>>` on the count, measured), `row_match_mask` (`vpcmpeqd`-lane), plus `as_bytes`/`ref_from_bytes` — a **zero-cost reinterpret** (`#[repr(C, align(16))]`; `as_bytes` measured to lower to `mov rax,rdi`, a literal no-op; fields read straight through as single loads). One register → row(`u32`)/tile(`u16`)/prefix(bit)/nibble(Morton) lenses, each one SIMD op (module docs). Lab-test write-up deferred. Additive, zero-dep; 741 lib green (default + `guid-v3-tail`), clippy `-D warnings` + fmt clean. EPIPHANIES `E-FACET-8-8-ALWAYS`. Branch `claude/facet-module`.
Expand Down
149 changes: 137 additions & 12 deletions crates/lance-graph-contract/src/canonical_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,11 @@ impl NodeGuid {
// u16` (`0xDDCC`; CLASSID_OSINT = 0x0700, CLASSID_FMA = 0x0A01). So
// `classid_concept_domain` masks the marker off and still routes the legacy
// domain — the Codex-P1 fix vs the rejected low-half `0x1007` (which read
// domain 0x10 = Unassigned). OSINT-V3 (`0x1000_0700`) is the WIRED exemplar;
// FMA-V3 (`0x1000_0A01`) + Genetics (domain TBD — `0x0D` is HR, not Genetics)
// follow once their value models are pinned.
// domain 0x10 = Unassigned). OSINT-V3 (`0x1000_0700`), FMA-V3 (`0x1000_0A01`),
// and CPIC-V3 (`0x1000_0E00`, Genetics domain `0x0E`, operator-allocated
// 2026-06-26 — `0x0D` was already HR) are the three wired V3 classes that
// complete Phase 1 (identity → V3); the atomic Canon:Custom half-order flip
// follows once the V3 set is complete (plan §2.3).

/// **OSINT-V3** — OSINT on a [`TailVariant::V3`] cascade tail. The generation
/// marker `0x1000` sits in the HIGH/custom u16; the canon `0x0700` is preserved
Expand All @@ -90,6 +92,37 @@ impl NodeGuid {
#[cfg(feature = "guid-v3-tail")]
pub const CLASSID_OSINT_V3: u32 = 0x1000_0700;

/// **FMA-V3** — FMA anatomy on a [`TailVariant::V3`] cascade tail. The marker
/// `0x1000` sits in the HIGH/custom u16; the canon `0x0A01` (Anatomy domain
/// `0x0A`, `anatomical_structure`) is preserved in the LOW u16, so
/// [`classid_concept_domain`](crate::ogar_codebook::classid_concept_domain)
/// still routes [`Anatomy`](crate::ogar_codebook::ConceptDomain::Anatomy).
/// Resolves to [`ReadMode::FMA_V3`] (same cold `Compressed` model as legacy FMA).
#[cfg(feature = "guid-v3-tail")]
pub const CLASSID_FMA_V3: u32 = 0x1000_0A01;

/// **CPIC-V3** — CPIC pharmacogenomics (gene–drug guidelines) on a
/// [`TailVariant::V3`] cascade tail, in the new **Genetics** domain (`0x0E`,
/// operator-allocated 2026-06-26 — `0x0D` was already HR). The marker `0x1000`
/// sits in the HIGH/custom u16; the canon `0x0E00` (Genetics domain root) is
/// preserved in the LOW u16, so
/// [`classid_concept_domain`](crate::ogar_codebook::classid_concept_domain)
/// routes [`Genetics`](crate::ogar_codebook::ConceptDomain::Genetics). Resolves
/// to [`ReadMode::CPIC_V3`].
///
/// **The 6 V3 basins are genomic MEREOLOGY, not labels** (operator directive
/// 2026-06-26; `I-VSA-IDENTITIES` Test-0, register-laziness): a gene's identity
/// is its *position* in the part-of hierarchy (genome → chromosome → region →
/// locus → gene), readable as HHTL `(X;Y)` coordinates per `(part_of:is_a)`
/// tile — never a flat type tag a `HashMap` would carry. The 6-basin + relative
/// location is a substantial address; spending it on labels wastes it. The human
/// genome is the **fixed schema view** the position is taken against, which is
/// why the value model is [`ValueSchema::Compressed`] (a fixed reference frame,
/// not a hot lifecycle); Phase 2 shapes the V3 tenants — gene expression as the
/// coordinate *value* — on top.
#[cfg(feature = "guid-v3-tail")]
pub const CLASSID_CPIC_V3: u32 = 0x1000_0E00;

/// 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 @@ -1014,16 +1047,38 @@ impl ReadMode {

/// The **OSINT-V3** read-mode ([`NodeGuid::CLASSID_OSINT_V3`]): the same hot
/// [`ValueSchema::Cognitive`] value model as legacy [`OSINT`](ReadMode::OSINT),
/// read through the new-generation [`TailVariant::V3`] cascade tail. The wired
/// V3 exemplar; FMA-V3 + Genetics-V3 follow once their classids/value models
/// are pinned.
/// read through the new-generation [`TailVariant::V3`] cascade tail. The first
/// V3 exemplar; [`FMA_V3`](ReadMode::FMA_V3) + [`CPIC_V3`](ReadMode::CPIC_V3)
/// complete the Phase-1 V3 set.
#[cfg(feature = "guid-v3-tail")]
pub const OSINT_V3: ReadMode = ReadMode {
tail_variant: TailVariant::V3,
value_schema: ValueSchema::Cognitive,
edge_codec: EdgeCodecFlavor::CoarseOnly,
};

/// The **FMA-V3** read-mode ([`NodeGuid::CLASSID_FMA_V3`]): the same cold
/// [`ValueSchema::Compressed`] value model as legacy [`FMA`](ReadMode::FMA),
/// read through the new-generation [`TailVariant::V3`] cascade tail.
#[cfg(feature = "guid-v3-tail")]
pub const FMA_V3: ReadMode = ReadMode {
tail_variant: TailVariant::V3,
value_schema: ValueSchema::Compressed,
edge_codec: EdgeCodecFlavor::CoarseOnly,
};

/// The **CPIC-V3** read-mode ([`NodeGuid::CLASSID_CPIC_V3`], Genetics domain):
/// CPIC pharmacogenomics on a [`TailVariant::V3`] cascade tail. The value model
/// [`ValueSchema::Compressed`] is the **Phase-1 provisional** (biomedical
/// reference, mirroring FMA's cold treatment); Phase 2 pins the V3-shaped
/// tenants. Edges [`EdgeCodecFlavor::CoarseOnly`].
#[cfg(feature = "guid-v3-tail")]
pub const CPIC_V3: ReadMode = ReadMode {
tail_variant: TailVariant::V3,
value_schema: ValueSchema::Compressed,
edge_codec: EdgeCodecFlavor::CoarseOnly,
};

/// All three axes are layout-preserving (a tail-variant/preset/flavor
/// re-interprets reserved bytes, never a stride change), so adopting any
/// read-mode needs no `ENVELOPE_LAYOUT_VERSION` bump.
Expand Down Expand Up @@ -1069,13 +1124,15 @@ static BUILTIN_READ_MODES: LazyLock<HashMap<u32, ReadMode>> = LazyLock::new(|| {
m.insert(NodeGuid::CLASSID_PROJECT, ReadMode::PROJECT);
m.insert(NodeGuid::CLASSID_ERP, ReadMode::ERP);
// V3 cascade-key classes (feature `guid-v3-tail`): same value model as their
// legacy domain, on a TailVariant::V3 tail. OSINT-V3 (`0x1000_0700`) is the
// wired exemplar — the high-u16 gen-marker is masked off by the domain router,
// so `classid_concept_domain` still resolves Osint. FMA-V3 + Genetics-V3 land
// here once their classids/value models are pinned.
// legacy domain, on a TailVariant::V3 tail. The high-u16 gen-marker is masked
// off by the domain router, so `classid_concept_domain` still resolves the
// legacy domain (Osint / Anatomy / Genetics). The three together complete the
// Phase-1 V3 set; the atomic Canon:Custom flip follows (plan §2.3).
#[cfg(feature = "guid-v3-tail")]
{
m.insert(NodeGuid::CLASSID_OSINT_V3, ReadMode::OSINT_V3);
m.insert(NodeGuid::CLASSID_FMA_V3, ReadMode::FMA_V3);
m.insert(NodeGuid::CLASSID_CPIC_V3, ReadMode::CPIC_V3);
Comment on lines +1134 to +1135

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Decode V3 family fields before graph projection

When guid-v3-tail is enabled and callers project rows for these newly registered V3 classids, mint_for(... TailVariant::V3 ...) stores leaf in bytes 10..12 and the actual family in bytes 12..14, but soa_graph::project_snapshot/nearest_anchor still group, label, and anchor rows with the V1 row.key.family()/identity() decoding from bytes 10..13 and 13..16. That pollutes FMA_V3/CPIC_V3 family IDs with the leaf byte, so family nodes, adapter-edge resolution, and anchor matching won't correspond to the minted V3 family; update those consumers to branch on the resolved tail variant (e.g. use family_v2/identity_v2 for V3) before enabling these read-mode entries.

Useful? React with 👍 / 👎.

}
m
});
Expand Down Expand Up @@ -2001,8 +2058,8 @@ mod tests {
assert!(TailVariant::V1.is_layout_preserving());
assert!(ReadMode::DEFAULT.is_layout_preserving());
// The mechanism axis exists for all three variants (V3 is the new-gen key).
// OSINT-V3 is the wired exemplar (see read_mode_osint_v3_routes_v3_tail_and_osint_domain);
// FMA-V3 + Genetics-V3 entries are DEFERRED until their classids are pinned.
// The Phase-1 V3 set is COMPLETE: OSINT-V3 + FMA-V3 + CPIC-V3 (Genetics) are
// all wired (see read_mode_fma_v3_and_cpic_v3_route_their_domains).
assert!(TailVariant::V3.is_layout_preserving());
assert!(TailVariant::V2.is_layout_preserving());
}
Expand Down Expand Up @@ -2047,6 +2104,74 @@ mod tests {
);
}

#[cfg(feature = "guid-v3-tail")]
#[test]
fn read_mode_fma_v3_and_cpic_v3_route_their_domains() {
use crate::ogar_codebook::{classid_concept_domain, ConceptDomain};
// Phase-1 V3 set completion: FMA-V3 + CPIC-V3 resolve to the V3 tail AND
// their domain still routes through the high-u16 gen-marker (masked off by
// the domain router) — the same scheme proven for OSINT-V3.

// FMA-V3: Anatomy domain (0x0A) intact; cold Compressed model (mirrors FMA).
assert_eq!(
classid_read_mode(NodeGuid::CLASSID_FMA_V3).tail_variant,
TailVariant::V3
);
assert_eq!(
classid_concept_domain(NodeGuid::CLASSID_FMA_V3),
ConceptDomain::Anatomy
);
assert_eq!(
classid_read_mode(NodeGuid::CLASSID_FMA_V3),
ReadMode::FMA_V3
);
assert_eq!(ReadMode::FMA_V3.value_schema, ValueSchema::Compressed);
assert_eq!(NodeGuid::CLASSID_FMA_V3, 0x1000_0A01);
assert_eq!(
NodeGuid::CLASSID_FMA_V3 >> 16,
0x1000,
"gen-marker high u16"
);
assert_eq!(
NodeGuid::CLASSID_FMA_V3 as u16,
NodeGuid::CLASSID_FMA as u16,
"low u16 == canon FMA concept (0x0A01)"
);

// CPIC-V3: the operator-allocated Genetics domain (0x0E); Compressed = the
// fixed human-genome schema view (basins = genomic mereology, not labels).
assert_eq!(
classid_read_mode(NodeGuid::CLASSID_CPIC_V3).tail_variant,
TailVariant::V3
);
assert_eq!(
classid_concept_domain(NodeGuid::CLASSID_CPIC_V3),
ConceptDomain::Genetics
);
assert_eq!(
classid_read_mode(NodeGuid::CLASSID_CPIC_V3),
ReadMode::CPIC_V3
);
assert_eq!(ReadMode::CPIC_V3.value_schema, ValueSchema::Compressed);
assert_eq!(NodeGuid::CLASSID_CPIC_V3, 0x1000_0E00);
assert_eq!(
NodeGuid::CLASSID_CPIC_V3 >> 16,
0x1000,
"gen-marker high u16"
);
assert_eq!(
NodeGuid::CLASSID_CPIC_V3 as u16,
0x0E00,
"low u16 == Genetics domain root (0x0E00)"
);

// The three V3 classes are mutually distinct, all V3 + layout-preserving.
assert!(ReadMode::FMA_V3.is_layout_preserving());
assert!(ReadMode::CPIC_V3.is_layout_preserving());
assert_ne!(NodeGuid::CLASSID_FMA_V3, NodeGuid::CLASSID_CPIC_V3);
assert_ne!(NodeGuid::CLASSID_FMA_V3, NodeGuid::CLASSID_OSINT_V3);
}

#[cfg(feature = "guid-v3-tail")]
#[test]
fn mint_for_osint_v3_is_end_to_end_routable() {
Expand Down
13 changes: 11 additions & 2 deletions crates/lance-graph-contract/src/ogar_codebook.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,13 @@ pub enum ConceptDomain {
/// bridge) and from `Health` PHI. Mirrors OGAR
/// `ogar_vocab::ConceptDomain::HR` (added in OGAR PR #127).
HR,
/// Any high-byte slot not yet assigned a domain (`0x03XX`–`0x06XX`, `0x0EXX`+).
/// `0x0EXX` — Genetics (pharmacogenomics; CPIC gene–drug guidelines, variant
/// annotations). Public reference knowledge, distinct from `Health` PHI.
/// Operator-allocated 2026-06-26 (`0x0D` was already HR). Mirror target
/// `ogar_vocab::ConceptDomain::Genetics` — OGAR catches up under the drift
/// guard (the parity tests pin `0x0E00 → Genetics`).
Genetics,
Comment on lines +77 to +82

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Update the OGAR parity bridge for Genetics.

Adding ConceptDomain::Genetics here changes the contract enum, but the parity matcher shown in crates/lance-graph-ogar/src/lib.rs:125-148 still stops at HR/Unassigned. That leaves the contract boundary out of sync: the drift guard will no longer cover 0x0E00, and the match can become non-exhaustive once Genetics is visible on both sides. Please add the (O::Genetics, C::Genetics) case in that bridge in the same change.

Also applies to: 104-104

🤖 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/ogar_codebook.rs` around lines 77 - 82, The
OGAR parity bridge is missing the new Genetics contract mapping, leaving
`ConceptDomain::Genetics` out of sync with the vocabulary side and potentially
non-exhaustive. Update the matcher in `ogar_vocab`/`ConceptDomain` parity logic
in `lib.rs` to include the `(O::Genetics, C::Genetics)` case alongside the
existing `HR` and `Unassigned` branches, and keep the drift guard coverage
aligned so `0x0E00` is explicitly matched.

/// Any high-byte slot not yet assigned a domain (`0x03XX`–`0x06XX`, `0x0FXX`+).
Unassigned,
}

Expand All @@ -95,6 +101,7 @@ pub fn canonical_concept_domain(id: u16) -> ConceptDomain {
0x0B => ConceptDomain::Auth,
0x0C => ConceptDomain::Automation,
0x0D => ConceptDomain::HR,
0x0E => ConceptDomain::Genetics,
_ => ConceptDomain::Unassigned,
}
}
Expand Down Expand Up @@ -439,7 +446,9 @@ mod tests {
assert_eq!(canonical_concept_domain(0x0D01), ConceptDomain::HR);
assert_eq!(canonical_concept_domain(0x0D04), ConceptDomain::HR);
assert_eq!(canonical_concept_domain(0x0500), ConceptDomain::Unassigned);
assert_eq!(canonical_concept_domain(0x0E00), ConceptDomain::Unassigned);
// Genetics (0x0E) operator-allocated 2026-06-26 for CPIC-V3 (was Unassigned).
assert_eq!(canonical_concept_domain(0x0E00), ConceptDomain::Genetics);
assert_eq!(canonical_concept_domain(0x0F00), ConceptDomain::Unassigned);
}

#[test]
Expand Down
Loading