Maintenance and Release Updates
The mapper has two kinds of data changes:
- Reviewed vocabulary changes, which can be deployed without rebuilding models.
- Terminology or model release changes, which require new derived artifacts, validation, and a controlled worker promotion.
Never overwrite the active release in place. Build a sibling versioned bundle, validate it, then switch the deployment pointer.
Routine Checks
Run these on a scheduled operations cadence and before every deployment:
$env:PYTHONPATH = "$PWD\src"
python -m loinc_mapper validate-assets --umls-path assets/umls/2026AA
python -m unittest discover -s tests -v
python -m mkdocs build --strict
Also run a batch smoke set containing LDL/LDLP, mass versus particle units, calculated versus direct methods, specimen differences, an OCR misspelling, and an intentionally unknown term. Confirm that every result has complete provenance and that dangerous unit/property cases do not map.
Include these release-derived checks in the smoke set:
COMMON_TEST_RANKis present for ranked release terms and only changes candidate ordering by a small bounded prior.ORDER_OBS=Orderterms are rejected for individual result rows, whileObservationandBothremain eligible.Blood, Venouscan reach validBldandSer/Plaschemistry/hematology terms but cannot reach urine, CSF, arterial, or capillary terms.- UCUM forms such as
IU/mL,u[IU]/mL,mg/dL, andmmol/Lretain correct dimensions.
Track these operational metrics separately:
- accepted precision and coverage on the frozen expert holdout.
- abstention rate and review queue age.
- dangerous false-positive count.
- top-1/top-3 retrieval recall and confidence calibration.
- per-laboratory performance and new-alias volume.
- UMLS backend, FAISS backend, artifact release, and model versions.
Reviewed Mapping Updates
Use this path for a newly observed local laboratory name or a confirmed OCR surface form:
unknown row -> candidate explanation -> review cluster -> expert decision
-> universal template or source evidence -> tests/gold set -> deployment -> monitor
Add the approved mapping through the review workflow, not by editing Python logic. Include required units and exclusions when the same phrase could denote different tests. Examples such as LDL versus LDLP must include hard unit or axis protection. A registry entry is still validated by the mapper; it is not an unconditional Elation override.
Choose the registry layer deliberately:
universal_templatemeans a reviewer confirmed the display and constraints are clinically invariant across laboratories. The original source can be retained as supporting provenance, but it is not a runtime requirement.source_evidencemeans the local test ID, method, or report style is needed to interpret the row. It is usable only for the exact source laboratory and must never block the universal LOINC-derived path for an unseen lab.
Audit the live registry before a migration and export a layered configuration only after that review:
python -m loinc_mapper audit-registry-scopes `
--registry config/mapping_registry.json `
--output evaluation/registry_scope_audit.json
python -m loinc_mapper export-layered-registry `
--registry config/mapping_registry.json `
--output config/mapping_registry.layered.json
Use cluster-review-queue to identify repeated unknown surfaces across
multiple laboratories. A cluster is a review draft only; it never creates a
production mapping automatically.
For names with LOINC siblings, keep the aliases unit- and property-constrained.
For example, the active serum/plasma SHBG pair is 13967-5 with SCnc and
nmol/L, versus 2942-1 with MCnc and ug/dL; Free Androgen Index is
24125-7 with SRto and %. Prefer official LOINC related-name evidence for
candidate synonyms, then require clinical review of the exact laboratory
surface form. Do not promote a judge/LLM suggestion directly when it conflicts
with the pinned catalog axes.
The server-selected clinician-fast publication policy enables
fast_path_enabled only after one batched exact replay of a verified clinician
universal template. This is still an engineering check, not a clinician UI
choice. A contained alias, legacy review, or any source_evidence record
always uses the full pipeline. Strict full/unseen replay remains the release
audit policy.
After a registry or reviewed-axis update:
- Add a regression test or expert-labeled row.
- Run the complete unit and safety suite.
- Run the affected laboratory batch and compare accepted precision.
- Build and promote a new configuration version.
- Restart workers so each worker loads the same registry version.
No LOINC catalog or FAISS rebuild is needed for a registry-only change.
Immutable Registry Publication
The main project, not this package, owns the durable review ledger and active registry pointer. Publish each registry update as follows:
- Persist append-only clinician decision events with case ID/revision, reviewer identity, rationale, and mapper provenance.
- Validate decisions with
validate_review_decisionand compile a candidate snapshot withcompile_registry_snapshot. - For normal clinician publication, run one
clinician_fast_replaybatch on the original reviewed facts and finalize only when it passes. - Run full original/unseen plus fast-equivalence replay asynchronously for release audits, not as a browser-blocking publish prerequisite.
- Store
mapping_registry.<version>.<sha>.jsonand its manifest as immutable private objects. The manifest records parent version, LOINC release, approval IDs, checksum, and replay result hash. - Move the active pointer with a storage generation precondition. Never overwrite a snapshot or mutate a running worker's registry file.
- Launch new mapping jobs with the explicit snapshot URI, generation, hash, and version. Existing jobs finish with their original snapshot.
Rollback means moving the active pointer to a previously finalized immutable snapshot, then launching new jobs. Do not edit a snapshot in place or erase the review event that caused it. See Clinician-governed learning for the UI and CSV contract.
For recurring Wellspan CBC rows, keep the exact report surface and units in a
source-evidence entry. The current report uses K/mcL for counts, M/mcL for
RBC, fL for MPV/RDW-SD, and % for differential fractions. Do not replace
these with a global WBC or Platelets shortcut without
class/property/unit constraints. For Testosterone, Free, retain the source
method and report document because pg/mL also has calculated and
detection-limit siblings. Normal source-neutral Component/System/Time
retrieval still runs for every new laboratory.
If a new report produces invalid_candidate for a familiar CBC analyte, inspect
result.unit_evidence first. An invalid display unit should be fixed in the
UCUM surface grammar; a correct unit with multiple surviving siblings should
be addressed by a reviewed source-laboratory mapping. Do not lower the
confidence margin to force a code.
Source-laboratory evidence records are review drafts until a clinical reviewer confirms the exact local test definition. Do not promote a proposed target solely because a report name looks familiar; confirm the performing laboratory, specimen, result style, units, method, source document, and current LOINC term. Do not promote it globally merely because one vendor's name looks like a universal synonym.
New LOINC Release
When a new LOINC release is adopted, keep the old release available for rollback and audit:
$release = "NEW_RELEASE"
$out = "assets/loinc/$release"
New-Item -ItemType Directory -Force $out | Out-Null
python -m loinc_mapper build-catalog `
--output "$out/catalog_v5.sqlite3" `
--release $release `
--context-aliases config/axis_context_aliases.json
python -m loinc_mapper build-vectors `
--catalog "$out/catalog_v5.sqlite3" `
--output "$out/terms.faiss" `
--metadata "$out/terms.json" `
--aliases-per-code 4 `
--batch-size 128
python -m loinc_mapper build-code-vectors `
--index "$out/terms.faiss" `
--metadata "$out/terms.json"
The new catalog and vector metadata must report the same LOINC release. Check
the catalog manifest and terms.json; never pair a new catalog with an old
FAISS sidecar. Run validate-assets for UMLS, then run the smoke set and the
expert holdout before promotion.
If the downloaded LOINC release changes file locations or columns, update the build input paths and compiler tests first. The original CSV/accessory files remain the immutable source record for that release.
Build class shards only after benchmarking the global sidecar against expected report routing. They add disk use and must demonstrate no retrieval recall loss on the frozen corpus:
python -m loinc_mapper build-vector-shards `
--catalog "$out/catalog_v5.sqlite3" `
--index "$out/terms.faiss" `
--metadata "$out/terms.json" `
--output "$out/shards"
New UMLS Release
Use only the licensed UMLS artifact selected for this mapper, currently
MRCONSO.RRF, unless the architecture is deliberately changed. Do not fetch
the full UMLS release into a small deployment disk.
Create a new directory and preserve the current release:
$release = "NEW_UMLS_RELEASE"
New-Item -ItemType Directory -Force "assets/umls/$release" | Out-Null
$env:UMLS_API_KEY = "read-from-secret-store"
python -m loinc_mapper download-umls `
--release-url "https://download.nlm.nih.gov/umls/kss/$release/umls-$release-mrconso.zip" `
--output "assets/umls/$release/mrconso.zip"
Extract MRCONSO.RRF according to the NLM release instructions, then build
and upgrade the new index:
python -m loinc_mapper prepare-umls `
--asset-path "assets/umls/$release" `
--output-index "assets/umls/$release/umls.sqlite3" `
--release $release
python -m loinc_mapper upgrade-umls `
--asset-path "assets/umls/$release"
python -m loinc_mapper build-umls-serving `
--asset-path "assets/umls/$release" `
--catalog "assets/loinc/LOINC_RELEASE/catalog_v5.sqlite3" `
--output "assets/umls/$release/serving/umls_loinc.sqlite3"
python -m loinc_mapper validate-assets `
--umls-path "assets/umls/$release"
The raw upgrade and serving build are disk-heavy and modify only the new
directory. Wait for both commands to finish before mapping. The serving build
checks raw checksums once, then emits serving_manifest.json. Confirm runtime
provenance reports serving_exact+fts5+trigram and that release/catalog/vector
metadata remain matched.
SapBERT Model or FAISS Changes
Changing the SapBERT model, alias selection, embedding dimension, or vector
construction parameters requires rebuilding terms.faiss and terms.json.
Treat it as a challenger artifact. Run the same expert holdout and safety
suite, require no dangerous false-positive regression, and retain the old
sidecar for rollback. Do not copy a vector file from one model or release into
another release directory.
Ranker Changes
An AdaRank-compatible or LambdaMART model may be trained from reviewed records, but it must be stored with its feature schema and model version. Compare it to the current champion using laboratory and alias-family holdout splits. Keep the current champion if accepted precision falls or dangerous false positives rise. An untrained challenger is never activated merely because it increases coverage.
Performance Approval
For every release, registry-fast-path change, UMLS serving rebuild, FAISS shard build, or ranker update, run the profile matrix on the frozen expert holdout:
python -m loinc_mapper evaluate `
--input evaluation/gold/holdout.csv `
--details evaluation/gold/profile_matrix.json `
--profile-matrix `
--umls-path assets/umls/2026AA
Record accepted precision, coverage, dangerous false positives, p50/p95 stage latency, and fast-path equivalence. Do not activate a change that loses a known correct mapping, reduces tested accepted precision, or adds a dangerous false positive.
Rollback and Incident Response
If a release causes unexpected mappings, increased dangerous rejections, or a backend downgrade:
- Stop promotion and preserve the failing result/provenance records.
- Switch workers back to the previous complete artifact bundle.
- Restart workers and verify the previous release in health output.
- Route affected rows to review; do not silently remap already-filed data.
- Compare manifests, stage counts, candidate evidence, and label audits.
- Correct the source/configuration and repeat the promotion checks.
Do not manually delete a UMLS SQLite journal or interrupt an in-place upgrade unless disk exhaustion requires it. Build new releases in a sibling directory so interruption does not affect the active artifact.