Criteria Requirements Library Profile¶
| Canonical | ../StructureDefinition/nexus-criteria-requirements |
|---|---|
| Status | draft · 1.26.0 |
| Base | Library (constraint) |
| Source | FSH · JSON |
The data-requirements manifest generated for a compiled clinical criterion: a FHIR R4 Library with type = module-definition, declaring the resource types the logic retrieves, the profiles they must satisfy, the code and value-set filters applied, and the elements read. Derived from the compiled logic rather than authored beside it, so it cannot drift from the criteria it describes. A deployment artifact verified when a consuming host starts, not an API surface an application reaches.
FSH
The authored source of truth: resources/StructureDefinition-nexus-criteria-requirements.fsh (edit on GitHub). One FSH file may define several related artifacts.
// StructureDefinition-nexus-criteria-requirements.fsh
// The data-requirements manifest a compiled clinical criterion travels with: a FHIR R4 `Library`
// with `type = module-definition`, saying what data the logic reads.
//
// WHAT PRODUCES IT AND WHAT READS IT. A criteria pack is generated once, at authoring time: the
// CQL is translated to ELM, the ELM is analysed, and one of these Libraries is written per
// criterion alongside it. Nothing hand-writes one, and a criteria library should not carry a
// hand-authored `dataRequirement` of its own -- the derived manifest is the only copy that cannot
// drift from the logic. On the reading side it is ordinary JSON: a query generator turns
// `dataRequirement` into a WHERE clause, a projection is sized from the elements read, and a
// cohort scheduler decides from the relative-date flag whether a change feed suffices or a
// periodic sweep is also required.
//
// WHY IT IS WORTH PROFILING, when the two Library shapes in this guide were previously left
// unconstrained. This one has a consumer that refuses to run without it. A consuming host
// verifies every manifest in a pack AT STARTUP and will not start on one whose
// `dataRequirement` is empty, or one carrying a `codeFilter` with no `path`. A criterion that
// reads no data fires for everyone or for no one, and a code filter with no path filters on
// nothing determinable; both are refused rather than guessed at. Those two rules were enforced in
// one service's startup path and stated nowhere a reader of this guide could find them. They are
// now cardinality on the profile, which is where a rule of that kind belongs.
//
// THIS DESCRIBES THE EXISTING SHAPE AND RESHAPES NOTHING. Every constraint below was checked
// against the manifests a pack ships today, and each one already holds on all of them. The
// generator cannot produce a Library that violates this profile: it refuses a criterion whose
// compiled ELM carries no library identifier or version, and it throws rather than emit a code
// filter it could not resolve a path for. So this is a statement of what is already true, not a
// new requirement anybody has to migrate to.
//
// WHAT IS DELIBERATELY NOT REQUIRED, and the absence is the statement. A shipped manifest carries
// no `id` and no `meta.profile`. It is a file in a pack read from disk, not a resource served from
// an endpoint, so it has no server identity to carry and no reason to nominate its own profile.
// Requiring either would invalidate every manifest in circulation to buy nothing, so neither is
// required here. A consumer identifies one of these by `type` and by where it came from.
//
// NOT EXPOSED OVER SMART, and this is a decision rather than an omission. A criteria pack is a
// deployment artifact: it ships with the software, it is verified when the host starts, and no
// third-party application reaches it through the FHIR API. Carrying no `smart-exposed` extension
// leaves it unreachable, which is the default for a new profile and the right answer here.
//
// THE OTHER `Library` IN THIS GUIDE IS A DIFFERENT RESOURCE and is not profiled. The CQL model
// definition (`Library/nexus-cql-modelinfo`) is `type = model-definition`, carries its ModelInfo
// as an attachment, and has no `dataRequirement` at all. It is a publication wrapper: the
// compiler that uses the ModelInfo loads it as an XML file, not as FHIR. One profile cannot
// describe both shapes -- they disagree on `status`, on `type`, and on whether the element this
// profile is mostly about exists -- and the second has no consumer that would benefit from one.
Alias: $NexusCriteriaRequirements = ../StructureDefinition/nexus-criteria-requirements
Alias: $LibraryType = http://terminology.hl7.org/CodeSystem/library-type
// Profile Definition
Profile: NexusCriteriaRequirements
Parent: Library
Id: nexus-criteria-requirements
Title: "Criteria Requirements Library Profile"
Description: "The data-requirements manifest generated for a compiled clinical criterion: a FHIR R4 Library with `type = module-definition`, declaring the resource types the logic retrieves, the profiles they must satisfy, the code and value-set filters applied, and the elements read. Derived from the compiled logic rather than authored beside it, so it cannot drift from the criteria it describes. A deployment artifact verified when a consuming host starts, not an API surface an application reaches."
* ^url = $NexusCriteriaRequirements // Canonical URL from alias
* ^status = #draft
* ^experimental = false
* . ^short = "Criteria data requirements (module-definition Library)"
* . ^definition = """
What data one compiled clinical criterion reads: which resource types it retrieves, which
profiles those must satisfy, which code and value-set filters it applies, and which of their
elements the logic touches.
Derived from the compiled logic by static analysis, never authored beside it. A criteria
library that also carries a hand-written `dataRequirement` has two answers to the same
question and no way to tell which one the logic actually obeys; this is the one that cannot
be wrong.
"""
// THE effective date for this profile: NONE, deliberately (see the EffectiveDate /
// NoEffectiveDate RuleSets). A criteria manifest describes no clinical event and belongs to no
// patient. `Library.date` is when the artifact was published and `effectivePeriod` is when its
// content is meant to be used; neither is an effective date in this model's sense, and dating a
// chart surface by either would be meaningless. Declaring `none` rather than nothing stops a
// consumer inventing a date from whichever it finds first.
* insert NoEffectiveDate
// NO `smart-exposed`. See the header: a criteria pack is a deployment artifact, not an API
// surface. A profile carrying no such declaration is unreachable over SMART, which is the
// default and the intended outcome -- so this is the whole of what needs saying about exposure.
// NO identifier block, and no `meta.source` or `text`. A manifest is a generated file inside a
// pack: it is not stored, not versioned by a server, and not rendered to a human. It carries a
// canonical `url`, a `name` and a `version`, and identity beyond those would be identity nothing
// assigns and nothing reads. R4 gives `Library` an `identifier`; a manifest does not use it.
// -----------------------------------------
// Identity -- how a consumer names one manifest among many
// -----------------------------------------
// All three are 1..1 rather than the base's optional, and the generator already guarantees them:
// the canonical is minted per criterion from the pack's own base, and `name` and `version` are
// read back out of the compiled ELM's library identifier -- a criterion whose ELM carries
// neither is refused at generation rather than shipped nameless.
* url 1..1 MS
* url ^short = "Canonical URL of this manifest, minted per criterion"
* url ^comment = "Minted from the pack's canonical base as `.../Library/<criterion>-requirements`, so it is stable for a given criterion and derivable without opening the file. Two criteria in one pack claiming one canonical is a pack whose consumer cannot resolve an include, and is refused at generation."
* name 1..1 MS
* name ^short = "The compiled library's own identifier, machine-readable"
* name ^comment = "Read back out of the compiled logic rather than restated: it is the identifier the criteria library declares for itself, so a manifest and the ELM beside it cannot disagree about which criterion they describe."
* version 1..1 MS
* version ^short = "The compiled library's own version"
* version ^comment = "Also read back out of the compiled logic. Together with `name` this is the identity an `include` in another criteria library resolves against."
// -----------------------------------------
// What this Library IS -- the two elements that make it a module definition
// -----------------------------------------
// `status` is fixed rather than merely constrained. A generated manifest describes logic that has
// already compiled; there is no draft state for it to be in, and no retired one either, because a
// criterion that is withdrawn is removed from the pack rather than left in it flagged.
* status = #active (exactly)
* status MS
* status ^short = "Always `active` -- a manifest exists only for a criterion that compiled"
// Written as a PATTERN rather than a fixed value, deliberately. A pattern pins the coding that
// makes this a module definition while leaving a producer free to add a `display`, which a fixed
// CodeableConcept would reject. What matters to a consumer is that the code is there, not that
// nothing else is.
* type = $LibraryType#module-definition
* type MS
* type ^short = "Always `module-definition` -- this Library declares requirements, it does not carry content"
* type ^comment = "The element that separates this shape from the other Library kind published here. A `model-definition` Library carries a ModelInfo attachment and no `dataRequirement`, and is a different resource with a different purpose; do not read one as the other."
// -----------------------------------------
// Data requirements -- what the logic reads
// -----------------------------------------
// 1..* rather than the base's 0..*: an empty manifest is the failure this element exists to make
// visible. A criterion that declares it reads no data either fires for everyone or fires for no
// one, and nothing in the artifact says which -- so a consumer cannot honour it and should not
// try. A consuming host refuses to start on one.
* dataRequirement 1..* MS
* dataRequirement ^short = "The resource types the logic retrieves, with their profiles and filters"
* dataRequirement ^definition = """
One entry per distinct retrieve the compiled logic performs. `type` is the FHIR resource type,
`profile` names the profile the retrieve was bound against, and `codeFilter` carries the code or
value-set restriction applied to it.
This is what bounds a FETCH: union `type` across a deployment's criteria estate and the result is
the set of resource types an initial load has to cover. What bounds a PROJECTION is finer and is
carried separately, on the `criteriaElementsRead` extension below.
"""
* dataRequirement ^comment = "Never empty. A manifest with no data requirement is not a criterion with no inputs, it is an analysis that produced nothing useful, and the two are indistinguishable from the artifact alone."
// The base makes `path` optional because a `codeFilter` may instead name a `searchParam`. This
// profile does not: the analysis that writes these resolves the element the retrieve binds its
// codes by, and refuses to emit a filter when it cannot. A filter with no path filters on nothing
// a consumer can determine, and a consuming host refuses to start on one -- so requiring the
// element here says in the specification what was previously only true at that host's startup.
* dataRequirement.codeFilter.path 1..1 MS
* dataRequirement.codeFilter.path ^short = "The element the codes apply to -- always present on a generated manifest"
* dataRequirement.codeFilter.path ^comment = "A path relative to the retrieved resource (`code`, `medication`). Where the retrieved profile declares a primary code path, this is that element; where it declares none, a bare code filter is refused at generation rather than quietly matched against the wrong element. `searchParam` is not used as an alternative here."
// -----------------------------------------
// The two derived flags
// -----------------------------------------
// Both extensions declare `Library` as their only context, and until this profile existed that
// context named a resource type this guide did not model.
* extension contains
CriteriaRelativeDate named criteriaRelativeDate 1..1 MS and
CriteriaElementsRead named criteriaElementsRead 0..* MS
// 1..1, and this is the cardinality carrying a promise the extension already makes in prose. The
// value is written even when false, because absence would otherwise mean EITHER "the analysis ran
// and the answer is no" OR "no analysis ran", and those are opposite instructions about whether a
// periodic sweep is needed. Required here, so a manifest that omits it is invalid rather than
// ambiguous, and a consumer never has to decide which of the two an absence meant.
* extension[criteriaRelativeDate] ^short = "Whether membership can change through the passage of time alone -- always present"
* extension[criteriaRelativeDate] ^comment = "Required, including when false. Reading an absent value as false would silently skip a periodic sweep that a time-anchored cohort needed, and the cohort would go stale with every surface still reporting success."
// 0..*, and the lower bound is genuine: a criterion may read no element beyond the code its
// retrieve is bound by. The list is also a LOWER bound in the other sense -- it is derived from
// retrieves and property accesses without inference across included libraries, so an absent path
// is not proof the logic never reads it.
* extension[criteriaElementsRead] ^short = "One element path per repetition, bounding what a projection must materialise"
* extension[criteriaElementsRead] ^comment = "Repeats, one path per occurrence, and never packs several into one delimited string. Paths are flat across every resource type the manifest retrieves and carry no type prefix, so sizing a per-resource projection means intersecting this list with the types in `dataRequirement` rather than reading it directly."