From c4ba85bd1ad42f7e354e0623e679307c742dcc2a Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 31 Aug 2026 17:54:21 +0000 Subject: [PATCH 1/2] ogar-obo: row_schema_of accepts both address forms of an aliased namespace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The S3 producer flip (#293) set the reader rule at from_concept_id — both 0x0301 and 0x9101 resolve Mondo — but row_schema_of still probed the registry with the RAW concept id, so a pre-flip artifact's legacy ids stopped resolving the carve the moment the registry minted the domain forms. Fold through the namespace alias before the registry probe; concepts without an alias (PATO/RO, spine, foreign) probe as themselves, and 0x0306 / non-OBO ids stay refused. --- crates/ogar-obo/src/layout.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/crates/ogar-obo/src/layout.rs b/crates/ogar-obo/src/layout.rs index dd863dc7..38389e91 100644 --- a/crates/ogar-obo/src/layout.rs +++ b/crates/ogar-obo/src/layout.rs @@ -101,13 +101,22 @@ const _: () = assert!(OBO_CORE_ROW.edge_lanes.bytes == OBO_CORE_ROW.lane_count * /// (`ogar-ro`'s relation-BODY classid, whose 512-byte row is `ogar-loco` /// call-slab shaped, not this schema) and every concept outside the /// registries. Refused, not guessed. +/// +/// Accepts BOTH address forms of an aliased namespace (the reader rule the S3 +/// producer flip set for [`Namespace::from_concept_id`]): a pre-flip artifact +/// keyed `0x0301` carries exactly this carve, so its legacy concept id must +/// resolve to the same schema the domain form does. #[must_use] pub fn row_schema_of(concept: u16) -> Option<&'static ClassRowSchema> { + // Fold a legacy alias onto its canonical (registry-minted) form before the + // registry probe; a concept without an alias probes as itself. + let canonical = + crate::Namespace::from_concept_id(concept).map_or(concept, crate::Namespace::concept_id); let known = registry::OBO_CORE .specs() .iter() .chain(registry::META_STUDY_SPINE.specs()) - .any(|s| s.concept_id == concept); + .any(|s| s.concept_id == canonical); known.then_some(&OBO_CORE_ROW) } @@ -131,6 +140,21 @@ mod tests { } } + /// A pre-flip artifact's rows carry the legacy ids and EXACTLY this + /// carve — the reader rule (`from_concept_id` accepts both) extends to + /// the schema resolution, or a legacy artifact becomes unreadable the + /// moment the registry mints the domain form. + #[test] + fn legacy_alias_concepts_resolve_to_the_same_schema() { + for legacy in [0x0301u16, 0x0302, 0x0303] { + assert_eq!( + row_schema_of(legacy), + Some(&OBO_CORE_ROW), + "{legacy:#06x} (legacy alias) must resolve the declared carve" + ); + } + } + #[test] fn foreign_concepts_are_refused_not_guessed() { // 0x0306 is ogar-ro's relation-body classid — call-slab shaped, NOT From 6392bcf990bc58d5e0a6988e761aaa34a44b745f Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 31 Aug 2026 17:57:47 +0000 Subject: [PATCH 2/2] ogar-obo: derive crosswalk's ANATOMY/DISEASE_CLASSID from the enum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The two export constants were literals (0x0303/0x0301) and the flip missed them — resolve_fma rendered 0x9303 while ANATOMY_CLASSID still said 0x0303, caught by MedCare's conformance suite. Derived from Namespace::concept_id() so they can never disagree with the renderer again. --- crates/ogar-obo/src/crosswalk.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/crates/ogar-obo/src/crosswalk.rs b/crates/ogar-obo/src/crosswalk.rs index b905daad..6d321b3d 100644 --- a/crates/ogar-obo/src/crosswalk.rs +++ b/crates/ogar-obo/src/crosswalk.rs @@ -130,11 +130,15 @@ impl Crosswalk { } } -/// The Uberon anatomy classid (`0x0303`), for a consumer that resolves an FMA -/// zone and wants to confirm the returned address is anatomy. -pub const ANATOMY_CLASSID: u32 = 0x0303_0000; -/// The MONDO disease classid (`0x0301`). -pub const DISEASE_CLASSID: u32 = 0x0301_0000; +/// The Uberon anatomy classid, for a consumer that resolves an FMA zone and +/// wants to confirm the returned address is anatomy. DERIVED from the enum +/// (never a literal), so it moved with the S3 producer flip (#293: +/// `0x0303_0000 -> 0x9303_0000`) instead of silently disagreeing with what +/// [`Crosswalk::resolve_fma`] actually renders — which is exactly how the +/// stale literal was caught (MedCare's conformance suite). +pub const ANATOMY_CLASSID: u32 = (crate::Namespace::Uberon.concept_id() as u32) << 16; +/// The MONDO disease classid (derived, see [`ANATOMY_CLASSID`]). +pub const DISEASE_CLASSID: u32 = (crate::Namespace::Mondo.concept_id() as u32) << 16; #[cfg(test)] mod tests {