Skip to content

feat(validator): FHIR NPM package pipeline + validation completeness hardening - #483

Open
sandhums wants to merge 10 commits into
HeliosSoftware:mainfrom
sandhums:feat/validator-fhir-packages
Open

feat(validator): FHIR NPM package pipeline + validation completeness hardening#483
sandhums wants to merge 10 commits into
HeliosSoftware:mainfrom
sandhums:feat/validator-fhir-packages

Conversation

@sandhums

@sandhums sandhums commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Two layers of work on helios-fhir-validator (plus its REST wiring):

  1. FHIR NPM/IG package materialization — a curated on-disk package cache, offline package.json dependency resolution, and StructureDefinition → FHIR Schema layer conversion, wired into ValidationService as CompositeResolver overlays.
  2. A completeness/hardening pass over the validation engine itself, driven by a four-axis audit (Helios style, FHIR-version correctness, idiomatic Rust, and coverage of the FHIR validation dimensions).

No second validation engine: packages compile into the same cooperative schema-set walk introduced in #232.

1. Package pipeline

  • Cache + resolution (src/packages/): curated cache layout, ensure_from_path accepting both .tgz archives and IG-publisher output directories, offline dependency resolution from package.json, and materialization into SchemaRegistry layers (profiles only; abstract definitions skipped).
  • Configurable sources: HFS_FHIR_PACKAGE_CACHE / HFS_FHIR_PACKAGES (path, publisher output dir, or URL). Fail-loud if a configured package cannot load — no silent empty overlay.
  • FHIR-version correctness:
    • resolve_packages rejects packages whose manifest fhirVersions is incompatible with the target version (packages/version.rs handles full versions 4.0.1, MIME-style 4.0, and R4-style labels).
    • materialize_package_layers_by_version partitions layers per FhirVersion; ValidationService keys its package layers by version so an R5 request never sees R4 overlays.
  • Sample package fixture: a bundled, anonymized sample.tgz (with pack.sh and source JSON) exercises cache → resolve → materialize → validate end to end without network access.

2. Engine completeness

Slicing

  • All five discriminator types compiled and evaluated: value/pattern (partial-match patterns, multi-discriminator), type, profile, binding, and exists (expected presence read from the slice differential: min >= 1 ⇒ present, max = 0 ⇒ absent).
  • Restricted-FHIRPath discriminator paths parsed properly (paren-aware — extension urls contain dots): dotted selections, $this, extension('url') (compiled to a dedicated containment matcher, incl. nested complex-extension chains), and ofType(Type) / choice-aware navigation (value resolves to valueQuantity). resolve() remains unsupported and degrades gracefully (slice kept, matcher and min dropped, conversion warning).
  • Reslicing: parent/child slice names scope the child to the parent's match; sliceIsConstraining slices inherit matchers instead of matching nothing.
  • Rules coverage (open/closed/openAtEnd/ordered/@default, prohibited max: 0 slices) preserved.

Value-domain keywords

  • maxLength, minValue[x], maxValue[x] carried through the converter and enforced in the walk (ordered comparison for numbers and date/time strings), with new error kinds max-length / min-value / max-value.

References and bindings

  • refers enforcement (type.targetProfile): flag-gated via ValidationOptions::enforce_refers (off by default for conformance-suite parity); rejects references whose target type is not among the allowed profiles' types (reference-target).
  • extensible bindings: opt-in via EffectHandlers::check_extensible_bindings, emitting warning-severity issues (required bindings remain errors).

QuestionnaireResponse validation

  • New questionnaire module validating a QR against its Questionnaire: required items, answer type vs item type, answerOption / answerValueSet membership (via the terminology provider), enableWhen gating, and unknown linkIds.
  • REST wiring via a QuestionnaireLookup trait on ValidationService.
  • #Pending - REST-side QuestionnaireLookup path, needs ValidationService plumbing in the rest crate's test suite

3. REST / service wiring (crates/rest/src/validation.rs)

  • Package layers keyed per FhirVersion, loaded through the version-partitioned materializer.
  • TerminologyMode (Off / Embedded / Remote) replaces the bare optional provider.
  • New error kinds mapped to OperationOutcome issue types.

4. Style / idiomatic Rust

  • PackageError migrated to thiserror; crate README.md + manifest readme key; lib.rs docs restructured with an honest "current limitations" section; clippy-clean across the crate.

Intentionally out of scope

  • Live npm registry client / network fetch on $validate (cache is preseeded or fed via ensure_from_path).
  • Terminology (CodeSystem/ValueSet) packaging — remains HTS.
  • Full ValueSet expansion for binding discriminators naming a canonical URL.
  • resolve() discriminator paths and resolve-ref matchers (need an instance graph).
  • mustHaveValue/valueAlternatives (R5) and computable mustSupport checking.

Tests

  • Upstream FHIR Schema conformance suite unchanged and passing (exact ordered error matching).

  • New extended fixtures: value_keywords, reslicing, slice_matchers (type/profile/binding/extension-sugar), exists_extension_matchers (exists + extension-path matchers, nested chains).

  • New suites: refers_enforcement.rs, extensible_bindings.rs, packages_tests.rs additions (fhirVersions rejection, sample.tgz end-to-end), non-ignored R4 pack smoke test.

  • Converter goldens for every new mapping (slice-discriminators, binding-slice, exists-extension-discriminators).

  • Questionnaire unit tests cover required items, answer-type mismatch, unknown linkIds, enableWhen gating (incl. enableBehavior: any and a warning for answered-but-disabled items), answerOption membership, and answerValueSet membership via a stub terminology provider (skipped, not failed, without a provider).

  • cargo test -p helios-fhir-validator — all suites green

  • cargo clippy -p helios-fhir-validator --all-targets — no warnings

  • cargo check -p helios-rest

Made with Cursor

sandhums and others added 2 commits August 1, 2026 12:05
Add package materialization proper on top of the HeliosSoftware#232 SchemaRegistry
overlay shape: curated cache, offline dependency resolution, and
StructureDefinition→schema layers wired into ValidationService via
HFS_FHIR_PACKAGE_CACHE / HFS_FHIR_PACKAGES (fail-loud on load errors).

Also evaluate type/profile/binding slice matchers in the FHIR Schema
engine so IG packages can enforce non-pattern discriminators.

Co-authored-by: Cursor <cursoragent@cursor.com>
…, URL)

Clarify that cache .staging is only a temp unpack area. Add
ensure_from_path for local .tgz, expanded dirs, and IG publisher output/
(preferring package.tgz). Wire HFS_FHIR_PACKAGE_SOURCES so HFS can seed
the cache from disk or HTTP(S) at boot without a manual install step.

Co-authored-by: Cursor <cursoragent@cursor.com>
@sandhums
sandhums marked this pull request as draft August 1, 2026 11:42
sandhums and others added 6 commits August 1, 2026 18:07
…README

Replace the hand-rolled Display/Error impls with thiserror derives, and give
the crate a README (wired via the manifest readme key) so the overview and
quick start no longer live only in lib.rs.

Co-authored-by: Cursor <cursoragent@cursor.com>
…per FHIR version

Resolution now rejects packages whose manifest fhirVersions is incompatible
with the target version (full versions, MIME-style 4.0, and R4-style labels
all understood), and materialize_package_layers_by_version partitions layers
per FhirVersion so overlays from one version never leak into another.

Also bundles an anonymized sample.tgz fixture (with pack.sh and sources)
exercising cache -> resolve -> materialize -> validate end to end offline,
and adds a non-ignored R4 pack smoke test alongside the full feature sweep.

Co-authored-by: Cursor <cursoragent@cursor.com>
…l discriminator coverage

Validation-completeness pass over the engine and converter:

- maxLength / minValue[x] / maxValue[x] carried through the converter and
  enforced in the walk with dedicated error kinds
- flag-gated refers (type.targetProfile) enforcement via
  ValidationOptions::enforce_refers; off by default for suite parity
- opt-in warning enforcement for extensible-strength bindings
  (EffectHandlers::check_extensible_bindings)
- reslicing (parent/child) scoped to the parent match, and
  sliceIsConstraining matcher inheritance
- discriminator paths parsed as the restricted-FHIRPath grammar:
  extension('url') compiles to a containment matcher (nested chains
  supported), exists discriminators compile to presence/absence matchers
  read from the slice differential, and ofType()/choice elements resolve
  to concrete JSON keys; resolve() still degrades gracefully

Pinned by converter goldens, extended engine fixtures (value_keywords,
reslicing, slice_matchers, exists_extension_matchers), and dedicated
refers/extensible-bindings suites.

Co-authored-by: Cursor <cursoragent@cursor.com>
…dule

Validate a QuestionnaireResponse against its Questionnaire definition:
required items, answer type vs item type, answerOption / answerValueSet
membership (via the terminology provider), enableWhen gating, and unknown
linkIds. Exposed from the crate root alongside restructured lib docs
(overview and quick start first, limitations kept current).

Co-authored-by: Cursor <cursoragent@cursor.com>
…naire lookup

ValidationService now keys package overlays by FhirVersion (loaded through
the version-partitioned materializer), replaces the bare optional
terminology provider with TerminologyMode (Off/Embedded/Remote), gains a
QuestionnaireLookup hook for QuestionnaireResponse checks, and maps the new
validator error kinds to OperationOutcome issue types.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@sandhums sandhums changed the title feat(validator): FHIR NPM package cache, deps, and package→schema layers feat(validator): FHIR NPM package pipeline + validation completeness hardening Aug 1, 2026
sandhums and others added 2 commits August 1, 2026 18:12
…aths

Add unit tests for enableWhen gating of required items (including the
warning on answered-but-disabled items and enableBehavior: any),
answerOption membership, and answerValueSet membership via a stub
terminology provider (skipped, not failed, when no provider is set).

Co-authored-by: Cursor <cursoragent@cursor.com>
…-packages

Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	crates/fhir-validator/tests/converter_tests.rs
@sandhums
sandhums marked this pull request as ready for review August 1, 2026 12:53
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