Skip to content

fix(hts): align tx-ecosystem conformance with 2026-07-01 upstream changes#189

Merged
smunini merged 9 commits into
mainfrom
fix/hts-tx-ecosystem-conformance-2026-07
Jul 3, 2026
Merged

fix(hts): align tx-ecosystem conformance with 2026-07-01 upstream changes#189
smunini merged 9 commits into
mainfrom
fix/hts-tx-ecosystem-conformance-2026-07

Conversation

@smunini

@smunini smunini commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Why

The nightly HTS Terminology IG Conformance run went red on 2026-07-02 after an 8+ day green streak — failing 14 of 600 tests on both backends (sqlite/R4 and postgres/R5). The validator (runner v6.9.11, tests v1.90) and the HTS binary were byte-identical to the last green run; only the upstream HL7/fhir-tx-ecosystem-ig suite — which this workflow checks out at HEAD every night — moved. Four commits landed upstream on 2026-07-01 (after our green run, before the red one) and tightened expected server behavior. This PR updates HTS to match.

Root-cause investigation: run 28565301822.

Changes

Group Tests fixed Upstream commit Change
A+D permutations/good-cc2-* (6), overload/validate-good2a b5658de8 CodeableConcept $validate-code returns the first valid coding, not the last (both CS and VS paths). good2a's version 1.0.02.0.0 falls out of first-coding selection.
B validation/validation-contained-good, inactive/inactive-3{,a,b}-validate, batch/batch-validate (5) 030182b7 Inactive+retired concepts emit a single merged warning ("...has a status of retired and inactive...") instead of two issues.
C tho/act-exclusion (1) 7409a11a Expansion dedupes identical (property, value) pairs. concept_property_values joins by CodeSystem URL only, so a URL stored in two versions returned each concept's status once per version.
search/search-filter-yes (1) 7409a11a Added to IGNORED_TESTS — not an HTS bug. See below.

search/search-filter-yes — upstream wiring gap

The primary response fixture is a hierarchical expansion. Upstream added a flat variant (search-expand-filter-yes-flat-response.json) but never wired it via a response:flat key in test-cases.json (unlike search-all-yes, which got both). HTS returns a spec-valid flat $expand that matches that flat fixture byte-for-byte. Excluded from the gate pending an upstream PR to add the missing response:flat wiring.

Verification

  • cargo test -p helios-hts — all suites pass
  • Builds on both backends (sqlite default + --features postgres,R4)
  • cargo fmt --check clean; cargo clippy exits 0 (no new warnings)

The full HL7 tx-ecosystem IG bench is the CI job itself (HTS Terminology IG Conformance) — the true green confirmation comes from running it against this branch.

smunini added 6 commits July 2, 2026 09:37
Establish the structure and rules of the road for multi-language support
across the stack:

- locales/{en,es,de}/main.ftl — seeded Fluent UI message catalogs
  (English source + Spanish + German)
- locales/README.md — catalog conventions
- docs/multi-language.md — discussion document covering the front-to-back
  approach, locale negotiation, UI/error/content/terminology layers,
  formatting, security, and language roadmap. Notes existing HTS SNOMED
  multi-language import/display as the terminology localization layer.

Relates to #186 (HTMX-first web UI foundation).
Lay the foundation for a server-rendered, HTMX-first web UI per #186.

- New helios-web crate: thin Axum handlers -> Askama (compile-time,
  auto-escaping) templates; no HTML in Rust, no browser-facing JSON API.
- Working POC: active-search demo showing the HX-Request full-page vs.
  fragment pattern, with graceful no-JS fallback.
- Vendored, version-pinned htmx.org@2.0.4 (no runtime CDN); local CSS.
- Template layout: layouts/ pages/ partials/; assets/ for static files.
- README.md: approach, templating trade-offs, file-placement rules of the
  road, asset/security policy, and mount follow-ups, with references.
- Wired into workspace default-members.

Refs #186
…nges

The nightly HTS Terminology IG Conformance run went red after
HL7/fhir-tx-ecosystem-ig landed several expectation changes on 2026-07-01.
The validator and HTS binary were unchanged; only the upstream test suite
moved. This updates HTS to match the tightened expectations.

- CodeableConcept validation now returns the FIRST valid coding, not the
  last (both CodeSystem and ValueSet $validate-code paths), matching
  upstream b5658de8. Fixes permutations/good-cc2-* (6) and
  overload/validate-good2a (its version 1.0.0->2.0.0 falls out of the
  first-coding selection).

- Inactive+retired concepts now emit a SINGLE merged status warning
  ("...has a status of retired and inactive...") instead of two separate
  issues, matching upstream 030182b7. Fixes validation/validation-contained-good,
  inactive/inactive-3{,a,b}-validate, and batch/batch-validate.

- Expansion now dedupes identical (property, value) pairs. concept_property_values
  joins by CodeSystem URL only, so a URL stored in two versions returned each
  concept's `status` property once per version. Fixes tho/act-exclusion.

- search/search-filter-yes added to IGNORED_TESTS: HTS returns a spec-valid
  flat $expand that matches upstream's own search-expand-filter-yes-flat-response.json
  byte-for-byte, but upstream never wired that flat variant via `response:flat`
  in test-cases.json. Excluded pending an upstream PR.
Bump helios-serde's quick-xml 0.38 -> 0.41 to fix the two DoS-via-XML
advisories on the client-facing FHIR XML parse path. The remaining
transitive copies (0.37.5 via object_store, 0.38.4 via octofhir-ucum's
build dependency) are not reachable with attacker-controlled input and
are ignored in the CI audit with a documented justification until their
upstreams ship on quick-xml >= 0.41.

Also regenerates Cargo.lock, which picks up the previously-uncommitted
helios-web/askama entries added by the feat(web) scaffold.
…ode)

overload/validate-good2a has two codings sharing (overload, code2) but with
different displays ("Display #2" then "Display 2"). The coding_displays /
coding_versions / coding_index maps were keyed by (system, code) and built
with last-wins .collect(), so the first-wins loop still echoed the second
coding's display and resolved the wrong CS version (1.0.0 instead of 2.0.0).
Build these maps keep-first to match first-wins selection.
build_concept_closure_pg loaded concepts and inserted closure rows without
holding any lock on the parent code_systems row. In the shared-database
postgres integration tests, a concurrent delete_normalized("CodeSystem", ..)
(e.g. importer_dicom_runs_against_postgres deleting the DICOM system at
teardown) could remove the code_systems row — cascading its concepts — in the
window between the concept load and the closure INSERT. The INSERT then
violated concept_closure_system_id_fkey ("Key (system_id)=(dicom|current) is
not present in table code_systems"), intermittently failing the whole test
binary (seen only under the Code Coverage job's timing, green under Test Rust).

Do the whole build in one transaction that first pins the code_systems row
with SELECT ... FOR SHARE (skipping the system if the row is already gone),
and read concepts/hierarchy within that transaction. A concurrent delete now
blocks until the build commits, closing the FK window. This also hardens
server startup against a code system being deleted mid closure-migrate.

SQLite is unaffected: it serializes writers and its tests use per-test DBs.
@codecov

codecov Bot commented Jul 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 62.85714% with 65 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/web/src/lib.rs 48.78% 42 Missing ⚠️
crates/hts/src/operations/validate_code.rs 42.10% 11 Missing ⚠️
crates/hts/src/backends/postgres/schema.rs 57.89% 8 Missing ⚠️
crates/hts/src/operations/expand.rs 92.72% 4 Missing ⚠️

📢 Thoughts on this report? Let us know!

smunini added 3 commits July 3, 2026 08:59
…se:flat

HL7/fhir-tx-ecosystem-ig#51 merged (upstream 49c830c3), adding the
`response:flat` key for search-filter-yes. HTS's spec-valid flat $expand
now matches, so the temporary IGNORED_TESTS entry is no longer needed.
Removing the skip and re-running (run 28662146109) revealed search-filter-yes
is a real HTS hierarchy-nesting gap, NOT the upstream wiring gap first
diagnosed. Upstream 7409a11a flipped the test from flat to hierarchical; HTS
keeps filtered/URL-resolved expansions flat. The validator treats HTS as
hierarchy-capable and always compares against the hierarchical response, so
the merged upstream response:flat wiring (#51) does not help. Re-skipping
with an accurate rationale until the default-nesting heuristic is broadened.
An `is-a` / `descendent-of` compose filter designates a hierarchy subtree, so
its $expand should nest matched concepts under their in-result ancestors by
default (tx.fhir.org behaviour). HTS only defaulted to tree mode for pure
full-system *inline* composes, leaving filtered and URL-resolved composes flat
— so search/search-filter-yes (URL-resolved is-a filter) expanded flat while
the IG (upstream 7409a11a) expects it nested.

Extend the default-nesting decision: when the compose (inline or URL-resolved)
carries an is-a/descendent-of filter, default hierarchical=true unless the
caller set hierarchical/excludeNested. No hierarchy-detection gate — an is-a
filter over a flat CS resolves to just its own code, so nesting is a no-op
there; gating on code_system_is_hierarchical would wrongly drop nesting when
that probe under-reports (as it does for the `search` CS). Full-system-with-
text-filter (search-all-yes) and enumerated composes (search-enum) stay flat.

Verified no run-test regressions: the only other is-a expand tests run against
HTS are broken-filter-expand (empty expansion) and parameters-expand-isa-*
(already excludeNested=false); the snomed/loinc/pc is-a tests are tx.fhir.org/
snomed-mode and not run. ecl_expand's expand_codes helper now collects codes
recursively (it asserts concept-set membership, not tree shape). Removes the
temporary search-filter-yes skip.
@smunini smunini merged commit 9722e6c into main Jul 3, 2026
28 of 29 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant