From f3564a1c573b0ccd164d309b8bd1f0816844a111 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 15 Oct 2026 06:31:08 +0000 Subject: [PATCH] vocab: withdraw the content mints, keep append-only field ordering Removes the concept 0x090D and the attribute mints added in #361. A consumer's codebook content does not belong in OGAR; OGAR keeps the generic part: `AttributeOptions::after_edges`, `Attribute::appended` and `Class::field_basis`, plus a test that an appended attribute lands after the edges and that `field_basis` matches the class view's order for every class. 0x090D is not reused. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg --- crates/ogar-class-view/src/lib.rs | 37 +++++++---- crates/ogar-vocab/src/lib.rs | 100 ++++-------------------------- crates/ogar-vocab/src/ports.rs | 2 +- 3 files changed, 39 insertions(+), 100 deletions(-) diff --git a/crates/ogar-class-view/src/lib.rs b/crates/ogar-class-view/src/lib.rs index d7232db8..0fdeb121 100644 --- a/crates/ogar-class-view/src/lib.rs +++ b/crates/ogar-class-view/src/lib.rs @@ -171,7 +171,6 @@ use ogar_vocab::{ recoder, rom_image, skeleton, - specialist_referral, tax_policy, textline, treatment, @@ -290,7 +289,6 @@ fn all_canonical_classes() -> Vec<(&'static str, Class)> { ("examination", examination()), ("practitioner", practitioner()), ("external_practice", external_practice()), - ("specialist_referral", specialist_referral()), // ── 0x0AXX — anatomy (FMA reference kinds) ── ("anatomical_structure", anatomical_structure()), ("skeleton", skeleton()), @@ -704,7 +702,6 @@ mod tests { "examination", "practitioner", "external_practice", - "specialist_referral", ] { let id = canonical_concept_id(concept).unwrap(); let view = v @@ -715,25 +712,21 @@ mod tests { } #[test] - fn diagnosis_field_basis_is_ten_attributes_two_edges_then_appended() { + fn diagnosis_field_basis_is_ten_attributes_then_two_edges() { // The 0x0902 worked example, projected: attributes first (icd_code - // leads), then the two family edges, then the attributes minted - // after the edges were fixed. Positions 0..=11 predate `recorded_on` - // and must not move: a FieldMask pinned against them still selects - // the same fields. + // leads), then the two family edges, in declaration order. let v = OgarClassView::new(); let id = canonical_concept_id("diagnosis").unwrap(); let fields = v.fields(id); - assert_eq!(fields.len(), 13); // 10 attributes + 2 edges + 1 appended + assert_eq!(fields.len(), 12); // 10 attributes + 2 edges assert_eq!(fields[0].predicate_iri, "icd_code"); assert_eq!(fields[10].predicate_iri, "patient"); assert_eq!(fields[11].predicate_iri, "encounter"); - assert_eq!(fields[12].predicate_iri, "recorded_on"); } /// `Class::field_basis` is the vocabulary-side spelling of the order the - /// class view projects. A consumer that pins positions from the vocabulary - /// (MedCare's RBAC field list does) gets the class view's positions. + /// class view projects, so a consumer that pins positions from the + /// vocabulary gets the class view's positions. #[test] fn field_basis_matches_the_projected_order_for_every_class() { let v = OgarClassView::new(); @@ -760,4 +753,24 @@ mod tests { ); } } + + /// An attribute flagged `after_edges` is projected after the edges, so it + /// never moves a position a consumer has already pinned. + #[test] + fn an_appended_attribute_lands_after_the_edges() { + let mut class = ogar_vocab::diagnosis(); + class + .attributes + .push(ogar_vocab::Attribute::appended("extra", "string")); + let view = lift_object_view(&class); + let names: Vec<&str> = view + .fields + .iter() + .map(|f| f.predicate_iri.as_str()) + .collect(); + assert_eq!(names[10], "patient"); + assert_eq!(names[11], "encounter"); + assert_eq!(names[12], "extra"); + assert_eq!(names, class.field_basis()); + } } diff --git a/crates/ogar-vocab/src/lib.rs b/crates/ogar-vocab/src/lib.rs index 5fef61d7..fb7abd5f 100644 --- a/crates/ogar-vocab/src/lib.rs +++ b/crates/ogar-vocab/src/lib.rs @@ -1374,7 +1374,6 @@ const CODEBOOK: &[(&str, u16)] = &[ ("examination", 0x090A), ("practitioner", 0x090B), ("external_practice", 0x090C), - ("specialist_referral", 0x090D), // ── 0x0AXX — Anatomy domain (FMA reference ontology) ── // The public anatomical reference frame consumed by the splat-native // ultrasound arc (`docs/SPLAT-NATIVE-CUSTOMER.md` §6 litmus) and the FMA @@ -1894,7 +1893,7 @@ pub fn canonical_concept_domain(id: u16) -> ConceptDomain { /// .map(|(name, _id)| name) /// .collect(); /// assert!(health.contains(&"patient")); -/// assert_eq!(health.len(), 13); // 7 OGIT Healthcare entities + 6 harvest-derived mints +/// assert_eq!(health.len(), 12); // 7 OGIT Healthcare entities + 5 harvest-derived mints /// ``` pub fn concepts_in_domain(domain: ConceptDomain) -> impl Iterator { CODEBOOK @@ -2276,12 +2275,6 @@ pub mod class_ids { /// than method-only; no clean OGIT entity — no port alias until one /// exists. pub const EXTERNAL_PRACTICE: u16 = 0x090C; - /// `specialist_referral` (`0x090D`) — a referral of a patient to a - /// specialist (German practice software: Facharztüberweisung). Lives in - /// the patient file, names the practitioner referred to, and runs from - /// a start date until it is closed. No curated OGIT entity — no port - /// alias until one exists. - pub const SPECIALIST_REFERRAL: u16 = 0x090D; // ── 0x0AXX — Anatomy domain (FMA reference ontology) ── @@ -2534,7 +2527,6 @@ pub mod class_ids { ("examination", EXAMINATION), ("practitioner", PRACTITIONER), ("external_practice", EXTERNAL_PRACTICE), - ("specialist_referral", SPECIALIST_REFERRAL), // 0x0AXX — anatomy (FMA reference ontology) ("anatomical_structure", ANATOMICAL_STRUCTURE), ("skeleton", SKELETON), @@ -3485,7 +3477,6 @@ pub fn all_promoted_classes() -> Vec { examination(), practitioner(), external_practice(), - specialist_referral(), // 0x0AXX — anatomy arm (FMA reference kinds), in class_ids::ALL order. anatomical_structure(), skeleton(), @@ -4611,12 +4602,6 @@ pub fn document() -> Class { attr("mime", "String"), attr("pages", "Vec"), attr("fields", "Vec"), - // Minted later (append-only basis): the stored-file view of a - // document, as a document store lists it. - Attribute::appended("name", "string"), - Attribute::appended("uploaded_at", "datetime"), - Attribute::appended("keywords", "string"), - Attribute::appended("note", "text"), ]; c } @@ -4706,9 +4691,6 @@ pub fn diagnosis() -> Class { resolved_date, is_primary, note, - // Minted after the edges (append-only basis): the date the finding - // was recorded, which `onset_date` is not. - Attribute::appended("recorded_on", "date"), ]; c } @@ -4743,10 +4725,6 @@ pub fn lab_value() -> Class { reference_range, abnormal_flag, collected_at, - // Minted after the edges (append-only basis). `recorded_at` is when - // the result was entered, not when the sample was collected. - Attribute::appended("recorded_at", "datetime"), - Attribute::appended("order_id", "string"), ]; c } @@ -4796,16 +4774,7 @@ pub fn treatment() -> Class { performed_at.type_name = Some("datetime".to_string()); let mut outcome = Attribute::new("outcome"); outcome.type_name = Some("string".to_string()); - c.attributes = vec![ - code, - description, - performed_at, - outcome, - // Minted after the edges (append-only basis): a treatment that runs - // over a period, and how many sessions it covers. - Attribute::appended("ended_at", "datetime"), - Attribute::appended("session_count", "integer"), - ]; + c.attributes = vec![code, description, performed_at, outcome]; c } @@ -4902,33 +4871,6 @@ pub fn investigation() -> Class { c } -/// SpecialistReferral — a referral of a patient to a specialist -/// (`0x090D`). The referral runs from a start date until it is closed, is -/// either open or done, and names the practitioner referred to. -#[must_use] -pub fn specialist_referral() -> Class { - let mut c = Class::new("SpecialistReferral"); - c.language = Language::Unknown; - c.canonical_concept = Some("specialist_referral".to_string()); - c.description = Some("A referral of a patient to a specialist".to_string()); - c.associations = vec![ - family_edge("patient", "Patient"), - family_edge("referred_to", "Practitioner"), - ]; - let typed = |name: &str, ty: &str| { - let mut a = Attribute::new(name); - a.type_name = Some(ty.to_string()); - a - }; - c.attributes = vec![ - typed("start_date", "date"), - typed("end_date", "date"), - typed("status", "string"), - typed("note", "text"), - ]; - c -} - /// Examination — a performed examination record (`0x090A`), distinct /// from [`investigation`] (performed exam vs ordered workup). Parallel /// grounding: OGIT `Healthcare:Assessment` (upstream 2026-07-06, not @@ -4942,12 +4884,7 @@ pub fn examination() -> Class { c.associations = vec![family_edge("patient", "Patient")]; let mut performed_at = Attribute::new("performed_at"); performed_at.type_name = Some("datetime".to_string()); - c.attributes = vec![ - performed_at, - // Minted after the edges (append-only basis). - Attribute::appended("form_kind", "string"), - Attribute::appended("note", "text"), - ]; + c.attributes = vec![performed_at]; c } @@ -6361,7 +6298,6 @@ mod tests { "examination", "practitioner", "external_practice", - "specialist_referral", ] { let id = canonical_concept_id(health_concept) .unwrap_or_else(|| panic!("{health_concept} missing from codebook")); @@ -6495,7 +6431,6 @@ mod tests { (examination, "examination", 0x090A), (practitioner, "practitioner", 0x090B), (external_practice, "external_practice", 0x090C), - (specialist_referral, "specialist_referral", 0x090D), ] { let c = builder(); assert_eq!(c.canonical_concept.as_deref(), Some(concept)); @@ -6515,25 +6450,17 @@ mod tests { // via the production lift, never by hand. Growing one of these // sets is a deliberate act: bring the harvest evidence and update // this pin in the same change. - // - // Grown with evidence (2026-10-15): `examination` gained `form_kind` - // and `note`, and `specialist_referral` was minted with four - // attributes. Each is a column of a harvested MedCare screen - // (FormularMaster: kind label and free text; Facharztüberweisung: - // start, stop, done flag, notice), appended after the edges so no - // earlier position moves. - for (c, expected_attrs, expected_edges) in [ - (anamnesis(), 1usize, 1usize), - (investigation(), 1, 1), - (examination(), 3, 1), - (practitioner(), 1, 0), - (external_practice(), 1, 0), - (specialist_referral(), 4, 2), + for (c, expected_edges) in [ + (anamnesis(), 1usize), + (investigation(), 1), + (examination(), 1), + (practitioner(), 0), + (external_practice(), 0), ] { assert_eq!( c.attributes.len(), - expected_attrs, - "{}: harvest-derived mint grew past its evidenced schema", + 1, + "{}: harvest-derived mint grew past its name-level schema", c.name, ); assert_eq!( @@ -6591,7 +6518,6 @@ mod tests { "examination", "practitioner", "external_practice", - "specialist_referral", ], "Health domain set drift — re-sync the consumer coverage gate", ); @@ -6599,8 +6525,8 @@ mod tests { for (_, id) in concepts_in_domain(ConceptDomain::Health) { assert_eq!(canonical_concept_domain(id), ConceptDomain::Health); } - // Counts line up with the codebook blocks (7 OGIT + 6 harvest mints). - assert_eq!(concepts_in_domain(ConceptDomain::Health).count(), 13); + // Counts line up with the codebook blocks (7 OGIT + 5 harvest mints). + assert_eq!(concepts_in_domain(ConceptDomain::Health).count(), 12); // 0x08XX OCR: nine container KINDS (unicharset/recoder/charset/ // network_layer + the PDF→text plan mints textline/blob/page_layout/ // page_image/ocr_renderer) plus the generic `document` (0x080B — the diff --git a/crates/ogar-vocab/src/ports.rs b/crates/ogar-vocab/src/ports.rs index 5ec7e2e3..9686e7b6 100644 --- a/crates/ogar-vocab/src/ports.rs +++ b/crates/ogar-vocab/src/ports.rs @@ -908,7 +908,7 @@ mod tests { // Patient / Diagnosis / LabValue / Medication / Treatment / Visit // / VitalSign — the 7 OGIT `NTO/Healthcare/entities/` classes this // port was lifted from. Deliberately NOT the Health-domain codebook - // cardinality (13): the 0x0908..0x090D harvest-derived mints carry + // cardinality (12): the 0x0908..0x090C harvest-derived mints carry // no OGIT entity and therefore no port alias. (0x090C // external_practice has a mechanical sql_mirror TTL stub but no // curated OGIT NTO/Healthcare entity — the port mirrors the curated