@@ -1027,12 +1027,14 @@ impl Class {
10271027/// 0x05XX unassigned
10281028/// 0x06XX unassigned
10291029/// 0x07XX reserved: OSINT
1030- /// 0x08XX reserved: OCR
1031- /// 0x09XX reserved: Health
1030+ /// 0x08XX OCR (container kinds: unicharset/recoder/charset)
1031+ /// 0x09XX Health (clinical / patient / care; 7 OGIT entities)
10321032/// 0x0AXX Anatomy (FMA reference ontology; bones/skeleton)
10331033/// 0x0BXX Auth (IAM; the AuthStore class family)
10341034/// 0x0CXX Automation (HIRO IT-automation: MARS CMDB + actuators)
1035- /// 0x0DXX+ unassigned
1035+ /// 0x0DXX HR (employment / org / contracts)
1036+ /// 0x0EXX reserved: Genetics (CPIC pharmacogenomics, consumed by q2)
1037+ /// 0x0FXX+ unassigned
10361038/// ```
10371039///
10381040/// **Anatomy vs Health (the firewall split).** `0x0AXX` Anatomy is the
@@ -1133,6 +1135,17 @@ const CODEBOOK: &[(&str, u16)] = &[
11331135 // McClelland/Rubicon
11341136 // person lens) lives consumer-side in q2's `osint_classview.rs` — OGAR
11351137 // vocabulary carries no OSINT concept names. Do NOT re-mint rows here.
1138+ // ── 0x08XX — OCR domain (document extraction; the Tesseract-rs arc) ──
1139+ // Class-level container KINDS only: unicharset / recoder / charset are
1140+ // the container types the Core resolves. Their CONTENT (the 112 unichars
1141+ // of a trained set, the code tables) lives in content stores — never as
1142+ // concept slots. Guard precedent: the 0x07XX Osint zero-rows ruling
1143+ // (content ≠ concepts); the difference here is that OCR's container
1144+ // kinds ARE cross-app concepts (Tesseract-rs producer, lance-graph
1145+ // keystone consumer), so the kinds get slots while the content does not.
1146+ ( "unicharset" , 0x0801 ) ,
1147+ ( "recoder" , 0x0802 ) ,
1148+ ( "charset" , 0x0803 ) ,
11361149 // ── 0x09XX — Health domain (clinical / patient / care) ──
11371150 // medcare-rs Healthcare-namespace promotion (Northstar T9). The 7
11381151 // entities the OGIT `NTO/Healthcare/entities/` TTL ships, projected
@@ -1187,9 +1200,10 @@ const CODEBOOK: &[(&str, u16)] = &[
11871200 // AND the Automation actuators (`ogit.Automation:` — KnowledgeItem /
11881201 // ActionHandler / Trigger, HIRO's behavioral vocabulary). Two OGIT
11891202 // sub-namespaces, ONE concept domain — the same justification the Auth
1190- // family uses (heterogeneous shapes, one cross-app concern): the render
1191- // prefix (`ogit-mars` / `ogit-automation`) is the hi-u16 skin; the domain
1192- // byte is the lo-u16 shared-concept half. The DO arm (`ActionDef`) and the
1203+ // family uses (heterogeneous shapes, one cross-app concern): the domain
1204+ // byte is the hi-u16 shared-concept half; the render prefix
1205+ // (`ogit-mars` / `ogit-automation`) is the lo-u16 skin (canon HIGH /
1206+ // custom LOW since the 2026-07-02 half-order flip). The DO arm (`ActionDef`) and the
11931207 // THINK arm (the MARS `Class`es) meet here. This IS the codebook pass that
11941208 // `docs/MARS-TRANSCODING.md` §1 deferred ("provisional… after the codebook
11951209 // pass"); minted via the 5+3 hardening (theorem-checker / doctrine-keeper /
@@ -1215,6 +1229,13 @@ const CODEBOOK: &[(&str, u16)] = &[
12151229 ( "hr_department" , 0x0D02 ) ,
12161230 ( "hr_job" , 0x0D03 ) ,
12171231 ( "hr_employment_contract" , 0x0D04 ) ,
1232+ // ── 0x0EXX — Genetics domain (CPIC pharmacogenomics, consumed by q2):
1233+ // ZERO vocabulary rows today — same posture as the 0x07XX OSINT block
1234+ // above. The domain slot is reserved (`ConceptDomain::Genetics`) so
1235+ // `canonical_concept_domain` returns a stable tag before any concept
1236+ // mints, per the ledger commitment to the V3 marker form
1237+ // `0x0E01_1000` (`docs/DISCOVERY-MAP.md` D-CLASSID-CANON-HIGH-FLIP).
1238+ // Do NOT mint rows here without an operator ruling.
12181239] ;
12191240
12201241/// Codebook **domain** — the high byte of a canonical id (see
@@ -1269,8 +1290,18 @@ pub enum ConceptDomain {
12691290 /// `vcard:Individual` / `org:OrganizationalUnit` / `org:Role` /
12701291 /// `fibo:Contract` alignment.
12711292 HR ,
1293+ /// `0x0EXX` — Genetics (CPIC pharmacogenomics), consumed by q2. Carries
1294+ /// ZERO vocabulary rows today — same posture as [`Osint`](Self::Osint):
1295+ /// the domain slot is reserved so `canonical_concept_domain` returns a
1296+ /// stable tag before any concept mints, per the 2026-07-02 half-order
1297+ /// flip ledger commitment to the V3 marker form `0x0E01_1000` (CPIC
1298+ /// under q2, appid `0x01`; `docs/DISCOVERY-MAP.md`
1299+ /// D-CLASSID-CANON-HIGH-FLIP). Do NOT re-mint OSINT-style hallucinated
1300+ /// concept rows here — same guard as the OSINT domain note in
1301+ /// [`CODEBOOK`].
1302+ Genetics ,
12721303 /// Any high-byte slot not yet assigned a domain (`0x03XX`–`0x06XX`,
1273- /// `0x0DXX `+).
1304+ /// `0x0FXX `+).
12741305 Unassigned ,
12751306}
12761307
@@ -1289,6 +1320,7 @@ pub fn canonical_concept_domain(id: u16) -> ConceptDomain {
12891320 0x0B => ConceptDomain :: Auth ,
12901321 0x0C => ConceptDomain :: Automation ,
12911322 0x0D => ConceptDomain :: HR ,
1323+ 0x0E => ConceptDomain :: Genetics ,
12921324 _ => ConceptDomain :: Unassigned ,
12931325 }
12941326}
@@ -1538,6 +1570,25 @@ pub mod class_ids {
15381570 /// `uom.uom` (`qudt:Unit`).
15391571 pub const UNIT_OF_MEASURE : u16 = 0x020B ;
15401572
1573+ // ── 0x08XX — OCR domain (document extraction; the Tesseract-rs arc) ──
1574+ // Class-level container KINDS only: the concept slots name the container
1575+ // types the Core resolves — never their content. The 112 unichars of a
1576+ // trained unicharset are content-store rows, NOT concept slots (the
1577+ // 0x07XX Osint zero-rows ruling is the guard precedent; unlike Osint,
1578+ // OCR's container kinds ARE cross-app concepts and do get slots).
1579+
1580+ /// `unicharset` (`0x0801`) — a character-set container: the trained
1581+ /// unichar inventory a recognizer resolves against (Tesseract
1582+ /// `UNICHARSET`). The unichars themselves are content rows.
1583+ pub const UNICHARSET : u16 = 0x0801 ;
1584+ /// `recoder` (`0x0802`) — a code-compression mapping between unichar ids
1585+ /// and recognizer output codes (Tesseract `UnicharCompress`).
1586+ pub const RECODER : u16 = 0x0802 ;
1587+ /// `charset` (`0x0803`) — an encoding/character-repertoire declaration a
1588+ /// document or model asserts (distinct from the trained `unicharset`
1589+ /// inventory).
1590+ pub const CHARSET : u16 = 0x0803 ;
1591+
15411592 // ── 0x09XX — health domain (medcare-rs Healthcare namespace) ──
15421593
15431594 /// `patient` (`0x0901`) — the person receiving care. OGIT
@@ -1699,7 +1750,13 @@ pub mod class_ids {
16991750 ( "pricelist" , PRICELIST ) ,
17001751 ( "pricelist_rule" , PRICELIST_RULE ) ,
17011752 ( "unit_of_measure" , UNIT_OF_MEASURE ) ,
1702- // 0x07XX — OSINT (AIRO/AIwar dual-use intelligence)
1753+ // 0x07XX — OSINT: ZERO vocabulary rows BY DESIGN (operator ruling
1754+ // 2026-07-02; see the CODEBOOK 0x07XX section note). No entries
1755+ // follow — OGAR vocabulary carries no OSINT concept names.
1756+ // 0x08XX — OCR (container kinds only; unichar content stays out)
1757+ ( "unicharset" , UNICHARSET ) ,
1758+ ( "recoder" , RECODER ) ,
1759+ ( "charset" , CHARSET ) ,
17031760 // 0x09XX — health
17041761 ( "patient" , PATIENT ) ,
17051762 ( "diagnosis" , DIAGNOSIS ) ,
@@ -1781,6 +1838,29 @@ pub mod class_ids {
17811838 }
17821839 }
17831840
1841+ #[ test]
1842+ fn count_fuse_matches_lance_graph_ogar_mirror ( ) {
1843+ // OGAR-side half of the two-sided drift fuse. The lance-graph
1844+ // side half is `lance_graph_ogar::parity::COUNT_FUSE`
1845+ // (lance-graph `crates/lance-graph-ogar/src/lib.rs:119`), a
1846+ // compile-time assert that
1847+ // `lance_graph_contract::ogar_codebook::CODEBOOK.len() ==
1848+ // ogar_vocab::class_ids::ALL.len()`. That fuse only fires if
1849+ // OGAR's count changes without the contract mirror following —
1850+ // it cannot detect the mirror itself drifting, because it has
1851+ // no independent number to compare against on the OGAR side.
1852+ // Pin the number here too, so a change to this count is visible
1853+ // in THIS repo's CI the moment it happens, not only when the
1854+ // lance-graph mirror is rebuilt against it.
1855+ assert_eq ! (
1856+ ALL . len( ) ,
1857+ 68 ,
1858+ "class_ids::ALL count changed — update this pin AND the \
1859+ lance-graph mirror COUNT_FUSE (crates/lance-graph-ogar/src/lib.rs) \
1860+ in the same PR",
1861+ ) ;
1862+ }
1863+
17841864 #[ test]
17851865 fn divergent_curator_names_share_one_constant ( ) {
17861866 // The whole point of the codebook, in code: a Redmine Issue
@@ -2591,8 +2671,15 @@ pub fn all_promoted_classes() -> Vec<Class> {
25912671 pricelist( ) ,
25922672 pricelist_rule( ) ,
25932673 unit_of_measure( ) ,
2594- // 0x07XX — OSINT arm (AIRO/AIwar dual-use: system + person cards),
2595- // in class_ids::ALL order.
2674+ // 0x07XX — OSINT arm: ZERO vocabulary rows BY DESIGN (operator
2675+ // ruling 2026-07-02, corrects PR #145's hallucinated
2676+ // `osint_system` / `osint_person` mints); no calls follow — OGAR
2677+ // vocabulary carries no OSINT concept names, see the CODEBOOK
2678+ // 0x07XX section note.
2679+ // 0x08XX — OCR arm (3 container kinds), in class_ids::ALL order.
2680+ unicharset( ) ,
2681+ recoder( ) ,
2682+ charset( ) ,
25962683 // 0x09XX — health arm (7 OGIT Healthcare concepts), in
25972684 // class_ids::ALL order.
25982685 patient( ) ,
@@ -3530,6 +3617,55 @@ pub fn unit_of_measure() -> Class {
35303617 c
35313618}
35323619
3620+ // ─────────────────────────────────────────────────────────────────────
3621+ // 0x08XX — OCR domain (document extraction; the Tesseract-rs arc).
3622+ // Class-level container KINDS only — the concept slots name the container
3623+ // types the Core resolves; their content (the 112 unichars of a trained
3624+ // set, the code tables) lives in content stores, never as concept slots
3625+ // (Osint zero-rows ruling is the guard precedent).
3626+ // ─────────────────────────────────────────────────────────────────────
3627+
3628+ /// Unicharset (`0x0801`) — a trained character-set container: the unichar
3629+ /// inventory a recognizer resolves against (Tesseract `UNICHARSET`). The
3630+ /// unichars themselves are content-store rows under this concept, the same
3631+ /// way the ~206 bones are cascade-path nodes under `bone`, not slots.
3632+ #[ must_use]
3633+ pub fn unicharset ( ) -> Class {
3634+ let mut c = Class :: new ( "Unicharset" ) ;
3635+ c. language = Language :: Unknown ;
3636+ c. canonical_concept = Some ( "unicharset" . to_string ( ) ) ;
3637+ let mut size = Attribute :: new ( "size" ) ; // number of unichars in the set
3638+ size. type_name = Some ( "integer" . to_string ( ) ) ;
3639+ c. attributes = vec ! [ size] ;
3640+ c
3641+ }
3642+
3643+ /// Recoder (`0x0802`) — the code-compression mapping between unichar ids
3644+ /// and recognizer output codes (Tesseract `UnicharCompress`). Compresses a
3645+ /// [`unicharset`]'s inventory; the code tables are content, not slots.
3646+ #[ must_use]
3647+ pub fn recoder ( ) -> Class {
3648+ let mut c = Class :: new ( "Recoder" ) ;
3649+ c. language = Language :: Unknown ;
3650+ c. canonical_concept = Some ( "recoder" . to_string ( ) ) ;
3651+ c. associations = vec ! [ family_edge( "compresses" , "Unicharset" ) ] ;
3652+ c
3653+ }
3654+
3655+ /// Charset (`0x0803`) — an encoding / character-repertoire declaration a
3656+ /// document or model asserts (distinct from the trained [`unicharset`]
3657+ /// inventory it may be realized by).
3658+ #[ must_use]
3659+ pub fn charset ( ) -> Class {
3660+ let mut c = Class :: new ( "Charset" ) ;
3661+ c. language = Language :: Unknown ;
3662+ c. canonical_concept = Some ( "charset" . to_string ( ) ) ;
3663+ let mut encoding = Attribute :: new ( "encoding" ) ;
3664+ encoding. type_name = Some ( "string" . to_string ( ) ) ;
3665+ c. attributes = vec ! [ encoding] ;
3666+ c
3667+ }
3668+
35333669// ─────────────────────────────────────────────────────────────────────
35343670// 0x09XX — Health domain (OGIT Healthcare). The reusable Active-Record
35353671// shape for the clinical concepts. `diagnosis` (0x0902) is the worked
@@ -4720,10 +4856,16 @@ mod tests {
47204856 // Automation block (0x0C) — HIRO IT-automation stack.
47214857 assert_eq ! ( canonical_concept_domain( 0x0C00 ) , ConceptDomain :: Automation ) ;
47224858 assert_eq ! ( canonical_concept_domain( 0x0C09 ) , ConceptDomain :: Automation ) ;
4723- // Unassigned blocks (3-6, D+ ).
4859+ // Unassigned blocks (3-6).
47244860 assert_eq ! ( canonical_concept_domain( 0x0300 ) , ConceptDomain :: Unassigned ) ;
47254861 assert_eq ! ( canonical_concept_domain( 0x0600 ) , ConceptDomain :: Unassigned ) ;
4862+ // HR block (0x0D).
47264863 assert_eq ! ( canonical_concept_domain( 0x0D00 ) , ConceptDomain :: HR ) ;
4864+ // Genetics block (0x0E) — reserved, zero concept rows today (item-3
4865+ // mint, `docs/DISCOVERY-MAP.md` D-CLASSID-CANON-HIGH-FLIP).
4866+ assert_eq ! ( canonical_concept_domain( 0x0E01 ) , ConceptDomain :: Genetics ) ;
4867+ assert_eq ! ( canonical_concept_domain( 0x0EFF ) , ConceptDomain :: Genetics ) ;
4868+ // Trailing unassigned tail (0x0F+).
47274869 assert_eq ! ( canonical_concept_domain( 0xFFFF ) , ConceptDomain :: Unassigned ) ;
47284870 }
47294871
@@ -4822,6 +4964,18 @@ mod tests {
48224964 }
48234965 // Counts line up with the codebook blocks.
48244966 assert_eq ! ( concepts_in_domain( ConceptDomain :: Health ) . count( ) , 7 ) ;
4967+ // 0x08XX OCR: the three container KINDS (unicharset/recoder/charset).
4968+ // Content (the 112 unichars, code tables) never becomes concepts —
4969+ // see the CODEBOOK 0x08XX section note (Osint zero-rows precedent).
4970+ assert_eq ! ( concepts_in_domain( ConceptDomain :: Ocr ) . count( ) , 3 ) ;
4971+ let ocr: Vec < & str > = concepts_in_domain ( ConceptDomain :: Ocr )
4972+ . map ( |( name, _) | name)
4973+ . collect ( ) ;
4974+ assert_eq ! (
4975+ ocr,
4976+ [ "unicharset" , "recoder" , "charset" ] ,
4977+ "OCR domain set drift — re-sync the consumer coverage gate" ,
4978+ ) ;
48254979 assert_eq ! ( concepts_in_domain( ConceptDomain :: HR ) . count( ) , 4 ) ;
48264980 assert_eq ! ( concepts_in_domain( ConceptDomain :: Commerce ) . count( ) , 11 ) ;
48274981 assert_eq ! ( concepts_in_domain( ConceptDomain :: ProjectMgmt ) . count( ) , 26 ) ;
@@ -4851,6 +5005,10 @@ mod tests {
48515005 // ruling 2026-07-02): its low byte is appid space (q2 = 0x01), not a
48525006 // concept slot — see the CODEBOOK 0x07XX section note.
48535007 assert_eq ! ( concepts_in_domain( ConceptDomain :: Osint ) . count( ) , 0 ) ;
5008+ // Same posture for the Genetics domain (0x0E, CPIC pharmacogenomics
5009+ // under q2) — reserved, zero concept rows until an operator ruling
5010+ // mints one — see the CODEBOOK 0x0EXX section note.
5011+ assert_eq ! ( concepts_in_domain( ConceptDomain :: Genetics ) . count( ) , 0 ) ;
48545012 }
48555013
48565014 #[ test]
0 commit comments