From ca60c381bc4173c3ce850051c8f457c497d90af9 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 24 Jun 2026 05:46:24 +0000 Subject: [PATCH 1/2] =?UTF-8?q?ogar-vocab:=20mint=20ConceptDomain::Automat?= =?UTF-8?q?ion=20(0x0C)=20=E2=80=94=20MARS/Automation=20classids?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves docs/MARS-TRANSCODING.md §1's deferred classid mint via the 5+3 codebook-hardening pass. One domain 0x0C spanning the MARS structural CMDB (A→R→S→M dependsOn backbone) AND the Automation DO-arm actuators — the Auth precedent (heterogeneous shapes, one cross-app concern; render prefix is the hi-u16 skin, the domain byte is the lo-u16 shared-concept half). 9 concepts (load-bearing only; the speculative rest reserved): 0x0C01 mars_application 0x0C02 mars_resource 0x0C03 mars_software 0x0C04 mars_machine 0x0C05 knowledge_item 0x0C06 mars_node_template 0x0C07 action_handler 0x0C08 action_applicability 0x0C09 automation_trigger Atomic lockstep (per runtime-archaeologist's 11-site checklist) across: - ogar-vocab: CODEBOOK + ConceptDomain enum + canonical_concept_domain arm + domain-byte doc block + class_ids consts + class_ids::ALL + 9 builder fns + all_promoted_classes() + count/routing tests. - ogar-class-view: import list + all_canonical_classes() (the drift-gate that broke CI for the Anatomy mint — every_codebook_id_appears_in_class_ids_all). Opportunistically fixed pre-existing doc drift the doctrine-keeper flagged: DIV-1 (Anatomy missing from APP-CLASS-CODEBOOK-LAYOUT domain map) + DIV-2 (stale 0x0A/0x0B in the lib.rs doc block; missing 0x0B Auth line). 5+3 hardening: theorem-checker (PASS) / doctrine-keeper / integration-lead / runtime-archaeologist savants + cargo fmt/test/clippy gates. ogar-vocab 94 + ogar-class-view 11 tests green, clippy-clean on new code. Docs: MARS-TRANSCODING §1 re-graded provisional→minted; D-MARS-CLASSID ledger row; EPIPHANIES E-MARS-AUTOMATION-MINT; APP-CLASS-CODEBOOK-LAYOUT domain map. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01EYvNjD8M8LMNYbRy3gq2FP --- .claude/board/EPIPHANIES.md | 42 +++++ crates/ogar-class-view/src/lib.rs | 81 +++++++-- crates/ogar-vocab/src/lib.rs | 263 +++++++++++++++++++++++++++++- docs/APP-CLASS-CODEBOOK-LAYOUT.md | 6 +- docs/DISCOVERY-MAP.md | 1 + docs/MARS-TRANSCODING.md | 19 ++- 6 files changed, 389 insertions(+), 23 deletions(-) diff --git a/.claude/board/EPIPHANIES.md b/.claude/board/EPIPHANIES.md index 564f0f9..6f16912 100644 --- a/.claude/board/EPIPHANIES.md +++ b/.claude/board/EPIPHANIES.md @@ -7,6 +7,48 @@ --- +## 2026-06-24 — E-MARS-AUTOMATION-MINT — the MARS/Automation classids are minted: `ConceptDomain::Automation` (0x0C), the deferred 5+3 codebook pass + +**Status:** FINDING (grounded `[G]` — shipped + drift-guard-green). + +`docs/MARS-TRANSCODING.md` §1 deferred the MARS classid mint ("provisional… +after the 5+3 codebook pass"). This is that pass. Outcome: **one domain +`0x0C` = `ConceptDomain::Automation`**, spanning the MARS structural CMDB +(`mars_application/resource/software/machine`, the A→R→S→M `dependsOn` backbone) +and the Automation DO-arm actuators (`knowledge_item`, `mars_node_template`, +`action_handler`, `action_applicability`, `automation_trigger`) — 9 concepts, +0x0C01–0x0C09. + +**Why one domain, not two** (the load-bearing decision): MARS (`ogit.MARS:`) and +Automation (`ogit.Automation:`) are different OGIT namespaces but the same HIRO +IT-automation stack. The render prefix (`ogit-mars` / `ogit-automation`) is the +hi-u16 skin; the **domain byte is the lo-u16 shared-concept half**. The Auth +family (`0x0B`: `auth_store` + per-IdP profiles) is the precedent — heterogeneous +shapes, one cross-app concern, one domain. The DO arm (`ActionDef`) and the THINK +arm (the MARS `Class`es) **meet** at this domain (cf. E-HIRO-IS-OGAR-DO-ARM: +ActionHandler is where DO meets auth/RBAC). Infrastructure config, **not PHI** — +same public-reference posture as Anatomy `0x0A`. + +**The 5+3 hardening that gated it** (CLAUDE.md): theorem-checker (PASS — 0x0C +free, ids collision-free/well-formed, the mint is a 4-part atomic edit); +doctrine-keeper (one domain, satisfies the §1 deferral, RESERVE-DON'T-RECLAIM +honored, flagged pre-existing doc drift DIV-1/DIV-2 fixed here); integration-lead +(OGAR-only correct for the string-keyed DO-arm, BUT mirror the `ConceptDomain` +routing arm into lance-graph `ogar_codebook.rs` to avoid soft-fail wire drift — +same branch, Anatomy precedent); runtime-archaeologist (the precise 11-site / +2-file lockstep checklist + every drift-guard test named — the Anatomy-break gate +is `every_codebook_id_appears_in_class_ids_all` in `ogar-class-view`). The +3 +reviewers = `cargo fmt` + the full drift-guard suite (ogar-vocab 94 / ogar-class-view +11 green) + clippy-clean on the new code. + +**Discipline:** minted only the 9 load-bearing concepts (each grounded by a real +vendored TTL entity AND used by the shipped structural or DO-arm lift). The rest +(`action_capability`, `intent`, `automation_issue`, `variable`, `mars_node`, +`mars_model`) are RESERVED — minted when a lift/consumer references them (the +anti-premature-commitment rule). Ledger: `DISCOVERY-MAP.md` D-MARS-CLASSID. + +--- + ## 2026-06-23 — E-HIRO-IS-OGAR-DO-ARM — HIRO's Automation domain is a production, externally-validated instance of OGAR's DO arm; the lossless rule is identity-points-to-body **Status:** FINDING (shape, grounded `[G]`) + CONJECTURE (executable equivalence, diff --git a/crates/ogar-class-view/src/lib.rs b/crates/ogar-class-view/src/lib.rs index 73921e9..c49f283 100644 --- a/crates/ogar-class-view/src/lib.rs +++ b/crates/ogar-class-view/src/lib.rs @@ -64,16 +64,67 @@ use lance_graph_contract::{ ontology::{DisplayTemplate, FieldRef, ObjectView}, }; use ogar_vocab::{ - Class, accounting_account, anatomical_structure, auth_ory_keto, auth_store, auth_zanzibar, - auth_zitadel, billable_work_entry, billing_party, bone, canonical_concept_id, - commercial_document, commercial_line_item, currency_policy, diagnosis, joint, lab_value, - medication, patient, payment_record, priority, product, project, project_actor, - project_attachment, project_changeset, project_comment, project_custom_field, - project_custom_value, project_enabled_module, project_forum, project_journal, - project_member_role, project_membership, project_message, project_news, project_query, - project_relation, project_repository, project_role, project_status, project_type, - project_version, project_watcher, project_wiki_page, project_work_item, skeleton, tax_policy, - treatment, visit, vital_sign, + Class, + accounting_account, + // 0x0CXX — automation (HIRO MARS CMDB + DO-arm actuators) + action_applicability, + action_handler, + anatomical_structure, + auth_ory_keto, + auth_store, + auth_zanzibar, + auth_zitadel, + automation_trigger, + billable_work_entry, + billing_party, + bone, + canonical_concept_id, + commercial_document, + commercial_line_item, + currency_policy, + diagnosis, + joint, + knowledge_item, + lab_value, + mars_application, + mars_machine, + mars_node_template, + mars_resource, + mars_software, + medication, + patient, + payment_record, + priority, + product, + project, + project_actor, + project_attachment, + project_changeset, + project_comment, + project_custom_field, + project_custom_value, + project_enabled_module, + project_forum, + project_journal, + project_member_role, + project_membership, + project_message, + project_news, + project_query, + project_relation, + project_repository, + project_role, + project_status, + project_type, + project_version, + project_watcher, + project_wiki_page, + project_work_item, + skeleton, + tax_policy, + treatment, + visit, + vital_sign, }; /// All promoted canonical concepts: `(canonical_concept_name, Class)`. @@ -138,6 +189,16 @@ fn all_canonical_classes() -> Vec<(&'static str, Class)> { ("auth_zitadel", auth_zitadel()), ("auth_zanzibar", auth_zanzibar()), ("auth_ory_keto", auth_ory_keto()), + // ── 0x0CXX — automation (HIRO MARS CMDB + DO-arm actuators) ── + ("mars_application", mars_application()), + ("mars_resource", mars_resource()), + ("mars_software", mars_software()), + ("mars_machine", mars_machine()), + ("knowledge_item", knowledge_item()), + ("mars_node_template", mars_node_template()), + ("action_handler", action_handler()), + ("action_applicability", action_applicability()), + ("automation_trigger", automation_trigger()), ] } diff --git a/crates/ogar-vocab/src/lib.rs b/crates/ogar-vocab/src/lib.rs index ca3987e..ac50233 100644 --- a/crates/ogar-vocab/src/lib.rs +++ b/crates/ogar-vocab/src/lib.rs @@ -1070,7 +1070,9 @@ impl Class { /// 0x08XX reserved: OCR /// 0x09XX reserved: Health /// 0x0AXX Anatomy (FMA reference ontology; bones/skeleton) -/// 0x0CXX+ unassigned +/// 0x0BXX Auth (IAM; the AuthStore class family) +/// 0x0CXX Automation (HIRO IT-automation: MARS CMDB + actuators) +/// 0x0DXX+ unassigned /// ``` /// /// **Anatomy vs Health (the firewall split).** `0x0AXX` Anatomy is the @@ -1202,6 +1204,31 @@ const CODEBOOK: &[(&str, u16)] = &[ ("auth_zitadel", 0x0B02), ("auth_zanzibar", 0x0B03), ("auth_ory_keto", 0x0B04), + // ── 0x0CXX — Automation domain (the HIRO IT-automation stack) ── + // One domain spanning the MARS structural CMDB (`ogit.MARS:` — + // Application/Resource/Software/Machine, the A→R→S→M dependsOn backbone) + // 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 + // 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 / + // integration-lead / runtime-archaeologist + cargo gates). The set is the + // load-bearing concepts the structural + DO-arm lifts stand on; further + // Automation entities (action_capability / intent / automation_issue / + // variable / mars_node) are RESERVED, minted when a lift/consumer uses them. + ("mars_application", 0x0C01), + ("mars_resource", 0x0C02), + ("mars_software", 0x0C03), + ("mars_machine", 0x0C04), + ("knowledge_item", 0x0C05), + ("mars_node_template", 0x0C06), + ("action_handler", 0x0C07), + ("action_applicability", 0x0C08), + ("automation_trigger", 0x0C09), ]; /// Codebook **domain** — the high byte of a canonical id (see @@ -1239,8 +1266,18 @@ pub enum ConceptDomain { /// `auth_zanzibar` / `auth_ory_keto`). See /// `docs/CLASSID-RBAC-KEYSTONE-SPEC.md` §7. Auth, + /// `0x0CXX` — Automation (the HIRO IT-automation stack). One domain + /// spanning the MARS structural CMDB (`mars_application` / `mars_resource` + /// / `mars_software` / `mars_machine` — the A→R→S→M dependsOn backbone) + /// and the Automation actuators (`knowledge_item` / `mars_node_template` / + /// `action_handler` / `action_applicability` / `automation_trigger` — + /// HIRO's DO-arm vocabulary). The DO arm (`ActionDef`) and the THINK arm + /// (the MARS `Class`es) meet here. Infrastructure config, NOT PHI — same + /// public-reference posture as [`Anatomy`](Self::Anatomy). See + /// `docs/MARS-TRANSCODING.md` + `docs/HIRO-DO-ARM-LIFT.md`. + Automation, /// Any high-byte slot not yet assigned a domain (`0x03XX`–`0x06XX`, - /// `0x0AXX`, `0x0CXX`+). + /// `0x0DXX`+). Unassigned, } @@ -1257,6 +1294,7 @@ pub fn canonical_concept_domain(id: u16) -> ConceptDomain { 0x09 => ConceptDomain::Health, 0x0A => ConceptDomain::Anatomy, 0x0B => ConceptDomain::Auth, + 0x0C => ConceptDomain::Automation, _ => ConceptDomain::Unassigned, } } @@ -1558,6 +1596,41 @@ pub mod class_ids { /// `auth_ory_keto` (`0x0B04`) — Ory Keto provider profile. pub const AUTH_ORY_KETO: u16 = 0x0B04; + // ── 0x0CXX — Automation domain (HIRO IT-automation stack) ── + + /// `mars_application` (`0x0C01`) — a MARS Application CMDB entity; head of + /// the A→R→S→M `dependsOn` backbone (`ogit.MARS:Application`). + pub const MARS_APPLICATION: u16 = 0x0C01; + /// `mars_resource` (`0x0C02`) — a MARS Resource (`ogit.MARS:Resource`). + pub const MARS_RESOURCE: u16 = 0x0C02; + /// `mars_software` (`0x0C03`) — a MARS Software component + /// (`ogit.MARS:Software`). + pub const MARS_SOFTWARE: u16 = 0x0C03; + /// `mars_machine` (`0x0C04`) — a MARS Machine; tail of the A→R→S→M chain + /// (`ogit.MARS:Machine`). + pub const MARS_MACHINE: u16 = 0x0C04; + /// `knowledge_item` (`0x0C05`) — the Automation KnowledgeItem; the DO-arm + /// `ActionDef` carrier (`ogit.Automation:KnowledgeItem`). Its opaque body + /// rides in `knowledgeItemFormalRepresentation` — pointed-to, never inlined + /// (lossless-DO; `docs/HIRO-DO-ARM-LIFT.md` §1). + pub const KNOWLEDGE_ITEM: u16 = 0x0C05; + /// `mars_node_template` (`0x0C06`) — the template a KnowledgeItem + /// `relates` to; the DO-arm `ActionDef.object_class` + /// (`ogit.Automation:MARSNodeTemplate`). + pub const MARS_NODE_TEMPLATE: u16 = 0x0C06; + /// `action_handler` (`0x0C07`) — the ActionHandler adapter/membrane that + /// `provides` Applicability + Capability (`ogit.Automation:ActionHandler`). + /// Where the DO arm and the auth/RBAC arm meet (`HIRO-DO-ARM-LIFT.md` §3). + pub const ACTION_HANDLER: u16 = 0x0C07; + /// `action_applicability` (`0x0C08`) — the ActionApplicability; its + /// `environmentFilter` is the DO-arm `KausalSpec::StateGuard` + /// (`ogit.Automation:ActionApplicability`). + pub const ACTION_APPLICABILITY: u16 = 0x0C08; + /// `automation_trigger` (`0x0C09`) — the Trigger a KnowledgeItem + /// `contains`; the DO-arm `KausalSpec::LifecycleTrigger` + /// (`ogit.Automation:Trigger`). + pub const AUTOMATION_TRIGGER: u16 = 0x0C09; + /// Every `(canonical_concept_name, id)` pair the constants vouch for. /// Drift-guarded against [`super::CODEBOOK`] by tests in this module. pub const ALL: &[(&str, u16)] = &[ @@ -1615,6 +1688,16 @@ pub mod class_ids { ("auth_zitadel", AUTH_ZITADEL), ("auth_zanzibar", AUTH_ZANZIBAR), ("auth_ory_keto", AUTH_ORY_KETO), + // 0x0CXX — automation (HIRO IT-automation: MARS CMDB + actuators) + ("mars_application", MARS_APPLICATION), + ("mars_resource", MARS_RESOURCE), + ("mars_software", MARS_SOFTWARE), + ("mars_machine", MARS_MACHINE), + ("knowledge_item", KNOWLEDGE_ITEM), + ("mars_node_template", MARS_NODE_TEMPLATE), + ("action_handler", ACTION_HANDLER), + ("action_applicability", ACTION_APPLICABILITY), + ("automation_trigger", AUTOMATION_TRIGGER), ]; #[cfg(test)] @@ -2475,6 +2558,17 @@ pub fn all_promoted_classes() -> Vec { auth_zitadel(), auth_zanzibar(), auth_ory_keto(), + // 0x0CXX — automation arm (HIRO MARS CMDB + DO-arm actuators), + // in class_ids::ALL order. + mars_application(), + mars_resource(), + mars_software(), + mars_machine(), + knowledge_item(), + mars_node_template(), + action_handler(), + action_applicability(), + automation_trigger(), ] } @@ -3683,6 +3777,142 @@ pub fn joint() -> Class { c } +// ── 0x0CXX — Automation domain builders (HIRO IT-automation stack) ── +// The MARS structural CMDB (A→R→S→M `dependsOn` backbone) + the Automation +// DO-arm actuators. Shapes grounded in the vendored OGIT TTL attributes +// (`vocab/imports/ogit/NTO/{MARS,Automation}/`). See `docs/MARS-TRANSCODING.md` +// + `docs/HIRO-DO-ARM-LIFT.md`. + +/// The `mars_application` (`0x0C01`) — head of the MARS A→R→S→M `dependsOn` +/// backbone (`ogit.MARS:Application`). +#[must_use] +pub fn mars_application() -> Class { + let mut c = Class::new("MarsApplication"); + c.language = Language::Unknown; + c.canonical_concept = Some("mars_application".to_string()); + let mut class = Attribute::new("class"); + class.type_name = Some("string".to_string()); + c.attributes = vec![class]; + c.associations = vec![family_edge("depends_on", "MarsResource")]; + c +} + +/// The `mars_resource` (`0x0C02`) — `ogit.MARS:Resource`. +#[must_use] +pub fn mars_resource() -> Class { + let mut c = Class::new("MarsResource"); + c.language = Language::Unknown; + c.canonical_concept = Some("mars_resource".to_string()); + let mut class = Attribute::new("class"); + class.type_name = Some("string".to_string()); + c.attributes = vec![class]; + c.associations = vec![family_edge("depends_on", "MarsSoftware")]; + c +} + +/// The `mars_software` (`0x0C03`) — `ogit.MARS:Software`. +#[must_use] +pub fn mars_software() -> Class { + let mut c = Class::new("MarsSoftware"); + c.language = Language::Unknown; + c.canonical_concept = Some("mars_software".to_string()); + let mut service_name = Attribute::new("service_name"); + service_name.type_name = Some("string".to_string()); + c.attributes = vec![service_name]; + c.associations = vec![family_edge("depends_on", "MarsMachine")]; + c +} + +/// The `mars_machine` (`0x0C04`) — tail of the A→R→S→M chain +/// (`ogit.MARS:Machine`). +#[must_use] +pub fn mars_machine() -> Class { + let mut c = Class::new("MarsMachine"); + c.language = Language::Unknown; + c.canonical_concept = Some("mars_machine".to_string()); + let mut cpu_arch = Attribute::new("cpu_arch"); + cpu_arch.type_name = Some("string".to_string()); + let mut cpu_cores = Attribute::new("cpu_cores"); + cpu_cores.type_name = Some("integer".to_string()); + c.attributes = vec![cpu_arch, cpu_cores]; + c +} + +/// The `knowledge_item` (`0x0C05`) — the Automation KnowledgeItem; the DO-arm +/// `ActionDef` carrier (`ogit.Automation:KnowledgeItem`). The opaque body +/// (`knowledge_item_formal_representation`) is pointed-to, never inlined. +#[must_use] +pub fn knowledge_item() -> Class { + let mut c = Class::new("KnowledgeItem"); + c.language = Language::Unknown; + c.canonical_concept = Some("knowledge_item".to_string()); + // The opaque body slot — the lossless-DO pointer (the attribute exists; + // the bytes are never inlined into the IR). + let mut body = Attribute::new("knowledge_item_formal_representation"); + body.type_name = Some("string".to_string()); + c.attributes = vec![body]; + c.associations = vec![ + family_edge("relates", "MarsNodeTemplate"), + family_edge("contains", "AutomationTrigger"), + ]; + c +} + +/// The `mars_node_template` (`0x0C06`) — the template a KnowledgeItem +/// `relates` to; the DO-arm `ActionDef.object_class` +/// (`ogit.Automation:MARSNodeTemplate`). +#[must_use] +pub fn mars_node_template() -> Class { + let mut c = Class::new("MarsNodeTemplate"); + c.language = Language::Unknown; + c.canonical_concept = Some("mars_node_template".to_string()); + let mut repr = Attribute::new("mars_node_formal_representation"); + repr.type_name = Some("string".to_string()); + c.attributes = vec![repr]; + c +} + +/// The `action_handler` (`0x0C07`) — the ActionHandler adapter/membrane +/// (`ogit.Automation:ActionHandler`); where the DO arm meets the auth/RBAC arm. +#[must_use] +pub fn action_handler() -> Class { + let mut c = Class::new("ActionHandler"); + c.language = Language::Unknown; + c.canonical_concept = Some("action_handler".to_string()); + let mut name = Attribute::new("name"); + name.type_name = Some("string".to_string()); + c.attributes = vec![name]; + c.associations = vec![family_edge("provides", "ActionApplicability")]; + c +} + +/// The `action_applicability` (`0x0C08`) — its `environment_filter` is the +/// DO-arm `KausalSpec::StateGuard` (`ogit.Automation:ActionApplicability`). +#[must_use] +pub fn action_applicability() -> Class { + let mut c = Class::new("ActionApplicability"); + c.language = Language::Unknown; + c.canonical_concept = Some("action_applicability".to_string()); + let mut env = Attribute::new("environment_filter"); + env.type_name = Some("string".to_string()); + c.attributes = vec![env]; + c +} + +/// The `automation_trigger` (`0x0C09`) — the Trigger a KnowledgeItem +/// `contains`; the DO-arm `KausalSpec::LifecycleTrigger` +/// (`ogit.Automation:Trigger`). +#[must_use] +pub fn automation_trigger() -> Class { + let mut c = Class::new("AutomationTrigger"); + c.language = Language::Unknown; + c.canonical_concept = Some("automation_trigger".to_string()); + let mut description = Attribute::new("description"); + description.type_name = Some("string".to_string()); + c.attributes = vec![description]; + c +} + #[cfg(test)] mod tests { use super::*; @@ -4298,10 +4528,13 @@ mod tests { // Anatomy block (0x0A) — FMA reference kinds. assert_eq!(canonical_concept_domain(0x0A00), ConceptDomain::Anatomy); assert_eq!(canonical_concept_domain(0x0A03), ConceptDomain::Anatomy); - // Unassigned blocks (3-6, C+). + // 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+). assert_eq!(canonical_concept_domain(0x0300), ConceptDomain::Unassigned); assert_eq!(canonical_concept_domain(0x0600), ConceptDomain::Unassigned); - assert_eq!(canonical_concept_domain(0x0C00), ConceptDomain::Unassigned); + assert_eq!(canonical_concept_domain(0x0D00), ConceptDomain::Unassigned); assert_eq!(canonical_concept_domain(0xFFFF), ConceptDomain::Unassigned); } @@ -4402,6 +4635,28 @@ mod tests { assert_eq!(concepts_in_domain(ConceptDomain::Health).count(), 7); assert_eq!(concepts_in_domain(ConceptDomain::Commerce).count(), 8); assert_eq!(concepts_in_domain(ConceptDomain::ProjectMgmt).count(), 26); + assert_eq!(concepts_in_domain(ConceptDomain::Anatomy).count(), 4); + assert_eq!(concepts_in_domain(ConceptDomain::Auth).count(), 4); + assert_eq!(concepts_in_domain(ConceptDomain::Automation).count(), 9); + // Every yielded Automation id really is in-domain (0x0CXX). + let automation: Vec<&str> = concepts_in_domain(ConceptDomain::Automation) + .map(|(name, _)| name) + .collect(); + assert_eq!( + automation, + [ + "mars_application", + "mars_resource", + "mars_software", + "mars_machine", + "knowledge_item", + "mars_node_template", + "action_handler", + "action_applicability", + "automation_trigger", + ], + "Automation domain set drift — re-sync the consumer coverage gate", + ); // An empty (reserved-but-unpopulated) domain yields nothing. assert_eq!(concepts_in_domain(ConceptDomain::Osint).count(), 0); } diff --git a/docs/APP-CLASS-CODEBOOK-LAYOUT.md b/docs/APP-CLASS-CODEBOOK-LAYOUT.md index 46c7411..117f6bd 100644 --- a/docs/APP-CLASS-CODEBOOK-LAYOUT.md +++ b/docs/APP-CLASS-CODEBOOK-LAYOUT.md @@ -54,8 +54,8 @@ classid : u32 = [ hi u16 : APP / codebook namespace ] [ lo u16 : in-codebook concept byte `CC`, exactly as the codebook encodes today. Within the core codebook (`hi = 0x0000`) the domain bytes are the canonical map (`0x01` project, `0x02` commerce, `0x07` osint, `0x08` ocr, `0x09` - health). Within an app-private codebook the app owns its own `DD|CC` - layout. + health, `0x0A` anatomy, `0x0B` auth, `0x0C` automation). Within an + app-private codebook the app owns its own `DD|CC` layout. **This is additive, not a reclaim.** Every classid shipped to date is `0x0000_DDCC` — i.e. it was *always* an APP‖class id with `APP = core`. @@ -130,7 +130,7 @@ materialised until the app mints its first private class). | `hi u16` | App / namespace | Core domain(s) it consumes | Private codebook today? | |---|---|---|---| -| `0x0000` | **Shared canonical core** | all (`0x01/02/07/08/09` + `0x0B` auth) | n/a (this *is* core) | +| `0x0000` | **Shared canonical core** | all (`0x01/02/07/08/09` + `0x0A` anatomy + `0x0B` auth + `0x0C` automation) | n/a (this *is* core) | | `0x0001` | OpenProject (openproject-nexgen-rs) | `0x01` project-mgmt | **no** — maps onto core | | `0x0002` | Odoo | `0x02` commerce | **no** — maps onto core (converge `od-ontology`) | | `0x0003` | WoA / woa-rs | `0x02` commerce (work orders) | **no** — maps onto core | diff --git a/docs/DISCOVERY-MAP.md b/docs/DISCOVERY-MAP.md index 188456f..e994061 100644 --- a/docs/DISCOVERY-MAP.md +++ b/docs/DISCOVERY-MAP.md @@ -209,6 +209,7 @@ two halves of a cell. ADR‑026 names the cascade that ties them. | D‑HINT | `schema_ddl_hint` loop closed — self‑describing registry via emit | G | CODED | (#33) | D‑SURREALQL, D‑KNOWABLE | | D‑ELIXIR | Elixir/HIRO SchemaSource scaffold (`gen_statem`→Rubicon) | G | CODED (scaffold) | `ogar-from-elixir/` | D‑VOCAB | | D‑HIRO‑DO | OGIT Automation → DO arm: `into_action_def` lifts `KnowledgeItem`→`ActionDef` (object_class←`relates`, kausal←`contains Trigger`, body **pointed‑to** not inlined — lossless‑DO §1); schema half of `PROBE‑OGAR‑DO‑ARM‑LIFT` green; lift→`emit_action_def`→SPO triples proven end‑to‑end (`tests/do_arm_emit.rs`, lossless‑DO holds across emit) | G | CODED (schema half) | `ogar-from-schema/src/do_arm.rs` | D‑VOCAB, D‑TTL, D‑ELIXIR, D‑EMIT | +| D‑MARS‑CLASSID | MARS/Automation classids MINTED: `ConceptDomain::Automation` (0x0C), 9 concepts (`mars_application/resource/software/machine` 0x0C01‑04, `knowledge_item` 05, `mars_node_template` 06, `action_handler` 07, `action_applicability` 08, `automation_trigger` 09) — one domain spanning MARS structural CMDB + Automation DO‑arm (Auth precedent); resolves MARS‑TRANSCODING §1 deferral; passed 5+3 hardening (theorem/doctrine/integration/runtime savants + drift‑guards). Reserves the speculative rest | G | CODED | `ogar-vocab/src/lib.rs`, `ogar-class-view/src/lib.rs` | D‑VOCAB, D‑HIRO‑DO | | D‑OSM | `ogar-from-osm-pbf` — Node/Way/Relation; quadkey NiblePath from resolved geometry | H | IDEA | (queued) | D‑VOCAB, `[per rt]` D‑OSM‑3 | | D‑PATTERN | `ogar-pattern` — recognition library + confidence (FMA‑D/FIBO/SKR/PROV‑O) | H | IDEA | (queued) | D‑TTL | | D‑ACTION | `ogar-actionable` — lifecycle → `ActionDef`/`KausalSpec` | H | IDEA | (queued) | D‑PATTERN | diff --git a/docs/MARS-TRANSCODING.md b/docs/MARS-TRANSCODING.md index f1798f7..9c135e6 100644 --- a/docs/MARS-TRANSCODING.md +++ b/docs/MARS-TRANSCODING.md @@ -72,14 +72,21 @@ The dependency backbone — **A→R→S→M**: ``` Application ──dependsOn──► Resource ──dependsOn──► Software ──dependsOn──► Machine - 0x??01 0x??02 0x??03 0x??04 + 0x0C01 0x0C02 0x0C03 0x0C04 ``` -(Classid byte allocation is **provisional**; mint requires the 5+3 -codebook pass per `CLAUDE.md`. See `docs/APP-CLASS-CODEBOOK-LAYOUT.md` -for the §4 mint protocol. The 0x??XX domain byte is reserved for MARS- -infrastructure; concrete `class_ids::MARS_*` constants land in a -follow-up after the codebook pass.) +**MINTED (2026-06-24).** The provisional `0x??XX` resolved to **domain `0x0C` +Automation** — one domain spanning the MARS structural CMDB *and* the Automation +DO-arm actuators (the Auth-precedent: heterogeneous shapes, one cross-app +concern). Shipped constants: `class_ids::{MARS_APPLICATION 0x0C01, MARS_RESOURCE +0x0C02, MARS_SOFTWARE 0x0C03, MARS_MACHINE 0x0C04, KNOWLEDGE_ITEM 0x0C05, +MARS_NODE_TEMPLATE 0x0C06, ACTION_HANDLER 0x0C07, ACTION_APPLICABILITY 0x0C08, +AUTOMATION_TRIGGER 0x0C09}` (`ogar-vocab`). This **was** the deferred 5+3 codebook +pass (theorem-checker / doctrine-keeper / integration-lead / runtime-archaeologist ++ cargo drift-guards); see `.claude/board/EPIPHANIES.md` E-MARS-AUTOMATION-MINT and +`docs/APP-CLASS-CODEBOOK-LAYOUT.md`. Further Automation entities +(action_capability / intent / automation_issue / variable / mars_node) are +RESERVED — minted when a lift or consumer references them. --- From 29203bb864561cf79be43df483d413fb0c43dc08 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 4 Jul 2026 13:05:41 +0000 Subject: [PATCH 2/2] =?UTF-8?q?ogar-from-ruff:=20lift=20Odoo=20`=5Finherit?= =?UTF-8?q?`=20(Model.inherits)=20onto=20Class.mixins=20=E2=80=94=20transp?= =?UTF-8?q?ile-chain=20LEG=202?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ruff PR #40 (merged) shipped the is_a input end: a frontend-agnostic `ruff_spo_triplet::Model.inherits: Vec` populated by the Odoo frontend from `_inherit` (self-reopen self-edge excluded upstream). The lift previously consumed only `sti.inherits_from -> Class.parent` (Rails STI) and dropped Model.inherits, so the Odoo is_a linkage never reached the Core. Route it to Class.mixins (extend), the multi-parent Vec shelf the vocab already designates for it: Class::mixins doc names `_inherit = 'mixin.thread'`, and Class::inheritance excludes mixins ("a SEPARATE axis ... never folded in here"). So NOT parent/inheritance (STI single-parent spine) — no parent widening, no information loss, no vocab-axis violation. Multi-parent handled natively by the Vec. Frontend-agnostic: only the Odoo frontend populates Model.inherits, so it is a no-op for Rails (sti) / C++ (bases). Consequence for LEG 3 (render, D-VCW-3): the FieldMask compose must union parent U mixins for Odoo inherited fields; render_rows stays concept-local. Tests: +2 (odoo_inherit_lands_on_mixins_not_parent, empty_inherits_adds_no_mixins); 48 green in ogar-from-ruff, workspace check + clippy clean. Ledger: docs/DISCOVERY-MAP.md D-OGAR-ODOO-INHERIT-MIXINS. --- crates/ogar-from-ruff/src/lib.rs | 40 ++++++++++++++++++++++++++++++++ docs/DISCOVERY-MAP.md | 29 +++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/crates/ogar-from-ruff/src/lib.rs b/crates/ogar-from-ruff/src/lib.rs index ae69f6e..78703d8 100644 --- a/crates/ogar-from-ruff/src/lib.rs +++ b/crates/ogar-from-ruff/src/lib.rs @@ -35,6 +35,7 @@ //! | `scopes` | `scopes` / `default_scope` | `Scope` / `Scopes` → `Class.scopes`; `DefaultScope` → `Class.default_scope` | //! | `acts_as` | `mixins` | rendered as `acts_as_` so they survive on the same shelf as concerns (`ogar-vocab` has no separate `acts_as` slot) | //! | `sti.inherits_from` | `parent` | STI parent — matches `Class.parent` slot | +//! | `inherits` | `mixins` (appended) | Odoo `_inherit` multi-parent mixin composition — the vocab's `mixins` doc names `_inherit`; the `inheritance` axis excludes mixins. Frontend-agnostic field, populated only by the Odoo frontend | //! | `functions` | `Vec` (DO-arm) | [`lift_actions`] — one `ActionDef` per method; standalone, not on `Class` | //! //! Fields NOT lifted today (no equivalent on the ruff side OR no clean @@ -177,6 +178,15 @@ fn lift_model_with_language(model: &Model, language: Language) -> Class { class.canonical_concept = Some(canonical_concept(&model.name)); class.associations = model.associations.iter().filter_map(lift_association).collect(); class.mixins = lift_mixins(model); + // Odoo `_inherit` (multi-parent mixin composition) lands on the same + // mixins shelf the vocab designates for it — `Class::mixins` doc names + // `_inherit = 'mixin.thread'`, and `Class::inheritance` explicitly + // excludes mixins ("Mixins / concerns are a SEPARATE axis"). The ruff + // frontend already normalised the names (dot→underscore→verbatim), + // deduped, and excluded the bare-`_inherit` reopen self-edge. Only the + // Odoo frontend populates `Model::inherits`, so this is a no-op for the + // Rails (`sti`) and C++ (`bases`) producers — hence unconditional. + class.mixins.extend(model.inherits.iter().cloned()); class.attributes = model.attributes.iter().filter_map(lift_attribute).collect(); class.enums = model.attributes.iter().filter_map(lift_enum).collect(); class.scopes = model.scopes.iter().filter_map(lift_scope).collect(); @@ -885,6 +895,36 @@ mod tests { assert!(class.computed_fields.is_empty()); } + #[test] + fn odoo_inherit_lands_on_mixins_not_parent() { + // The is_a input end of the transpile chain: `ruff_python_spo` + // populates the frontend-agnostic `Model::inherits` from Odoo + // `_inherit` (self-reopen already excluded upstream). The lift routes + // it to `Class::mixins` — the vocab's designated multi-parent shelf — + // NOT to the single `parent` / `inheritance` is_a spine (those stay + // Rails-STI-shaped; the vocab excludes mixins from `inheritance`). + let mut m = mk_odoo_model(); + m.inherits = vec!["mail_thread".to_string(), "mail_activity_mixin".to_string()]; + let class = lift_model_python(&m); + + // Both parents preserved on the mixins shelf, order kept. + assert!(class.mixins.contains(&"mail_thread".to_string())); + assert!(class.mixins.contains(&"mail_activity_mixin".to_string())); + // The is_a spine is untouched — Odoo `_inherit` is NOT STI. + assert_eq!(class.parent, None); + assert_eq!(class.inheritance, Inheritance::Root); + } + + #[test] + fn empty_inherits_adds_no_mixins() { + // Frontend-agnostic no-op: the Rails / C++ producers never populate + // `Model::inherits`, so the lift must not fabricate mixins. A bare + // model (no concerns, no acts_as, no `_inherit`) lifts with an empty + // mixins shelf — the `inherits` extension contributes nothing. + let class = lift_model(&Model::new("Bare")); + assert!(class.mixins.is_empty()); + } + #[test] fn lift_inheritance_concrete_from_sti_parent() { // mk_model's StiInfo has inherits_from = Some("Issue"). diff --git a/docs/DISCOVERY-MAP.md b/docs/DISCOVERY-MAP.md index 95e5022..f9a2e8f 100644 --- a/docs/DISCOVERY-MAP.md +++ b/docs/DISCOVERY-MAP.md @@ -710,3 +710,32 @@ isolation. The map's job is to keep them visible. 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. + +- **D-OGAR-ODOO-INHERIT-MIXINS (transpile-chain LEG 2; 2026-07-04; + [G] — CODED + tested):** the middle leg of the operator's transpile + chain (`ruff *_spo harvest → ogar-from-ruff lift → CompiledClass → + ClassView × FieldMask → askama render`). ruff PR #40 shipped the input + end: a frontend-agnostic `ruff_spo_triplet::Model.inherits: Vec` + populated by the Odoo frontend from `_inherit` (self-reopen self-edge + excluded upstream). `ogar-from-ruff` previously consumed only + `sti.inherits_from → Class.parent` (Rails STI) and **dropped** + `Model.inherits`, so the Odoo is_a linkage never reached the Core. + **Resolution (this commit):** bump the ruff pin to merged main + (`61ce2b49`), then `class.mixins.extend(model.inherits)` in + `lift_model_with_language`. **The multi-parent "decision required" I + forwarded was already answered by the vocab:** `Class::mixins` doc + explicitly names `_inherit = 'mixin.thread'`, and `Class::inheritance` + doc states "Mixins / concerns are a SEPARATE axis … never folded in + here." So Odoo `_inherit` → `mixins` (the multi-parent `Vec` shelf), + NOT `parent`/`inheritance` (STI single-parent spine) — no `parent` + widening, no information loss, no vocab-axis violation. **Consequence + for LEG 3 (V3/D-VCW-3 render):** the FieldMask compose step must union + over `parent` ∪ `mixins` when materialising Odoo inherited fields — + the is_a spine and the mixin shelf are BOTH inheritance surfaces for + the render; `render_rows` itself stays concept-local (the union is the + caller's compose-time `FieldMask::inherit` bitwise-or). 48 tests green + in `ogar-from-ruff` (2 new: `odoo_inherit_lands_on_mixins_not_parent`, + `empty_inherits_adds_no_mixins`); workspace `cargo check` clean; + clippy clean. Supersedes the "widen parent vs primary+relation" + framing in the 2026-07-04 lance-graph broadcast — the vocab's mixins + axis is the answer.