feat(validator): FHIR NPM package pipeline + validation completeness hardening - #483
Open
sandhums wants to merge 10 commits into
Open
feat(validator): FHIR NPM package pipeline + validation completeness hardening#483sandhums wants to merge 10 commits into
sandhums wants to merge 10 commits into
Conversation
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
marked this pull request as draft
August 1, 2026 11:42
…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>
…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
marked this pull request as ready for review
August 1, 2026 12:53
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two layers of work on
helios-fhir-validator(plus its REST wiring):package.jsondependency resolution, and StructureDefinition → FHIR Schema layer conversion, wired intoValidationServiceasCompositeResolveroverlays.No second validation engine: packages compile into the same cooperative schema-set walk introduced in #232.
1. Package pipeline
src/packages/): curated cache layout,ensure_from_pathaccepting both.tgzarchives and IG-publisher output directories, offline dependency resolution frompackage.json, and materialization intoSchemaRegistrylayers (profiles only; abstract definitions skipped).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.resolve_packagesrejects packages whose manifestfhirVersionsis incompatible with the target version (packages/version.rshandles full versions4.0.1, MIME-style4.0, andR4-style labels).materialize_package_layers_by_versionpartitions layers perFhirVersion;ValidationServicekeys its package layers by version so an R5 request never sees R4 overlays.sample.tgz(withpack.shand source JSON) exercises cache → resolve → materialize → validate end to end without network access.2. Engine completeness
Slicing
value/pattern(partial-match patterns, multi-discriminator),type,profile,binding, andexists(expected presence read from the slice differential:min >= 1⇒ present,max = 0⇒ absent).$this,extension('url')(compiled to a dedicated containment matcher, incl. nested complex-extension chains), andofType(Type)/ choice-aware navigation (valueresolves tovalueQuantity).resolve()remains unsupported and degrades gracefully (slice kept, matcher andmindropped, conversion warning).parent/childslice names scope the child to the parent's match;sliceIsConstrainingslices inherit matchers instead of matching nothing.open/closed/openAtEnd/ordered/@default, prohibitedmax: 0slices) 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 kindsmax-length/min-value/max-value.References and bindings
refersenforcement (type.targetProfile): flag-gated viaValidationOptions::enforce_refers(off by default for conformance-suite parity); rejects references whose target type is not among the allowed profiles' types (reference-target).extensiblebindings: opt-in viaEffectHandlers::check_extensible_bindings, emitting warning-severity issues (required bindings remain errors).QuestionnaireResponse validation
questionnairemodule validating a QR against its Questionnaire: required items, answer type vs item type,answerOption/answerValueSetmembership (via the terminology provider),enableWhengating, and unknownlinkIds.QuestionnaireLookuptrait onValidationService.3. REST / service wiring (
crates/rest/src/validation.rs)FhirVersion, loaded through the version-partitioned materializer.TerminologyMode(Off/Embedded/Remote) replaces the bare optional provider.4. Style / idiomatic Rust
PackageErrormigrated tothiserror; crateREADME.md+ manifestreadmekey;lib.rsdocs restructured with an honest "current limitations" section; clippy-clean across the crate.Intentionally out of scope
$validate(cache is preseeded or fed viaensure_from_path).resolve()discriminator paths andresolve-refmatchers (need an instance graph).mustHaveValue/valueAlternatives(R5) and computablemustSupportchecking.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.rsadditions (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,
enableWhengating (incl.enableBehavior: anyand a warning for answered-but-disabled items),answerOptionmembership, andanswerValueSetmembership via a stub terminology provider (skipped, not failed, without a provider).cargo test -p helios-fhir-validator— all suites greencargo clippy -p helios-fhir-validator --all-targets— no warningscargo check -p helios-restMade with Cursor