Task Note (one conversation turn) Profile¶
| Canonical | ../StructureDefinition/nexus-emr-task-note |
|---|---|
| Status | draft · 1.26.0 |
| Base | Annotation (constraint) |
| Source | FSH · JSON |
One turn in a task's conversation: who said it, when, and what they said. Both people and machine actors (harness graphs, decision-support rules) author turns. Because R4 does not permit Device as an Annotation.author target and a profile cannot widen it, a machine-authored turn is an identifier-only logical reference under the nexus-harness-graph or nexus-cds-rule NamingSystem, carrying a display and no literal reference; the identifier's system is what says the author is a graph. Human authors use the element normally.
FSH
The authored source of truth: resources/StructureDefinition-nexus-emr-task-note.fsh (edit on GitHub). One FSH file may define several related artifacts.
// StructureDefinition-nexus-emr-task-note.fsh
// One turn in a task's conversation.
//
// WHY A DATATYPE PROFILE, AND WHY THIS ONE FIRST. This is the IG's first profile on a datatype
// rather than a resource, and the reason is that `Task.note` carries something load-bearing enough
// to deserve its own name: the conversation that runs the whole length of a task's life. A task IS
// a conversation with ownership attached -- the ask, the answers, the reason it was forwarded, the
// reason it was held. Leaving that as a bare `Annotation` left the rules in prose on the Task
// profile, where a reader had to reconstruct them and a validator could not check them.
//
// THE DEVICE PROBLEM, which is the substantive reason this profile exists.
// `Annotation.author[x]` in R4 is `Reference(Practitioner | Patient | RelatedPerson | Organization)`
// or `string`. **Device is not an allowed target.** That is a problem here and almost nowhere else,
// because in this EMR an AI-harness graph is a first-class participant in task conversations: it
// asks, answers, and is handed work exactly as a person is, and it is named everywhere else in the
// model as a logical `Device` reference under NamingSystem/nexus-harness-graph (a harness graph) or
// NamingSystem/nexus-cds-rule (a decision-support rule).
//
// A profile CANNOT widen a type list -- profiling constrains, it never widens (R4 profiling rules;
// SUSHI refuses the attempt outright with "does not match any of the allowed types"). So the shape
// is forced, and it is worth stating exactly what was chosen and what was rejected:
//
// CHOSEN: an identifier-only logical reference. `author.identifier` under the harness-graph
// NamingSystem names the graph, `author.display` carries a readable name, and `author.reference`
// is ABSENT (there is no Device resource to point at -- an agent is a running graph, not stored
// equipment). `Reference.type` is deliberately OMITTED rather than set to `Device`, because
// setting it would assert a target this element does not permit. The identifier's SYSTEM carries
// the identity instead: anything under the harness-graph system is a graph, by construction.
//
// REJECTED: an extension carrying the Device reference alongside an `authorString`. It validates,
// but it hides the author from every consumer that has not read this IG -- the opposite of what a
// partner-facing spec should do. An identifier-only reference with a display degrades gracefully:
// a generic reader still sees a named author; a Nexus-aware reader resolves it to a graph.
//
// NOT AVAILABLE: waiting for a future FHIR release. R5 does not add Device either (it adds
// PractitionerRole and nothing else), so this is a durable constraint, not a bridge.
//
// Validator note, recorded because it is a trap: a logical reference has nothing to resolve, so the
// HL7 validator does NOT flag `type: "Device"` here -- it passes silently. The constraint is real
// even where the tooling is quiet, which is exactly why the invariant below exists.
Profile: NexusEmrTaskNote
Parent: Annotation
Id: nexus-emr-task-note
Title: "Task Note (one conversation turn)"
Description: "One turn in a task's conversation: who said it, when, and what they said. Both people and machine actors (harness graphs, decision-support rules) author turns. Because R4 does not permit `Device` as an `Annotation.author` target and a profile cannot widen it, a machine-authored turn is an identifier-only logical reference under the nexus-harness-graph or nexus-cds-rule NamingSystem, carrying a display and no literal reference; the identifier's system is what says the author is a graph. Human authors use the element normally."
* ^status = #draft
* ^date = "2026-08-19"
* ^publisher = "WELLSTAR Technologies Corp."
* ^purpose = "Give the conversation turn on a Task a named, checkable shape -- including machine authorship, which base R4 cannot express directly."
// Standards status: trial-use, matching the Task profile that uses it.
* ^extension[+].url = "http://hl7.org/fhir/StructureDefinition/structuredefinition-standards-status"
* ^extension[=].valueCode = #trial-use
* obeys nexus-note-agent-author
// Attribution is the point of a turn. An unattributed turn in a clinical conversation is worse than
// no turn: it is a statement in the record with nobody behind it. Base R4 leaves both optional
// (`author[x] 0..1`, `time 0..1`); here both are required, because a task handed to someone else is
// only intelligible if every turn says who said it and when.
* author[x] 1..1 MS
* author[x] ^short = "Who said it: a person, or a machine actor (identifier-only reference)"
* author[x] ^definition = """
The author of this turn.
A **person** is referenced normally (`authorReference` to a Practitioner, Patient, RelatedPerson or
Organization), or named as free text with `authorString` where no structured reference exists.
A **machine actor** is an identifier-only logical reference: `authorReference.identifier` under its
producing registry's namespace -- `../NamingSystem/nexus-harness-graph` naming
a harness graph, or `../NamingSystem/nexus-cds-rule` naming a decision-support
rule -- with
`authorReference.display` carrying a readable name and NO `authorReference.reference`. `Device` is
not an allowed target of this element in R4 and a profile cannot add one, so the identifier system
carries the identity. `Reference.type` is omitted deliberately: setting it to `Device` would assert
a target the element does not permit.
"""
* author[x] ^comment = "Required here though optional in base R4: an unattributed turn is a statement in the record with nobody behind it. **PractitionerRole is NOT a permitted target in R4 even though `Task.owner` permits it** -- a task can be owned by a role and still have every turn authored by a person, and a writer that reaches for the same reference in both places will produce an invalid note. (R5 adds PractitionerRole; R4 does not, and a profile cannot widen the list.) For agent authors see `nexus-note-agent-author`, which enforces the identifier-only shape. Granularity for a machine author is the GRAPH or the RULE -- the unit a clinician or auditor recognises -- never the model or the prompt; per-model trace stays with the producer."
* time 1..1 MS
* time ^short = "When the turn was made -- the thread's ordering key"
* time ^definition = "When this turn was made. Required here though optional in base R4, because the turns are read as an ordered thread and a turn with no time cannot be placed in it."
* time ^comment = "Turns are read oldest-first by `time`. This element is the ordering key, which is why it is required: one undated turn makes the whole conversation's order a guess."
* text MS
* text ^short = "What was said (markdown)"
* text ^comment = "Markdown, per base R4. A turn carries prose, not structured payload: a turn that needs to cite chart resources cites them from the Task (`focus`, `input`, `supportingInformation`), not from inside the annotation. See the Task profile's note element for the graduation valve if that ever becomes insufficient."
// Enforcement for the Device problem described at the top of this file.
//
// NOTE THE ANTECEDENT SHAPE, which is not cosmetic. It is written as
// `identifier.where(system = ...).exists()` rather than the shorter `identifier.system = '...'`,
// because in FHIRPath a comparison against an EMPTY left operand returns empty, and
// `empty implies false` is empty -- which a validator treats as a failed constraint, not a passed
// one. The short form therefore FAILED every human-authored turn and every `authorString` turn:
// caught by mutation-testing the invariant against cases it was supposed to ignore, not by review.
// `.exists()` always returns a true/false boolean, so the implication is silent on turns this
// constraint has no opinion about.
Invariant: nexus-note-agent-author
Description: "A turn authored by a machine actor SHALL be an identifier-only logical reference: `author.identifier` under the nexus-harness-graph or nexus-cds-rule NamingSystem, carrying a display and NO literal `reference`. Device is not an allowed target of Annotation.author in R4, so the identifier system carries the identity; `Reference.type` is omitted deliberately rather than asserting a target the element does not permit."
Severity: #error
Expression: "author.ofType(Reference).identifier.where(system = '../NamingSystem/nexus-harness-graph' or system = '../NamingSystem/nexus-cds-rule').exists() implies (author.ofType(Reference).reference.empty() and author.ofType(Reference).type.empty() and author.ofType(Reference).display.exists() and author.ofType(Reference).identifier.value.exists())"