Skip to content

Commit 2ef1101

Browse files
committed
docs(plans/unified-bridge): §4 reframe — hydrators ARE the spine for O(1) inheritance from family buckets
Two user clarifications, folded into §4: 1. The OWL/DOLCE cross-walk table is not "interop crutches" — it is the SOURCE MATERIAL from which lance-graph-ontology constructs the OGIT/OWL/DOLCE mapping. The hydrators are the construction tool; the cross-walk standards (DOLCE+DUL, OWL-Time, PROV-O, QUDT, schema.org, FIBO, SKOS, Schematron, XSD, SKR DATEV, ZUGFeRD) are the bricks; OGIT canonical surface (per-family codebook + inherits-from DAG + edge whitelists at OGIT::*_V1 slots) is the synthesis. 2. The hydrators + inherits_from + per-family codebook + family- bucket dense array TOGETHER form the spine that makes schema / label / codebook inheritance O(1) cheap at lookup time. §4.1 rewritten as "the source material for the OGIT mapping" — direction-of-build diagram added showing how each external standard flows through its hydrator into its OGIT::*_V1 G-slot. The MetaAnchors fields (foundry_object_type, owl_upper_class, dolce_marker, wikidata_qid) are reframed as the runtime READ SURFACE over the constructed mapping, not as the populated target. DolceMarker enum naming open question (Endurant/ Perdurant vs Object/Event per canonical DUL rename) called out explicitly as a decision needed before D-UB-3. §4.3 lead-in reframed from "the cross-walk surface is now concrete" to "the construction tool that builds the OGIT mapping is now concrete in lance_graph_ontology::hydrators::*". PR #408 reference added (NamespaceRegistry::seed_defaults wires the corresponding G-slots at boot). §4.4 NEW — locks the O(1) inheritance property: - Schema inheritance: inherits_from chain flattened into FamilyEntry.axiom_blob at hydration; query-time cost is one masked u16 + one array index = O(1). Zero chain-walks at query time. - Label inheritance: rdfs:label per-locale collapsed during the subClassOf walk at hydration; FamilyEntry.label_* reads are O(1) array index. Zero parent lookup at query time. - Codebook inheritance: per-family centroid references parent codebook by u8 offset when content distributions overlap (with Jirak-aware Berry-Esseen bound per I-NOISE-FLOOR-JIRAK). One indirection max. - Why family buckets vs flat dict: ~5ns L1-cache-resident two array indices vs ~50-100ns hash + collision + cache miss = 20× cost gap. Co-design between construction tool (hydrators) and runtime substrate (family buckets) — neither earns the property alone. Concrete consumer-side payoffs spelled out for woa-bridge, medcare-bridge, smb-bridge: pre-baked schema / label / codebook inheritance means route handlers read one FamilyEntry per row identity; no OWL reasoning, no rdfs:label walk, no Schematron re-parse at query time.
1 parent 137e104 commit 2ef1101

1 file changed

Lines changed: 71 additions & 10 deletions

File tree

.claude/plans/unified-bridge-consumer-migration-v1.md

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -111,18 +111,59 @@ Hot path (every Cypher/SPARQL/Gremlin query that touches a row):
111111

112112
**Per-row LanceDB overhead is 6 bytes** (tenant_id u32 + owl_id u16). The codebook + label + schema + verbs do NOT live on each row — they live ONCE in the static OgitFamilyTable, addressed by the 2-byte owl_id. This is the "O(1) lookup cached in lancedb column" property: the table is materialised at boot, persisted by the `lance-cache` feature so re-hydration is O(rows) once not O(rows × ttl-parse-cost), and consulted by the same masked u16 compare DataFusion lowers Cypher MATCH to (§3.10).
113113

114-
### 4.1 OWL/DOLCE cross-walk surface
114+
### 4.1 OWL/DOLCE cross-walk — the source material for the OGIT mapping
115115

116-
The `MetaAnchors` field (super-domain-rbac §3.5) on each `SuperDomainEntry` carries pointers to upper ontologies:
116+
**The cross-walk table below is not an interop crutch — it is the source material from which `lance-graph-ontology` *constructs* the OGIT/OWL/DOLCE mapping.** External standards (DOLCE+DUL, OWL-Time, PROV-O, QUDT, schema.org, FIBO, SKOS, …) ride in as TTL/OWL artifacts; the hydrators ingest them; the OGIT canonical classification (per-family codebook of §3.3, edge whitelists, inherits-from chain at `OGIT::*_V1` slots) is the *synthesis* that emerges. lance-graph-ontology is the construction site; the hydrators are the construction tool; the cross-walk standards are the bricks.
117117

118-
| Cross-walk | Field | Consulted when | Populated by (concrete hydrator) |
118+
Direction of build:
119+
120+
```
121+
External standards → Hydrator (consumes TTL/OWL/XSD/SKR) → OGIT canonical surface
122+
───────────────────────── ────────────────────────────────── ──────────────────────────────
123+
DOLCE+DUL.owl ──► hydrate_dolce ──► OGIT::DOLCE_V1 bundle
124+
+ DUL extensions (root, inherits_from: None)
125+
+ 17-IRI edge whitelist
126+
127+
OWL-Time.ttl ──► hydrate_owltime ──► OGIT::OWLTIME_V1 bundle
128+
PROV-O.ttl ──► hydrate_provo ──► OGIT::PROVO_V1 bundle
129+
QUDT 2.1 ──► hydrate_qudt ──► OGIT::QUDT_V1 bundle
130+
schema.org.ttl ──► hydrate_schemaorg ──► OGIT::SCHEMAORG_V1 bundle
131+
(all four inherits_from: Some(OGIT::DOLCE_V1.0))
132+
133+
SKOS.ttl ──► hydrate_skos ──► OGIT::SKOS_V1 bundle
134+
FIBO-FND ──► hydrate_fibo_fnd ──► OGIT::FIBO_FND_V1 bundle
135+
FIBO-BE ──► hydrate_fibo_be ──► OGIT::FIBO_BE_V1 bundle
136+
(inherits FND, not DOLCE direct)
137+
138+
ISO Schematron rules ──► SchematronHydrator ──► rule-pattern bundle entries
139+
XSD type defs ──► XsdHydrator ──► type-def bundle entries
140+
ZUGFeRD/Factur-X (EN16931)──► hydrate_zugferd + hydrate_zugferd_rules
141+
(composes XsdHydrator + Schematron) ──► ZUGFeRD-specific bundle
142+
DATEV SKR 03/04/03-Bau ──► SkrHydrator + hydrate_skr* ──► account-hierarchy bundles
143+
(SKR03_IRI_PREFIX etc.)
144+
145+
The OGIT mapping IS the union of:
146+
• OGIT::*_V1 G-slots populated by the above
147+
• the inherits-from DAG that chains them
148+
• the per-family codebook (§3.3) with
149+
FamilyEntry { label_uri, kind, dolce_marker,
150+
owl_characteristics, axiom_blob,
151+
provenance, verbs }
152+
at every OwlIdentity slot
153+
```
154+
155+
The `MetaAnchors` struct on `SuperDomainEntry` (super-domain-rbac §3.5) is the **runtime read surface** over this constructed mapping — the field shapes are:
156+
157+
| MetaAnchors field | What it points at | Built from which hydrator's output | Read at runtime by |
119158
|---|---|---|---|
120-
| Foundry ObjectType | `foundry_object_type: Option<&'static str>` | Customer requests Foundry-shape export | Not auto-populated; manual cross-walk per `super-domain-rbac-tenancy-v1.md` §10 OQ-1. `lance_graph_ontology::hydrators::hydrate_schemaorg` and `hydrate_fibo_be` provide the upper-class anchors Foundry typically maps to. |
121-
| OWL upper class | `owl_upper_class: Option<&'static str>` | OWL reasoner runs over the same registry | `lance_graph_ontology::hydrators::hydrate_dolce` (L1 root, `OGIT::DOLCE_V1`, `inherits_from: None`). Resolves `rdfs:subClassOf` / `owl:equivalentClass` chains for the upper-category subsumption (Object / Event / Quality / Abstract). |
122-
| DOLCE marker | `dolce_marker: DolceMarker` (Endurant / Perdurant / Quality / Abstract) | Per-row philosophical-category tagging | `lance_graph_ontology::hydrators::hydrate_dolce` populates the L1 bundle; downstream hydrators (`hydrate_owltime`, `hydrate_provo`, `hydrate_qudt`, `hydrate_schemaorg`, …) `inherits_from: Some(OGIT::DOLCE_V1.0)` and align via the `dul:isClassifiedBy` / `subClassOf dul:Event|Object|Quality|Abstract` chain DOLCE preserves. **Note:** canonical DOLCE+DUL renames `Endurant → Object` and `Perdurant → Event` per the DUL header — the `DolceMarker` enum either stays on the legacy naming (with downstream consumers aware of the renaming) or migrates at the cost of one source-tree-wide find/replace. Decide before D-UB-3. |
123-
| Wikidata QID | `wikidata_qid: Option<u64>` | Wikidata sync / sameAs link generation | Not in the current hydrator surface — would need a `hydrate_wikidata` glue (~50 LOC) following the same `OwlHydrator { g, version, domain_name, inherits_from, starting_entity_id }` pattern. Probably deferred until a tenant explicitly requests Wikidata sync. |
159+
| `foundry_object_type: Option<&'static str>` | Foundry ObjectType string like `"PhysicalSystem"` | Cross-walk authored from `hydrate_schemaorg` + `hydrate_fibo_be` outputs (the upper-class anchors Foundry maps to). NO auto-population — per `super-domain-rbac-tenancy-v1.md` §10 OQ-1 ("Foundry ObjectType cross-walk targets … need product-side input"). | Foundry-shape export path; not by `authorize()`. |
160+
| `owl_upper_class: Option<&'static str>` | OWL upper-class IRI like `"BiomedicalOntology"` | `hydrate_dolce`'s `OGIT::DOLCE_V1` bundle (the 17-IRI cascade preserves `rdfs:subClassOf` / `owl:equivalentClass` chains). | OWL reasoner running over the registry. |
161+
| `dolce_marker: DolceMarker` (Endurant / Perdurant / Quality / Abstract) | DOLCE primary category at the per-row philosophical-category tag | `hydrate_dolce` populates DOLCE_V1; downstream `hydrate_owltime` / `provo` / `qudt` / `schemaorg` align via `inherits_from: Some(OGIT::DOLCE_V1.0)` + `dul:isClassifiedBy` / `rdfs:subClassOf dul:Event\|Object\|Quality\|Abstract`. **DolceMarker enum naming open question:** canonical DOLCE+DUL renamed `Endurant → Object` and `Perdurant → Event` in the DUL header — the enum variants either stay on the legacy `Endurant/Perdurant` (with downstream consumers aware of the rename) or migrate to `Object/Event`. Decide before D-UB-3 + reflect in the §4.3 hydrator inventory. | Per-row tagging in `FamilyEntry.dolce_marker`. |
162+
| `wikidata_qid: Option<u64>` | Wikidata QID like `Q11190` | NOT YET BUILT — no `hydrate_wikidata` hydrator exists. Adding one is ~50 LOC of `OwlHydrator { g: OGIT::WIKIDATA_V1.0, inherits_from: Some(OGIT::DOLCE_V1.0), … }` glue once a tenant requests Wikidata sync. | Wikidata sync / `sameAs` link generation. |
163+
164+
The `MetaAnchors` fields are **the read API over what the hydrators built**. The 4-stage `authorize()` doesn't consult them directly — it operates on `OwlIdentity` against the per-family codebook. The cross-walks are consulted when interop with an external system (Foundry export, OWL reasoner, Wikidata sync) is requested.
124165

125-
These are **interop crutches**, not the hot path. The 4-stage `authorize()` never consults them; the per-family codebook is the runtime fast lane. OWL/DOLCE values ride in `FamilyEntry.axiom_blob` for slots that carry semantic obligations (functional / transitive / inverseFunctional / etc., per `GLUE_LAYER_OGIT_TO_OWL_SPEC.md` 1-byte bitfield).
166+
OWL property characteristics (functional / transitive / inverseFunctional / etc., per `GLUE_LAYER_OGIT_TO_OWL_SPEC.md` 1-byte bitfield in `FamilyEntry.owl_characteristics`) flow the same way — `hydrate_dolce` + the L2/L3 hydrators preserve the OWL axioms in `FamilyEntry.axiom_blob`, and the bitfield is the runtime cache over them.
126167

127168
### 4.2 Lance-cache persistence (the "cached in lancedb column" property)
128169

@@ -148,9 +189,9 @@ Reads are append-only; writes go through `MappingProposal::sha256()`-keyed dedup
148189

149190
**This is what makes the codebook "O(1) cached in lancedb column":** the column store IS the cache; reads are scan-then-build-in-memory once at boot; lookups are array-index on `OwlIdentity.slot()` after.
150191

151-
### 4.3 Hydrator inventory (post-PR #407)
192+
### 4.3 Hydrator inventory (post-PR #407) — the construction tool
152193

153-
The OWL/DOLCE/OGIT cross-walk surface this plan referenced abstractly is now concrete in `lance_graph_ontology::hydrators::*` (PR #407 + the ~11 preceding feature commits). Every hydrator follows the same shape — a free function `hydrate_<name>(registry: &OntologyRegistry) -> Result<u32, HydrateErr>` that constructs an `OwlHydrator { g: OGIT::<SLOT>.0, version: OGIT::<SLOT>.1, domain_name: "<name>".to_string(), inherits_from: Option<u32>, starting_entity_id: 100 }`, calls `.hydrate(ttl_path, registry)` (or `.hydrate_many(&paths, registry)` for multi-file bundles like DOLCE+DUL+extensions), then registers an edge-IRI whitelist via `registry.register_edge_types(g, &EDGE_WHITELIST)`. The return value is the OGIT `G` slot u32 the bundle landed in.
194+
The hydrators that build the OGIT/OWL/DOLCE mapping (per §4.1) are now concrete in `lance_graph_ontology::hydrators::*` (PR #407 + the ~11 preceding feature commits; PR #408 wires the corresponding `NamespaceRegistry::seed_defaults` entries so the OGIT G-slots register at boot). Every hydrator follows the same shape — a free function `hydrate_<name>(registry: &OntologyRegistry) -> Result<u32, HydrateErr>` that constructs an `OwlHydrator { g: OGIT::<SLOT>.0, version: OGIT::<SLOT>.1, domain_name: "<name>".to_string(), inherits_from: Option<u32>, starting_entity_id: 100 }`, calls `.hydrate(ttl_path, registry)` (or `.hydrate_many(&paths, registry)` for multi-file bundles like DOLCE+DUL+extensions), then registers an edge-IRI whitelist via `registry.register_edge_types(g, &EDGE_WHITELIST)`. The return value is the OGIT `G` slot u32 the bundle landed in — i.e., the address of the synthesised mapping in OGIT space.
154195

155196
**Generic substrate (the bO-* scaffold every hydrator instantiates):**
156197

@@ -223,6 +264,26 @@ use lance_graph_ontology::{
223264

224265
The bridge constructor never sees `OwlHydrator` directly — it sees a hydrated `OntologyRegistry`. The deployment / `main.rs` of each consumer chooses the hydration menu.
225266

267+
### 4.4 The hydrators are the spine that makes inheritance O(1) from family buckets
268+
269+
The shape that earns the cost-model gains: **`inherits_from` + per-family codebook + family-bucket dense array together make schema, label, and codebook inheritance O(1) at lookup time.** Spelled out:
270+
271+
| Inheritance flavour | Substrate | Lookup cost | Pre-bake step |
272+
|---|---|---|---|
273+
| **Schema inheritance** (a `Patient` in Healthcare resolves to `dul:Object → dul:Agent → dul:PhysicalAgent` per the DOLCE chain) | Each hydrator's `inherits_from: Option<u32>` chains its OGIT G-slot to its parent. `OGIT::DOLCE_V1` is the root (None). At hydration time, `OntologyRegistry` walks the `rdfs:subClassOf` / `owl:equivalentClass` chain ONCE and **flattens** it into `FamilyEntry.axiom_blob`. | O(1) — one masked u16 compare into `OgitFamilyTable[OgitFamily.0 as usize]`, then `.entries[OwlIdentity.slot() as usize]`. **Zero chain-walks at query time.** The flattened chain rides in the static blob. | At hydration: walk the OWL/DOLCE/PROV-O `subClassOf` graph from leaf to root, materialise as `axiom_blob`. Per-family lock-in by `inherits_from`. |
274+
| **Label inheritance** (`rdfs:label@de` / `rdfs:label@en` propagated from parent when child lacks an override) | `hydrate_<name>` reads `rdfs:label` per-locale at TTL parse time. When a slot has no own label, the inherits-from walker copies the parent's label into `FamilyEntry.label_uri` (or `.label_de` / `.label_en` once those columns ride the FamilyEntry — see `lance-cache` schema §4.2). | O(1) — same `OgitFamilyTable` array index → `FamilyEntry.label_*` field read. **Zero parent lookup at query time.** | At hydration: per-locale label collapse during the subClassOf walk. |
275+
| **Codebook inheritance** (per-family centroid reuses when a slot's content distribution overlaps the parent family's) | `PerFamilyCodebook` (§3.3) is sized 5-8 bit per family; when a child family's slot range overlaps the parent's centroid space, the child's `centroid_blob` REFERENCES the parent's centroid index directly (no per-child re-quantisation). The reference is a u8 offset into the parent codebook. | O(1) — `FamilyEntry.centroid_blob``parent_family.codebook[centroid_idx]`. **One indirection max.** | At hydration: when a child family's content distribution is statistically close to the parent's (Jirak-aware Berry-Esseen bound per `I-NOISE-FLOOR-JIRAK`), emit a centroid-reference instead of re-fitting. |
276+
277+
**Why family buckets, not a flat dict:** A flat OGIT URI → entry HashMap costs ~50-100ns per lookup at scale (hash + collision walk + cache miss). The 256-slot dense `[Option<FamilyEntry>; 256]` per family + the high-byte family index makes it **two array indices, both predictable, both L1-cache-resident**: ~5ns. The 20× cost difference is the headline gain from the layered substrate.
278+
279+
**What this means for downstream consumers:**
280+
281+
- `woa-bridge` doesn't pay the OWL-reasoning cost at every `wo_detail` query. Whatever schema / label / codebook inheritance applies to `ogit.WorkOrder:Customer` is pre-baked into the `FamilyEntry` at boot; the route handler reads one entry in one array index.
282+
- `medcare-bridge`'s clinical-code lookups (ICD-10 / SNOMED via `hydrate_skos`) inherit their hierarchy chain at hydration; runtime cohort queries walk only the per-row identity, never the standards' subClassOf chain.
283+
- `smb-bridge`'s X-Rechnung validation against ZUGFeRD Schematron rules doesn't re-parse the rule set at every invoice — the rules are baked once at boot via `hydrate_zugferd_rules` into the same family-bucket structure, and the per-invoice check is one masked predicate per rule.
284+
285+
**The construction tool (hydrators) and the runtime substrate (family buckets) are co-designed:** the hydrators only earn the O(1) property because they flatten the inheritance chains at construction time; the family buckets only earn the cost-model gain because the hydrators populate them with per-slot dense entries. Neither half works alone.
286+
226287
---
227288

228289
## 5 — Deliverables

0 commit comments

Comments
 (0)