Ovahol Interchange Implementation Profile
Base spec: ovahol/interchange-schema v0.1.0, vendored byte-identical at backend/internal/modules/interchange/schema/v0.1.0.json. Every bundle Ovahol emits validates against the unmodified draft.
This document records where Ovahol's behavior goes beyond the draft's letter. Nothing here violates the schema — but none of it is derivable from the schema either. A second system reading our bundles needs this page, not just the JSON file. Each section ends with what we'd propose upstream.
1. Composite unknown-model names
- Spec: silent on placeholders.
- Profile: an unresolvable model is named
Unknown Model - {brand} {device}(e.g.Unknown Model - Unknown Brand Multiparametric patient monitor), via the single shared helper. Brand/device unknowns stay bare (Unknown Brand,Unknown Manufacturer); only models compose. - Why: per-brand-per-device placeholders survive import, match on re-import, and read unambiguously in review — one bare "Unknown Model" per row does none of that.
- Upstream: standardize a placeholder naming convention (or a first-class
placeholderflag) so importers can distinguish "unknown, resolve later" from real catalog names.
2. Unassigned holding location
- Spec: assets need resolvable placement; unplaced assets are undefined behavior.
- Profile: rows with neither location nor department land in an explicit
Unassignedlocation inside anUnassigneddepartment — never the arbitrary first location, never dropped. Literal "Unknown"/"N/A"/"-" cells fold to missing first. - Why: every converted asset must stay placeable; silent misplacement (first-location inheritance) and silent drops are both worse than an honest holding pen, which matches Ovahol's existing holding-location concept for transfers.
- Upstream: define unplaced-asset handling (holding-location convention or an explicit nullable placement with a reason code).
3. UMDNS → device fallback
- Spec: definitions carry a
nomenclaturearray; no promotion rule. - Profile:
UMDNS Namemaps to the device slot but only wins when no equipment/device column claims it (deterministic first-wins race). Rows with an empty equipment name but a UMDNS name get a real definition (e.g.Refrigerators, Blood Bank) instead of the model fallback. The losing column stays preserved insourceData. - Why: UMDNS is device nomenclature; using it beats naming a definition after a model number.
- Upstream: a stated precedence (equipment name > nomenclature > model fallback) for definition naming.
4. Header normalization and mapping policy
- Spec: silent (bundles, not foreign files).
- Profile: case/separator/diacritic folding, parenthetical qualifier stripping (
Equipment Name(common)), camelCase synonyms (SerialNumber,EquipmentID), deterministic tie-breaking (sorted headers, filled cells claim first, empties never steal slots), and per-file explicitcolumn_mapping(exposed in the UI) for idiosyncratic columns. Unmapped columns are preserved verbatim, never dropped; the report lists only never-claimed headers. - Why: real inventories need zero-code onboarding; determinism makes converts reproducible.
- Upstream: nothing — this is importer-local concern, correctly out of spec scope. Documented here so operators know the rules.
5. Misplaced-value gates and preservation
- Spec:
sourceData/dataQualityexist; no policy on use. - Profile: date-like, letter-less, or condition-vocabulary text in name columns folds to missing (Unknown placeholders) with the raw text preserved (
*_dropped_not_a_name, full row insourceData). Persisted at rest insource_attributesJSONB ({source_system, source_record_id, fields}), GIN-indexed and searchable; export writes fields back assourceData. Round trips lose nothing. Provenance (migration authorship) is immutable — updates never touch the column. - Why: shifted/mislabeled columns are the norm in hospital files; the alternative is catalog entries named
10/15/22(seen live). - Upstream: a
dataQuality-adjacent convention for "value present but not credible as a name" — currently each importer invents its own.
6. Recommendation staging
- Spec: silent (dictionary governance is outside the bundle).
- Profile: unmatched manufacturers, brands, eponymous brands (same-named brand under a brand-less manufacturer), models (with eponymous-brand fallback), and devices are staged for super-admin review — never auto-created. Devices carry lookup-verified default classification for the reviewer to correct. Staging is atomic (validate-all, save in one transaction) and deduped against pending/rejected rows; every skipped name carries an operator-actionable reason.
- Why: migration must propose, never pollute, the shared catalog.
- Upstream: a standard "proposed new dictionary entry" envelope would let any importer stage into any review queue.
7. Dry-run fidelity contract
- Spec: silent on execution.
- Profile: dry-run executes the identical apply path inside an always-rolled-back transaction — including catalog ensures, which ride the context transaction (a past leak of placeholder rows past rollback is fixed and regression-tested). Per-record savepoints keep one bad row from poisoning the batch. Claimed accuracy: same-path predictive, not literally 100% — concurrent catalog edits between dry-run and apply can still diverge outcomes.
- Upstream: a conformance-test hook ("dry-run must leave zero side effects") any implementation can self-certify against.
8. Identity and idempotency
- Spec: identifiers travel; matching policy is importer-defined.
- Profile: serial > UDI > tag; junk placeholders (
n/a,-,unknown) never match; re-import links instead of duplicating; exact-alias device names resolve to canonical names (common names never leak into definitions); genuine ties stay human. - Upstream: a recommended matching precedence would make cross-system imports converge instead of each importer guessing differently.
9. Condition mapping policy
- Spec: operational conditions are open codes.
- Profile: a curated free-text → condition map; ambiguous statuses (
Inactive) deliberately omit + annotate rather than guess, defaulting to New/Commissioning at import. Absence of a mapping is always visible (dataQuality, skip reasons), never silent. - Upstream: candidate operational-condition vocabulary with an explicit "ambiguous — do not map" list, so omissions are interoperable instead of implementation-specific.