From 66316fac5a73a7fc2b45afd004eefa25454ffe08 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 2 Jul 2026 14:08:14 +0000 Subject: [PATCH] vocab+class-view: cross-repo fuses + serialized allocation batch (flip fuse, COUNT_FUSE, Genetics 0x0E, OCR 0x08XX, 0x1000 reservation) + post-flip prose sweep MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit One batched arc per the cross-session convergence ruling (serialize the allocation-table mints; every consumer owns its classids and trips a wire when out of sync): - ogar-class-view: classid_order_agrees_with_lance_graph_contract_canon_high — the #628<->#147 lockstep fuse, pinning ogar_vocab::app::{app_of, concept_of} against contract split_classid/CanonHigh on 0x0102_0001. Floating branch=main dep is a FEATURE here: the fuse tests the moving tip, so a one-sided revert fails within a build. - ogar-vocab: count_fuse_matches_lance_graph_ogar_mirror — the OGAR-side half of lance_graph_ogar::parity::COUNT_FUSE, pinning ALL.len() (now 68). - Mints: ConceptDomain::Genetics (0x0E, zero rows — CPIC ledger commitment); OCR 0x08XX container kinds (unicharset/recoder/charset — content never becomes concepts, Osint zero-rows precedent); 0x1000 pinned RESERVED never-a-port-prefix in the allocation-table test. - class-view registry lifts the 3 OCR kinds; reverse/forward gates green (ogar-vocab 96/96, ogar-class-view 12/12 vs contract main c7149eab). - Post-flip prose sweep: 6 sites where order/count prose rotted against code (Automation comment stated the flip backwards; ports.rs test prose; capstone doc; 0x0D/0x08/0x09 doc-table rows; class-view Cargo.toml concept count). - DISCOVERY-MAP: D-TRUNCATION-DISALLOWED-SOC-REROUTE (mirrored from lance-graph + ruff soc.rs) and D-CLASSID-HI-U16-SPELLING (operator confirmation: domain:appid:classview; the app-byte vs APP_PREFIX homonym warning). Lockstep note: merge THIS before the lance-graph PR carrying the contract mirror's matching 0x08XX rows; lance-graph then bumps its ogar-vocab lock pin. COUNT_FUSE firing in between is the fuse working. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01MLBnPuScZy6w9di2QEjsXM --- crates/ogar-class-view/Cargo.toml | 2 +- crates/ogar-class-view/src/lib.rs | 40 +++++ crates/ogar-vocab/src/lib.rs | 180 ++++++++++++++++++++-- crates/ogar-vocab/src/ports.rs | 28 +++- docs/DISCOVERY-MAP.md | 58 +++++++ docs/OGAR-SEMANTIC-TRANSPILER-CAPSTONE.md | 3 +- 6 files changed, 296 insertions(+), 15 deletions(-) diff --git a/crates/ogar-class-view/Cargo.toml b/crates/ogar-class-view/Cargo.toml index 71cd07c..acd7bd1 100644 --- a/crates/ogar-class-view/Cargo.toml +++ b/crates/ogar-class-view/Cargo.toml @@ -3,7 +3,7 @@ name = "ogar-class-view" version.workspace = true edition.workspace = true license.workspace = true -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." +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." [dependencies] ogar-vocab = { path = "../ogar-vocab" } diff --git a/crates/ogar-class-view/src/lib.rs b/crates/ogar-class-view/src/lib.rs index 03cb1b6..2d89627 100644 --- a/crates/ogar-class-view/src/lib.rs +++ b/crates/ogar-class-view/src/lib.rs @@ -79,6 +79,7 @@ use ogar_vocab::{ billing_party, bone, canonical_concept_id, + charset, commercial_document, commercial_line_item, currency_policy, @@ -126,9 +127,11 @@ use ogar_vocab::{ project_watcher, project_wiki_page, project_work_item, + recoder, skeleton, tax_policy, treatment, + unicharset, unit_of_measure, visit, vital_sign, @@ -181,6 +184,10 @@ fn all_canonical_classes() -> Vec<(&'static str, Class)> { ("pricelist", pricelist()), ("pricelist_rule", pricelist_rule()), ("unit_of_measure", unit_of_measure()), + // ── 0x08XX — OCR (container kinds; content stays in content stores) ── + ("unicharset", unicharset()), + ("recoder", recoder()), + ("charset", charset()), // ── 0x09XX — health (OGIT Healthcare) ── ("patient", patient()), ("diagnosis", diagnosis()), @@ -340,6 +347,39 @@ mod tests { use super::*; use lance_graph_contract::class_view::FieldMask; + #[test] + fn classid_order_agrees_with_lance_graph_contract_canon_high() { + // The two-sided flip fuse: OGAR's `ogar_vocab::app::{app_of, + // concept_of}` (PR #147) and lance-graph-contract's + // `ogar_codebook::split_classid` under `ClassidOrder::CanonHigh` + // (PR #628/#630) must agree bit-for-bit on the same composed + // literal, or a one-sided revert on either side goes silent — + // this crate is the only place in OGAR that links + // `lance-graph-contract`, so it is the natural home for the + // lockstep pin. Known-good literal: openproject:WorkPackage + // composes to `0x0102_0001` (concept hi=0x0102, app lo=0x0001). + use lance_graph_contract::ogar_codebook::{CLASSID_ORDER, ClassidOrder, split_classid}; + + assert_eq!(CLASSID_ORDER, ClassidOrder::CanonHigh); + + let classid = + ogar_vocab::app::render_classid(0x0001, ogar_vocab::class_ids::PROJECT_WORK_ITEM); + assert_eq!(classid, 0x0102_0001); + + let ogar_app = ogar_vocab::app::app_of(classid); + let ogar_concept = ogar_vocab::app::concept_of(classid); + let (contract_canon, contract_custom) = split_classid(classid); + + assert_eq!( + ogar_concept, contract_canon, + "OGAR concept half must equal lance-graph-contract's canon half", + ); + assert_eq!( + ogar_app, contract_custom, + "OGAR app half must equal lance-graph-contract's custom half", + ); + } + #[test] fn registry_carries_every_codebook_concept() { // Forward gate: every concept in `all_canonical_classes` resolves diff --git a/crates/ogar-vocab/src/lib.rs b/crates/ogar-vocab/src/lib.rs index ae526b7..9d6042e 100644 --- a/crates/ogar-vocab/src/lib.rs +++ b/crates/ogar-vocab/src/lib.rs @@ -1027,12 +1027,14 @@ impl Class { /// 0x05XX unassigned /// 0x06XX unassigned /// 0x07XX reserved: OSINT -/// 0x08XX reserved: OCR -/// 0x09XX reserved: Health +/// 0x08XX OCR (container kinds: unicharset/recoder/charset) +/// 0x09XX Health (clinical / patient / care; 7 OGIT entities) /// 0x0AXX Anatomy (FMA reference ontology; bones/skeleton) /// 0x0BXX Auth (IAM; the AuthStore class family) /// 0x0CXX Automation (HIRO IT-automation: MARS CMDB + actuators) -/// 0x0DXX+ unassigned +/// 0x0DXX HR (employment / org / contracts) +/// 0x0EXX reserved: Genetics (CPIC pharmacogenomics, consumed by q2) +/// 0x0FXX+ unassigned /// ``` /// /// **Anatomy vs Health (the firewall split).** `0x0AXX` Anatomy is the @@ -1133,6 +1135,17 @@ const CODEBOOK: &[(&str, u16)] = &[ // McClelland/Rubicon // person lens) lives consumer-side in q2's `osint_classview.rs` — OGAR // vocabulary carries no OSINT concept names. Do NOT re-mint rows here. + // ── 0x08XX — OCR domain (document extraction; the Tesseract-rs arc) ── + // Class-level container KINDS only: unicharset / recoder / charset are + // the container types the Core resolves. Their CONTENT (the 112 unichars + // of a trained set, the code tables) lives in content stores — never as + // concept slots. Guard precedent: the 0x07XX Osint zero-rows ruling + // (content ≠ concepts); the difference here is that OCR's container + // kinds ARE cross-app concepts (Tesseract-rs producer, lance-graph + // keystone consumer), so the kinds get slots while the content does not. + ("unicharset", 0x0801), + ("recoder", 0x0802), + ("charset", 0x0803), // ── 0x09XX — Health domain (clinical / patient / care) ── // medcare-rs Healthcare-namespace promotion (Northstar T9). The 7 // entities the OGIT `NTO/Healthcare/entities/` TTL ships, projected @@ -1187,9 +1200,10 @@ const CODEBOOK: &[(&str, u16)] = &[ // AND the Automation actuators (`ogit.Automation:` — KnowledgeItem / // ActionHandler / Trigger, HIRO's behavioral vocabulary). Two OGIT // sub-namespaces, ONE concept domain — the same justification the Auth - // family uses (heterogeneous shapes, one cross-app concern): the render - // prefix (`ogit-mars` / `ogit-automation`) is the hi-u16 skin; the domain - // byte is the lo-u16 shared-concept half. The DO arm (`ActionDef`) and the + // family uses (heterogeneous shapes, one cross-app concern): the domain + // byte is the hi-u16 shared-concept half; the render prefix + // (`ogit-mars` / `ogit-automation`) is the lo-u16 skin (canon HIGH / + // custom LOW since the 2026-07-02 half-order flip). The DO arm (`ActionDef`) and the // THINK arm (the MARS `Class`es) meet here. This IS the codebook pass that // `docs/MARS-TRANSCODING.md` §1 deferred ("provisional… after the codebook // pass"); minted via the 5+3 hardening (theorem-checker / doctrine-keeper / @@ -1215,6 +1229,13 @@ const CODEBOOK: &[(&str, u16)] = &[ ("hr_department", 0x0D02), ("hr_job", 0x0D03), ("hr_employment_contract", 0x0D04), + // ── 0x0EXX — Genetics domain (CPIC pharmacogenomics, consumed by q2): + // ZERO vocabulary rows today — same posture as the 0x07XX OSINT block + // above. The domain slot is reserved (`ConceptDomain::Genetics`) so + // `canonical_concept_domain` returns a stable tag before any concept + // mints, per the ledger commitment to the V3 marker form + // `0x0E01_1000` (`docs/DISCOVERY-MAP.md` D-CLASSID-CANON-HIGH-FLIP). + // Do NOT mint rows here without an operator ruling. ]; /// Codebook **domain** — the high byte of a canonical id (see @@ -1269,8 +1290,18 @@ pub enum ConceptDomain { /// `vcard:Individual` / `org:OrganizationalUnit` / `org:Role` / /// `fibo:Contract` alignment. HR, + /// `0x0EXX` — Genetics (CPIC pharmacogenomics), consumed by q2. Carries + /// ZERO vocabulary rows today — same posture as [`Osint`](Self::Osint): + /// the domain slot is reserved so `canonical_concept_domain` returns a + /// stable tag before any concept mints, per the 2026-07-02 half-order + /// flip ledger commitment to the V3 marker form `0x0E01_1000` (CPIC + /// under q2, appid `0x01`; `docs/DISCOVERY-MAP.md` + /// D-CLASSID-CANON-HIGH-FLIP). Do NOT re-mint OSINT-style hallucinated + /// concept rows here — same guard as the OSINT domain note in + /// [`CODEBOOK`]. + Genetics, /// Any high-byte slot not yet assigned a domain (`0x03XX`–`0x06XX`, - /// `0x0DXX`+). + /// `0x0FXX`+). Unassigned, } @@ -1289,6 +1320,7 @@ pub fn canonical_concept_domain(id: u16) -> ConceptDomain { 0x0B => ConceptDomain::Auth, 0x0C => ConceptDomain::Automation, 0x0D => ConceptDomain::HR, + 0x0E => ConceptDomain::Genetics, _ => ConceptDomain::Unassigned, } } @@ -1538,6 +1570,25 @@ pub mod class_ids { /// `uom.uom` (`qudt:Unit`). pub const UNIT_OF_MEASURE: u16 = 0x020B; + // ── 0x08XX — OCR domain (document extraction; the Tesseract-rs arc) ── + // Class-level container KINDS only: the concept slots name the container + // types the Core resolves — never their content. The 112 unichars of a + // trained unicharset are content-store rows, NOT concept slots (the + // 0x07XX Osint zero-rows ruling is the guard precedent; unlike Osint, + // OCR's container kinds ARE cross-app concepts and do get slots). + + /// `unicharset` (`0x0801`) — a character-set container: the trained + /// unichar inventory a recognizer resolves against (Tesseract + /// `UNICHARSET`). The unichars themselves are content rows. + pub const UNICHARSET: u16 = 0x0801; + /// `recoder` (`0x0802`) — a code-compression mapping between unichar ids + /// and recognizer output codes (Tesseract `UnicharCompress`). + pub const RECODER: u16 = 0x0802; + /// `charset` (`0x0803`) — an encoding/character-repertoire declaration a + /// document or model asserts (distinct from the trained `unicharset` + /// inventory). + pub const CHARSET: u16 = 0x0803; + // ── 0x09XX — health domain (medcare-rs Healthcare namespace) ── /// `patient` (`0x0901`) — the person receiving care. OGIT @@ -1699,7 +1750,13 @@ pub mod class_ids { ("pricelist", PRICELIST), ("pricelist_rule", PRICELIST_RULE), ("unit_of_measure", UNIT_OF_MEASURE), - // 0x07XX — OSINT (AIRO/AIwar dual-use intelligence) + // 0x07XX — OSINT: ZERO vocabulary rows BY DESIGN (operator ruling + // 2026-07-02; see the CODEBOOK 0x07XX section note). No entries + // follow — OGAR vocabulary carries no OSINT concept names. + // 0x08XX — OCR (container kinds only; unichar content stays out) + ("unicharset", UNICHARSET), + ("recoder", RECODER), + ("charset", CHARSET), // 0x09XX — health ("patient", PATIENT), ("diagnosis", DIAGNOSIS), @@ -1781,6 +1838,29 @@ pub mod class_ids { } } + #[test] + fn count_fuse_matches_lance_graph_ogar_mirror() { + // OGAR-side half of the two-sided drift fuse. The lance-graph + // side half is `lance_graph_ogar::parity::COUNT_FUSE` + // (lance-graph `crates/lance-graph-ogar/src/lib.rs:119`), a + // compile-time assert that + // `lance_graph_contract::ogar_codebook::CODEBOOK.len() == + // ogar_vocab::class_ids::ALL.len()`. That fuse only fires if + // OGAR's count changes without the contract mirror following — + // it cannot detect the mirror itself drifting, because it has + // no independent number to compare against on the OGAR side. + // Pin the number here too, so a change to this count is visible + // in THIS repo's CI the moment it happens, not only when the + // lance-graph mirror is rebuilt against it. + assert_eq!( + ALL.len(), + 68, + "class_ids::ALL count changed — update this pin AND the \ + lance-graph mirror COUNT_FUSE (crates/lance-graph-ogar/src/lib.rs) \ + in the same PR", + ); + } + #[test] fn divergent_curator_names_share_one_constant() { // The whole point of the codebook, in code: a Redmine Issue @@ -2591,8 +2671,15 @@ pub fn all_promoted_classes() -> Vec { pricelist(), pricelist_rule(), unit_of_measure(), - // 0x07XX — OSINT arm (AIRO/AIwar dual-use: system + person cards), - // in class_ids::ALL order. + // 0x07XX — OSINT arm: ZERO vocabulary rows BY DESIGN (operator + // ruling 2026-07-02, corrects PR #145's hallucinated + // `osint_system` / `osint_person` mints); no calls follow — OGAR + // vocabulary carries no OSINT concept names, see the CODEBOOK + // 0x07XX section note. + // 0x08XX — OCR arm (3 container kinds), in class_ids::ALL order. + unicharset(), + recoder(), + charset(), // 0x09XX — health arm (7 OGIT Healthcare concepts), in // class_ids::ALL order. patient(), @@ -3530,6 +3617,55 @@ pub fn unit_of_measure() -> Class { c } +// ───────────────────────────────────────────────────────────────────── +// 0x08XX — OCR domain (document extraction; the Tesseract-rs arc). +// Class-level container KINDS only — the concept slots name the container +// types the Core resolves; their content (the 112 unichars of a trained +// set, the code tables) lives in content stores, never as concept slots +// (Osint zero-rows ruling is the guard precedent). +// ───────────────────────────────────────────────────────────────────── + +/// Unicharset (`0x0801`) — a trained character-set container: the unichar +/// inventory a recognizer resolves against (Tesseract `UNICHARSET`). The +/// unichars themselves are content-store rows under this concept, the same +/// way the ~206 bones are cascade-path nodes under `bone`, not slots. +#[must_use] +pub fn unicharset() -> Class { + let mut c = Class::new("Unicharset"); + c.language = Language::Unknown; + c.canonical_concept = Some("unicharset".to_string()); + let mut size = Attribute::new("size"); // number of unichars in the set + size.type_name = Some("integer".to_string()); + c.attributes = vec![size]; + c +} + +/// Recoder (`0x0802`) — the code-compression mapping between unichar ids +/// and recognizer output codes (Tesseract `UnicharCompress`). Compresses a +/// [`unicharset`]'s inventory; the code tables are content, not slots. +#[must_use] +pub fn recoder() -> Class { + let mut c = Class::new("Recoder"); + c.language = Language::Unknown; + c.canonical_concept = Some("recoder".to_string()); + c.associations = vec![family_edge("compresses", "Unicharset")]; + c +} + +/// Charset (`0x0803`) — an encoding / character-repertoire declaration a +/// document or model asserts (distinct from the trained [`unicharset`] +/// inventory it may be realized by). +#[must_use] +pub fn charset() -> Class { + let mut c = Class::new("Charset"); + c.language = Language::Unknown; + c.canonical_concept = Some("charset".to_string()); + let mut encoding = Attribute::new("encoding"); + encoding.type_name = Some("string".to_string()); + c.attributes = vec![encoding]; + c +} + // ───────────────────────────────────────────────────────────────────── // 0x09XX — Health domain (OGIT Healthcare). The reusable Active-Record // shape for the clinical concepts. `diagnosis` (0x0902) is the worked @@ -4720,10 +4856,16 @@ mod tests { // Automation block (0x0C) — HIRO IT-automation stack. assert_eq!(canonical_concept_domain(0x0C00), ConceptDomain::Automation); assert_eq!(canonical_concept_domain(0x0C09), ConceptDomain::Automation); - // Unassigned blocks (3-6, D+). + // Unassigned blocks (3-6). assert_eq!(canonical_concept_domain(0x0300), ConceptDomain::Unassigned); assert_eq!(canonical_concept_domain(0x0600), ConceptDomain::Unassigned); + // HR block (0x0D). assert_eq!(canonical_concept_domain(0x0D00), ConceptDomain::HR); + // Genetics block (0x0E) — reserved, zero concept rows today (item-3 + // mint, `docs/DISCOVERY-MAP.md` D-CLASSID-CANON-HIGH-FLIP). + assert_eq!(canonical_concept_domain(0x0E01), ConceptDomain::Genetics); + assert_eq!(canonical_concept_domain(0x0EFF), ConceptDomain::Genetics); + // Trailing unassigned tail (0x0F+). assert_eq!(canonical_concept_domain(0xFFFF), ConceptDomain::Unassigned); } @@ -4822,6 +4964,18 @@ mod tests { } // Counts line up with the codebook blocks. assert_eq!(concepts_in_domain(ConceptDomain::Health).count(), 7); + // 0x08XX OCR: the three container KINDS (unicharset/recoder/charset). + // Content (the 112 unichars, code tables) never becomes concepts — + // see the CODEBOOK 0x08XX section note (Osint zero-rows precedent). + assert_eq!(concepts_in_domain(ConceptDomain::Ocr).count(), 3); + let ocr: Vec<&str> = concepts_in_domain(ConceptDomain::Ocr) + .map(|(name, _)| name) + .collect(); + assert_eq!( + ocr, + ["unicharset", "recoder", "charset"], + "OCR domain set drift — re-sync the consumer coverage gate", + ); assert_eq!(concepts_in_domain(ConceptDomain::HR).count(), 4); assert_eq!(concepts_in_domain(ConceptDomain::Commerce).count(), 11); assert_eq!(concepts_in_domain(ConceptDomain::ProjectMgmt).count(), 26); @@ -4851,6 +5005,10 @@ mod tests { // ruling 2026-07-02): its low byte is appid space (q2 = 0x01), not a // concept slot — see the CODEBOOK 0x07XX section note. assert_eq!(concepts_in_domain(ConceptDomain::Osint).count(), 0); + // Same posture for the Genetics domain (0x0E, CPIC pharmacogenomics + // under q2) — reserved, zero concept rows until an operator ruling + // mints one — see the CODEBOOK 0x0EXX section note. + assert_eq!(concepts_in_domain(ConceptDomain::Genetics).count(), 0); } #[test] diff --git a/crates/ogar-vocab/src/ports.rs b/crates/ogar-vocab/src/ports.rs index 42ce820..94c0a3d 100644 --- a/crates/ogar-vocab/src/ports.rs +++ b/crates/ogar-vocab/src/ports.rs @@ -1053,7 +1053,8 @@ mod tests { /// Pins all six APP_PREFIX overrides against the §2 allocation table /// in `APP-CLASS-CODEBOOK-LAYOUT.md`. The default in the trait is /// `0x0000` (shared canonical core); each app port overrides with its - /// reserved high-u16 so consumers can re-export the typed constant + /// reserved low-u16 (canon HIGH / custom LOW since the 2026-07-02 + /// half-order flip) so consumers can re-export the typed constant /// instead of hardcoding hex literals. /// /// Reserving a prefix costs nothing (§2): no codebook is materialised @@ -1087,7 +1088,30 @@ mod tests { // The trait default (0x0000 = shared core) is expressed directly // in the trait definition; ports that do not override it resolve // to the core codebook namespace, which is the bootstrap/core - // prefix per §2 ("hi = 0x0000 is the bootstrap/core prefix"). + // prefix per §2 ("lo = 0x0000 is the bootstrap/core prefix" — + // canon HIGH / custom LOW since the 2026-07-02 half-order flip). + // + // 0x1000 is RESERVED — the V3-adoption monitor marker (the custom + // half stamped on legacy stored classids, e.g. `0x0701_1000` OSINT + // / `0x0A01_1000` FMA / `0x0E01_1000` CPIC — + // `docs/DISCOVERY-MAP.md` D-CLASSID-CANON-HIGH-FLIP). It must never + // be allocatable as a port's APP_PREFIX. + for prefix in [ + OpenProjectPort::APP_PREFIX, + OdooPort::APP_PREFIX, + WoaPort::APP_PREFIX, + SmbPort::APP_PREFIX, + HealthcarePort::APP_PREFIX, + RedminePort::APP_PREFIX, + ] { + assert_ne!( + prefix, 0x1000, + "0x1000 is reserved for the V3-adoption monitor marker, \ + never a port APP_PREFIX", + ); + } + // NOTE: q2 does not get an APP_PREFIX row here by design — its + // render-prefix allocation awaits an operator naming ruling. } /// **The five-way `billable_work_entry` convergence pin.** Ratifies diff --git a/docs/DISCOVERY-MAP.md b/docs/DISCOVERY-MAP.md index 3c14587..95e5022 100644 --- a/docs/DISCOVERY-MAP.md +++ b/docs/DISCOVERY-MAP.md @@ -652,3 +652,61 @@ isolation. The map's job is to keep them visible. `PHILOSOPHY.md`/`PHILOSOPHIE.md`, `README.md`/`README.de.md`, `integration/AR-OGAR-MAILBOX-INTEGRATION-PLAN.md` §7, and this repo's `CLAUDE.md`. + +--- + +- **D-TRUNCATION-DISALLOWED-SOC-REROUTE (truncation-disallowed / + overflow-as-SoC-reroute doctrine; 2026-07-02; [G], mirrored from + lance-graph):** names/entries over the 256-slot cascade-tier + cardinality are NEVER truncated; overflow is rerouted as a + separation-of-concerns split, never a silent drop and never a + field-widen. This operationalizes OGAR's own `256-cap-is-a-lint` law + stated at the top of this file (`scale = the next cascade level, + never field-widening`) — a bucket/tier that exceeds its 256-slot + cardinality is a DESIGN smell, and the overflow itself is the reroute + signal (split the overflowing class into sub-concerns, or escalate to + the next family/basin), not a minter limitation to "fix" with a bigger + packer. Doctrine statement: lance-graph + `.claude/knowledge/ast-as-partof-isa-address.md` ("Truncation is + DISALLOWED; bucket overflow is a separation-of-concerns REROUTE + trigger") + lance-graph `.claude/board/EPIPHANIES.md` + E-BRICK3-RAN-TRUNCATION-DISALLOWED (2026-07-01, operator doctrine: + "truncations were disallowed / we introduced bucket overflow with + separation of concerns as a trigger for rerouting"). **Shipped + implementation** (the reason this is graded `[G]` rather than `[H]`): + `ruff` `crates/ruff_spo_address/src/soc.rs` — + `MAX_SIBLINGS_PER_TIER: usize = u8::MAX as usize` (255, the + byte-cardinality ceiling shared with the per-tier sibling rank), + `SocVerdict::{Duplication, Conflation, DuplicationAndConflation, + Counterexample}`, and `law_holds` as the falsifier (`false` iff some + over-cap class is neither type-collapsible nor + data⊥behaviour-mixed). Division of labour: `mint_factored` + (lance-graph's rank-minter, per the brick-3 falsification of the naive + fixed-width 6-tier packer) handles **addressing precision**; + overflow→SoC-reroute (`ruff::soc`) handles **structure** — together: + zero truncation, zero collision, over-cap classes flagged for split + rather than silently mangled. + +- **D-CLASSID-HI-U16-SPELLING (operator confirmation; 2026-07-02; [G], + recorded in both ledgers same-arc):** the composed classid u32 reads + **`domain : appid : classview`** — canon hi u16 = `domain byte ++ + appid byte`, custom lo u16 = the ClassView selector — and **"concept" + NAMES the whole hi u16** while `domain:appid` is its byte spelling. + Canonical text: lance-graph `.claude/v3/soa_layout/le-contract.md` + (§ the 4-byte prefix; worked example `0x07:01` = OSINT:q2) + + `.claude/v3/knowledge/v3-substrate-primer.md` §5 (`[hi u16 CANON + concept/domain:appid][lo u16 CUSTOM]`). The 2026-07-02 cross-session + "conflict" between OGAR's `domain:concept-slot` prose and the + le-contract's `domain:appid` was a PHANTOM — the same second byte + under two names. **Homonym warning (the root cause):** the word "app" + appears in BOTH halves with different meanings — the **appid byte** + (hi half) is the canonical app-concept slot within a domain (shared + across vendors: `0x0102` = project work item, converged on by + OpenProject `0x0102_0001` AND Redmine `0x0102_0007`), while the + **APP render PREFIX** (lo half, OGAR#95 register: `0x0001` = + OpenProject) is the per-vendor ClassView skin. q2's appid `0x01` + (inside `0x07:01` OSINT:q2, hi half) and OpenProject's APP_PREFIX + `0x0001` (lo half) are positionally distinct registers — no collision, + no guard needed beyond this line. Cite this entry instead of + re-deriving; a "two ledgers disagree" claim checks the le-contract / + primer line FIRST. diff --git a/docs/OGAR-SEMANTIC-TRANSPILER-CAPSTONE.md b/docs/OGAR-SEMANTIC-TRANSPILER-CAPSTONE.md index c32d2ea..fe6c1fa 100644 --- a/docs/OGAR-SEMANTIC-TRANSPILER-CAPSTONE.md +++ b/docs/OGAR-SEMANTIC-TRANSPILER-CAPSTONE.md @@ -51,7 +51,8 @@ Per `OGAR-AST-CONTRACT.md`: - **THINK arm — `Class` → ClassView → fieldview (a three-level chain, not one layer):** `Class` is the source-of-truth IR node (data + declared hooks); - **ClassView** is a per-app render skin (hi-u16); **fieldview** is a presence + **ClassView** is a per-app render skin (lo-u16, canon HIGH / custom LOW + since the 2026-07-02 half-order flip); **fieldview** is a presence mask over ClassView. Structure-preserving: Odoo view → ClassView; Redmine ERB partial → Askama bitmask partial; OpenProject WP schema → `Class` facets. **CODED-today** (`lance-graph-contract/src/class_view.rs`: `ClassView`,