Skip to content

Commit 75d955b

Browse files
authored
Merge pull request #148 from AdaWorldAPI/claude/v3-substrate-migration-review-o0yoxv
Cross-repo fuses + serialized allocation batch (flip fuse, COUNT_FUSE, Genetics 0x0E, OCR 0x08XX, 0x1000 reservation) + post-flip prose sweep
2 parents 19373a2 + 66316fa commit 75d955b

6 files changed

Lines changed: 296 additions & 15 deletions

File tree

crates/ogar-class-view/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "ogar-class-view"
33
version.workspace = true
44
edition.workspace = true
55
license.workspace = true
6-
description = "Bridge crate: lifts `ogar_vocab::Class` (canonical AR shape, codebook-keyed) onto `lance_graph_contract::ClassView` (presence-bitmask + render-row resolver). The seam between the calcified inner shape and the classview-parameterized outer materialization (askama/jinja). One trait impl over the 32 promoted canonical concepts; no I/O."
6+
description = "Bridge crate: lifts `ogar_vocab::Class` (canonical AR shape, codebook-keyed) onto `lance_graph_contract::ClassView` (presence-bitmask + render-row resolver). The seam between the calcified inner shape and the classview-parameterized outer materialization (askama/jinja). One trait impl over the 68 promoted canonical concepts; no I/O."
77

88
[dependencies]
99
ogar-vocab = { path = "../ogar-vocab" }

crates/ogar-class-view/src/lib.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ use ogar_vocab::{
7979
billing_party,
8080
bone,
8181
canonical_concept_id,
82+
charset,
8283
commercial_document,
8384
commercial_line_item,
8485
currency_policy,
@@ -126,9 +127,11 @@ use ogar_vocab::{
126127
project_watcher,
127128
project_wiki_page,
128129
project_work_item,
130+
recoder,
129131
skeleton,
130132
tax_policy,
131133
treatment,
134+
unicharset,
132135
unit_of_measure,
133136
visit,
134137
vital_sign,
@@ -181,6 +184,10 @@ fn all_canonical_classes() -> Vec<(&'static str, Class)> {
181184
("pricelist", pricelist()),
182185
("pricelist_rule", pricelist_rule()),
183186
("unit_of_measure", unit_of_measure()),
187+
// ── 0x08XX — OCR (container kinds; content stays in content stores) ──
188+
("unicharset", unicharset()),
189+
("recoder", recoder()),
190+
("charset", charset()),
184191
// ── 0x09XX — health (OGIT Healthcare) ──
185192
("patient", patient()),
186193
("diagnosis", diagnosis()),
@@ -340,6 +347,39 @@ mod tests {
340347
use super::*;
341348
use lance_graph_contract::class_view::FieldMask;
342349

350+
#[test]
351+
fn classid_order_agrees_with_lance_graph_contract_canon_high() {
352+
// The two-sided flip fuse: OGAR's `ogar_vocab::app::{app_of,
353+
// concept_of}` (PR #147) and lance-graph-contract's
354+
// `ogar_codebook::split_classid` under `ClassidOrder::CanonHigh`
355+
// (PR #628/#630) must agree bit-for-bit on the same composed
356+
// literal, or a one-sided revert on either side goes silent —
357+
// this crate is the only place in OGAR that links
358+
// `lance-graph-contract`, so it is the natural home for the
359+
// lockstep pin. Known-good literal: openproject:WorkPackage
360+
// composes to `0x0102_0001` (concept hi=0x0102, app lo=0x0001).
361+
use lance_graph_contract::ogar_codebook::{CLASSID_ORDER, ClassidOrder, split_classid};
362+
363+
assert_eq!(CLASSID_ORDER, ClassidOrder::CanonHigh);
364+
365+
let classid =
366+
ogar_vocab::app::render_classid(0x0001, ogar_vocab::class_ids::PROJECT_WORK_ITEM);
367+
assert_eq!(classid, 0x0102_0001);
368+
369+
let ogar_app = ogar_vocab::app::app_of(classid);
370+
let ogar_concept = ogar_vocab::app::concept_of(classid);
371+
let (contract_canon, contract_custom) = split_classid(classid);
372+
373+
assert_eq!(
374+
ogar_concept, contract_canon,
375+
"OGAR concept half must equal lance-graph-contract's canon half",
376+
);
377+
assert_eq!(
378+
ogar_app, contract_custom,
379+
"OGAR app half must equal lance-graph-contract's custom half",
380+
);
381+
}
382+
343383
#[test]
344384
fn registry_carries_every_codebook_concept() {
345385
// Forward gate: every concept in `all_canonical_classes` resolves

crates/ogar-vocab/src/lib.rs

Lines changed: 169 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)