Skip to content

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 placeholder flag) 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 Unassigned location inside an Unassigned department — 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 nomenclature array; no promotion rule.
  • Profile: UMDNS Name maps 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 in sourceData.
  • 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 explicit column_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/dataQuality exist; 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 in sourceData). Persisted at rest in source_attributes JSONB ({source_system, source_record_id, fields}), GIN-indexed and searchable; export writes fields back as sourceData. 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.

Ovahol developer documentation