Skip to content

Provenance Profile

Canonical../StructureDefinition/nexus-emr-core-provenance
Statusdraft · 1.26.0
BaseProvenance (constraint)
SourceFSH · JSON

Attribution for AI-mediated and cross-boundary writes: what was produced, when, by which agent (human or non-human), from what inputs, and -- for generated text -- which verbatim source passage each statement rests on. Used standalone against a produced resource, and contained in an AI-suggestion review Task against the suggestion contained alongside it.

FSH

The authored source of truth: resources/StructureDefinition-nexus-emr-core-provenance.fsh (edit on GitHub). One FSH file may define several related artifacts.

// NexusEmrCoreProvenance.fsh
// Attribution for AI-mediated and cross-boundary writes: what an agent produced, when, from
// what inputs, under whose supervision, and -- for generated text -- which verbatim source
// passage each statement rests on.
//
// WHAT IT IS FOR. Every AI action links back to the FHIR resources it read and the resources it
// wrote, and that trail has to survive the write-back: when an AI-drafted note or order is saved
// into the record, "which agent produced this, from what inputs, under whose supervision" must
// be answerable afterwards. Two shapes are in use, and both are worked examples:
//   * STANDALONE, targeting the resource an agent produced -- an accepted AI-drafted note, or a
//     generated summary carrying its per-fact citations.
//   * CONTAINED in an AI-suggestion review Task and linked from `relevantHistory`, targeting the
//     suggested resource contained alongside it. A suggestion and its citation trail are then promoted,
//     declined and retained as one unit, and neither is independently visible pre-approval.
//
// Provenance is infrastructure, not clinical content: it describes how a record came to be. The
// FHIR Gateway separately stamps an `UpdatedBy` extension on `meta` for a narrower audit purpose,
// so Provenance is not the whole audit record -- see coverage, below.
//
// NOT SETTLED. Stable enough to build on, with three questions deliberately left open:
//   1. MODEL IDENTITY AND VERSION. No element here carries them, and none should be assumed to
//      appear in a particular place. The non-human agent is named at the granularity of the
//      graph or feature that ran (see `agent.who`); which model executed inside it, and at what
//      version, stays in the producing system and is correlated by identifier. What would
//      decide otherwise is a consumer that must answer "which model version produced this
//      statement" from the record alone -- withdrawing or re-reviewing every output of a
//      version found to be defective, without access to the producing system's own logs.
//      External AI-transparency and clinical-decision-support standards are moving toward
//      requiring exactly that, so expect this to be revisited rather than to stay open
//      indefinitely. Reopening it is a change to `agent.who` and the Device it names, not to
//      this element list.
//
//      OPERATIONAL TELEMETRY IS SEPARATE AND STAYS OUT regardless: token spend, latency and
//      cost are not clinical facts and do not belong in the record, whatever is decided about
//      identity.
//   2. THE `UpdatedBy` RELATIONSHIP. Whether the gateway's `meta.extension` audit stamp is
//      re-expressed as Provenance or kept as a cheaper parallel mechanism. The two differ in
//      host, location and datatype today. Whether consumers need a single queryable audit spine
//      is what decides it.
//   3. COVERAGE. Provenance is written for AI-mediated and cross-boundary writes; whether it is
//      also written for ordinary human edits is open, and writing it for everything carries real
//      storage and query cost. Treat its presence as informative and its absence as
//      inconclusive.

Alias: $NexusEmrCoreProvenance = ../StructureDefinition/nexus-emr-core-provenance

// R4 Provenance has NO `identifier` element. Correlating a Provenance to an external ledger
// therefore has to go through
// `agent.who` or `entity.what` identifiers, or a future extension -- see the token/model
// accounting note above.

// The point of a Provenance is to say what it is about. An empty target makes the resource
// unattributable and unfindable, and R4's 1..* already requires one; this states the intent
// that the target be a resource a reader can actually resolve or match.
Invariant:     provenance-target-required
Description:   "Provenance.target must name at least one resource -- a provenance with no target is unattributable"
Severity:      #error
Expression:    "target.exists()"

// An AI-mediated action must name the non-human agent that performed it, or the trail cannot
// distinguish generated content from human-authored content -- the specific outcome this profile
// exists to prevent. The rule needs a marker for "AI-mediated" that is readable from the instance
// itself, and the attributed-fact extension is that marker: it is only ever present on generated
// text. Warning severity, because the profile also covers ordinary human and system provenance
// where the distinction is moot, and because a Device may be referenced in ways this expression
// cannot see (a contained Device, or a bare identifier reference with no `type`) -- set
// `agent.who.type` to `Device` and the rule reads correctly.
//
// Written as `extension.where(url = ...)` rather than the `extension(...)` shorthand: the two are
// equivalent in FHIRPath, but only the longhand is inside TypedFhir's emittable subset, and an
// invariant outside that subset compiles to `invariant-unverifiable` -- it would render on the
// published page and then check nothing at runtime. Same reason `nexus-emr-core-patient.fsh:26`
// is written this way.
Invariant:     provenance-ai-action-names-agent
Description:   "A Provenance that carries attributed facts SHOULD name the non-human agent that generated them (agent.who typed or referenced as Device), in addition to any supervising human -- otherwise the trail cannot distinguish generated content from human-authored content"
Severity:      #warning
Expression:    "extension.where(url = '../StructureDefinition/nexus-attributed-fact').exists() implies agent.where(who.type = 'Device' or who.reference.startsWith('Device/')).exists()"

// Profile Definition
Profile:        NexusEmrCoreProvenance
Parent:         Provenance
Id:             nexus-emr-core-provenance
Title:          "Provenance Profile"
Description:    "Attribution for AI-mediated and cross-boundary writes: what was produced, when, by which agent (human or non-human), from what inputs, and -- for generated text -- which verbatim source passage each statement rests on. Used standalone against a produced resource, and contained in an AI-suggestion review Task against the suggestion contained alongside it."
* ^url = $NexusEmrCoreProvenance // Canonical URL from alias
* ^status = #draft
* ^experimental = false
* obeys provenance-target-required
* obeys provenance-ai-action-names-agent

* . ^short      = "Nexus EMR Provenance Core"
* . ^definition = """
  A record of what produced or changed a resource: the activity, when it was recorded, the
  agents responsible (which may include a non-human agent), and the inputs it derived from.

  Primary intended use is AI-action attribution -- keeping an AI-produced resource traceable to
  the agent that produced it, the human who accepted it, and the record context it was generated
  from. Where the product is generated text, the `attributedFact` extension carries the trail at
  statement grain: each statement with the verbatim quote(s) backing it.
  """

// THE effective date for this profile: when the activity happened, falling back to when the
// provenance was recorded. `recorded` is 1..1 in R4 so this always resolves -- but `occurred[x]`
// is preferred because a provenance written late (a batch write-back) should date to the action,
// not to the bookkeeping.
* insert EffectiveDate([[occurredDateTime | occurredPeriod.start | recorded]])

* insert Salient(activity, activity, coded)
* insert Salient(date, [[occurredDateTime | occurredPeriod.start | recorded]], dateTime)

// SMART on FHIR exposure -- read and write, but NOT delete, and the asymmetry is the point. A
// producer records its own attribution alongside the write it describes, so write is exposed; an
// audit trail a client can destroy is not an audit trail, so delete is exposed to nobody. A
// Provenance CONTAINED in another resource is not reached through this grant at all -- it travels
// under the grant of the resource containing it.
* insert SmartExposedNoDelete(provenance)

* meta.source 0..1 MS
* meta.source ^short = "Identifies EMR instance & pipeline version the resource came from"
* meta.source ^definition = "A URI that identifies the EMR pipeline and version from which this resource originated.  This tells you which EMR instance (recommend using the instance identifier), and the version of the pipeline code/transformations."

* text 0..1 MS // Narrative element with human-readable summary
* text ^short = "Narrative"
* text ^definition = "A human-readable narrative summary of the NexusEmrCoreProvenance resource for human interpretation."

// -----------------------------------------
// Attributed facts -- per-statement citations for AI-generated text
// -----------------------------------------
// Finer-grained than `entity`: entity says which resources the activity read; this says
// which statement in the generated text rests on which verbatim passage. Defined in
// AttributedFactExtension.fsh, where the wire-schema mapping is documented.
* extension contains NexusAttributedFact named attributedFact 0..* MS
* extension[attributedFact] ^short = "Per-fact citations for AI-generated text (statement + verbatim quotes)"
* extension[attributedFact] ^comment = "Present when the target is AI-generated text produced under the cite-every-fact contract (summaries, chiefly). Repeats in fact order; order is significant. A generated summary need not be persisted at all -- but when it or its provenance is, this is where the citation trail lives, so the trail survives the write-back at full fidelity."

// -----------------------------------------
// Target -- what this provenance is about
// -----------------------------------------
* target 1..* MS
* target ^short = "The resource(s) this provenance describes"
* target ^definition = """
The resources produced or changed by the activity. For an AI write-back this is the resource
the AI produced -- the drafted DocumentReference, the suggested MedicationRequest, the
Task.

Multiple targets are permitted and are the normal case for a single AI action that writes a
bundle: one Provenance covering the set is preferable to one per resource, because the action
was one decision.
"""
* target ^comment = "Per the Nexus EMR logical-reference model these MAY be identifier-only. A consumer resolving provenance MUST handle a reference with no literal `reference` -- this is the single most common cause of provenance appearing 'missing' when it is present."

// -----------------------------------------
// Occurred / recorded -- when
// -----------------------------------------
* occurred[x] 0..1 MS
* occurred[x] ^short = "When the activity happened"
* occurred[x] ^comment = "Preferred over `recorded` as THE date. A provenance written asynchronously (a queued write-back) has a `recorded` well after the action; dating the action to the bookkeeping timestamp misplaces it in every timeline."
* recorded 1..1 MS
* recorded ^short = "When this provenance record was created"

* activity 0..1 MS
* activity ^short = "What kind of activity produced the target"
* activity ^comment = "Deliberately unbound. R4's example binding (`provenance-activity-type`) does not anticipate AI-mediated authorship -- draft-generated, AI-suggested, human-accepted -- and asserting it would mis-describe the data. Treat the activity vocabulary as open and read `activity.text`, which is what the examples carry. A validator run against the base spec will warn that no code was supplied; that warning is expected here."

// -----------------------------------------
// Agent -- WHO OR WHAT acted (the harness's central need)
// -----------------------------------------
* agent 1..* MS
* agent ^short = "Who took part in the action"
* agent ^comment = "Includes the AI agent and its human supervisor where content was AI-drafted."
* agent ^definition = """
Who or what was responsible for the activity.

For an AI-mediated action there are normally TWO agents and both matter:
  • the **non-human agent** that generated the content -- a Device, identifying the harness
    and, where a consumer needs it, the model;
  • the **human** who reviewed, accepted or attested to it -- a Practitioner.

Recording only the human makes AI-drafted content indistinguishable from human-authored
content, which is the specific outcome this profile exists to prevent. Recording only the AI
loses accountability.
"""
// REQUIRED (1..1), aligning with US Core 9.0.0 `us-core-provenance`, which also sets
// `agent.type` min 1. This is a deliberate tightening rather than the 0..1 the base allows: the
// element is the ONLY thing that distinguishes the generating AI from the accepting human, which
// is the entire reason this profile exists. Two untyped agents are an ambiguous record, and an
// ambiguous record here means AI-drafted content is indistinguishable from human-authored content.
//
// NOT adopted from US Core: its must-support on `target.reference`. That would require literal
// references and this IG's logical-reference model deliberately permits identifier-only.
* agent.type 1..1 MS
* agent.type ^short = "The agent's role (author, verifier, attester, ...)"
* agent.type ^definition = """
The role this agent played in the activity -- for example `author` for the agent that generated
the content, and `attester` or `verifier` for the human who reviewed and accepted it.

Required (1..1) in this profile, matching US Core. This is the element that distinguishes a
non-human generating agent from the human who took responsibility for its output; without it, a
Provenance naming two agents does not say which did what.
"""
* agent.type ^comment = "Bound extensible to `provenance-agent-type` by the base. `author` + `attester` is the expected pair for an AI-mediated write -- see the AI-drafted-note example."
* agent.who 1..1 MS
* agent.who ^short = "The agent itself -- Practitioner, Device (AI/system), Organization, Patient"
* agent.who only Reference(NexusEmrCorePractitioner or NexusEmrCorePractitionerRole or NexusEmrCorePatient or NexusEmrCoreRelatedPerson or NexusEmrCoreOrganization or NexusEmrCoreAppDevice or NexusEmrCoreEmrDevice)
* agent.who ^comment = "Practitioner is the primary provider identity in this IG. A non-human agent is a Device, referenced logically (by identifier) where the harness graph has no stored Device instance; set `Reference.type` to `Device` so a reader can tell what kind of agent it is without resolving. Whether model identity rides on that Device instance is open -- see the token/model accounting note at the top of this file."
* agent.onBehalfOf 0..1 MS
* agent.onBehalfOf ^short = "The party the agent acted for"
* agent.onBehalfOf only Reference(NexusEmrCorePractitioner or NexusEmrCorePractitionerRole or NexusEmrCoreOrganization)
* agent.onBehalfOf ^comment = "Covers the delegation case the harness will need: an AI agent acting on behalf of the supervising practitioner, or of the clinic."

// -----------------------------------------
// Entity -- what the activity DERIVED FROM (the AI's inputs)
// -----------------------------------------
* entity 0..* MS
* entity ^short = "The inputs the activity derived from -- for AI, the record context it was given"
* entity ^definition = """
The resources the activity used as input. For an AI action this is the record context the model
was given: the resources drawn from the patient compartment that informed the output.

This is what makes an AI output auditable rather than merely attributable -- "this draft was
generated from these twelve resources" is a reviewable claim, where "this draft came from the
harness" is not.
"""
* entity ^comment = "Records WHICH RESOURCES the activity read, and stays at that grain deliberately: statement-level attribution -- which generated sentence rests on which passage -- rides the `attributedFact` extension instead, and a citation there MAY point back to the entity it was drawn from. For a chart-wide compartment load, listing every member may still be prohibitively large, and a summarizing convention (the compartment snapshot's own identifier rather than each member) is the expected direction for that case."
* entity.role 1..1 MS
* entity.role ^short = "derivation | revision | quotation | source | removal"
* entity.what 1..1 MS
* entity.what ^short = "The input resource"
* entity.what ^comment = "May be identifier-only per the logical-reference model."