Clinical Documents¶
An encounter note, a letter and a machine-generated chart summary are the same FHIR resource with different fields set. There is no note resource, no letter resource and no summary resource: there is one Composition profile, and what separates the three is field values rather than different mechanisms. Everything on this page is a consequence of that.
Generated Summaries and Templates go deeper on two of the three streams. This page is the shape they share.
Three documents, one resource¶
Same skeleton for all three. Only type separates them, and a consumer that ignores it renders a machine-generated summary as though a clinician wrote it.
Composition.author will not do that job. It answers who wrote it, and never what it is: a scribe-drafted note and a generated summary both carry a Device author. Composition.type answers it, and it is the only element that always can -- it is 1..1, so every document carries one.
A source system may write a refinement code into the class slot
Converted OSCAR data arrives with LOINC 11488-4 "Consult note" on every casemgmt_note, whatever the note actually is. That is a refinement code sitting in the class slot. A converter that knows better writes 34109-9 "Note" on type and carries 11488-4 on category.
Composition, or DocumentReference?¶
The test is not "is it clinical". Both are. It is: did we author it, and is the content text we control?
A note held as text is readable, greppable and diffable in place. A document held as bytes is not readable at all until somebody dereferences it.
| Composition | DocumentReference | |
|---|---|---|
| What it is | a note we authored | a document that arrived as a file |
| Content | XHTML in section.text |
bytes behind content.attachment.url |
| Examples | progress note, consult note, telephone note, letter, generated summary | scan, fax, external PDF letter, consent form |
| Lifecycle | preliminary / final / amended |
current / superseded |
| Readable without a second fetch? | yes | no |
A scanned consult letter is a DocumentReference even though a human wrote it, because what we hold is a TIFF. A dictated-then-transcribed progress note is a Composition even though a machine produced the text, because we hold the text.
A deliberate divergence from US Core
US Core models clinical notes as DocumentReference. FHIR's document architecture and its C-CDA lineage are the deeper prior art for authored, sectioned, attested narrative, and DocumentReference.docStatus borrows the CompositionStatus value set, so Composition is that lifecycle's native home. An implementer arriving from US Core should expect to find progress notes here.
The smallest possible note¶
Six elements are required on every document.
| Required | |
|---|---|
status |
preliminary / final / amended |
type |
the CLASS of document |
subject |
the patient. Always exactly one |
date |
when the document was last logically changed |
author |
a person, a role, or a Device |
title |
what a chart list shows |
A document with no title is unlistable in a UI, which is why title is required rather than nice to have.
| Optional, set when it applies | |
|---|---|
category |
the refinement under the class |
encounter |
the visit this documents |
event.period |
when the care happened |
attester |
who signed |
identifier |
a business key |
section |
the body, in order |
identifier is 0..1. Do not use its presence to tell document kinds apart
The obligation sits on the element rather than on the cardinality. A document synced from another system should carry that system's identifier, because it has to be reconcilable across the boundary. A generated summary must carry its business key, or it duplicates on every run. A document born here with nothing to reconcile against may omit it.
section is optional too, and a document with no readable body is caught by a warning-severity invariant rather than an error -- composition-has-narrative, on the invariants page. A note with no body renders as a blank chart entry rather than as a visible failure, which is the outcome the warning exists to make noisy.
Classify with type, refine with category¶
The rule is one sentence: classify with Composition.type, refine with Composition.category. They are two levels of "what is this", they are different questions, and they bind different vocabularies.
type is 1..1 and carries exactly one of three LOINC codes. That is the element a consumer switches on, and the element to search on.
| Document | type -- the class |
category -- the refinement |
|---|---|---|
| Progress note | 34109-9 Note |
11506-3 Progress note |
| Consult note | 34109-9 Note |
11488-4 Consult note |
| Letter | 51852-2 Letter |
51852-2 Letter |
| Day-sheet summary | 34133-9 Summary of episode note |
nexus-daysheet-summary |
| Interval summary | 34133-9 Summary of episode note |
nexus-interval-summary |
| Encounter recap | 34133-9 Summary of episode note |
nexus-encounter-recap |
The refinement set also names 18842-5 Discharge summary, 34117-2 History and physical and 34748-4 Telephone encounter note, all under the 34109-9 Note class.
A letter carries the same code on both, and that is the ordinary case rather than a special one: where a document has no kind finer than its class, category repeats type. The refinement set is a strict superset of the class set precisely so that repeat validates -- a nesting the build enforces rather than the prose merely claiming it.
Why type is extensible and category is preferred¶
The asymmetry is the point, and it is the easiest thing on this page to get backwards. The stronger binding sits on the smaller vocabulary.
type is bound extensible to Composition Classification. The class axis is small and the value set enumerates it: note, summary, letter. That is the whole space, so a code outside it is nearly always a refinement written into the class slot -- exactly the mistake worth a warning. Nothing is rejected, so a document that genuinely is none of the three stays conformant.
category is bound preferred to Composition Kind. The refinement axis is LOINC's whole document space and no set here enumerates it. Extensible would warn a producer for holding a more precise code than the guide happened to list -- punishing precision, which is the opposite of what a refinement slot is for. Preferred says prefer these, and be more precise where you can.
Neither binding is silent in the validator
TypedFhir grades required at ERROR and both extensible and preferred at WARNING; example is not checked. The check fires only for a code outside the bound set, so a warning here means "this code is not one the guide listed", never "this instance is wrong".
One value set per element¶
Three elements ask three different questions about what something is. Nothing in a code says which question it answers, so each element gets its own vocabulary.
A set bound to one element contains only codes that are legal on that element. One set per element is what turns a rule written in prose into a rule a validator can catch.
type asks what class of document this is, and binds the classification set. category asks which kind under that class, and binds the kind set. section.code asks what one section of the body clinically is, and binds the section set. The kind set contains the three class codes on purpose, so a class code is legal in either document slot. Nothing else is legal on more than one of the three.
The error no reader can catch. LOINC hands out document codes and section codes from one system, in one format, with nothing on the code itself to say which axis it belongs to. 34109-9 "Note" types a document. 8716-3 "Vital signs" types a section. Put the first one in a section slot and it is well formed, it parses, it round-trips, and it is quietly wrong for the life of the record. The only thing that separates the two axes is the element the code sits in, which is why that element carries a binding rather than a sentence of prose.
The split reaches the call signature
The class set draws on one code system, so a bare code is unambiguous and TypedFhir takes one: composition.type = "34133-9". The refinement set unions LOINC with the guide's own nexus-composition-type, so a bare code there could mean either system and the client refuses it -- category takes a full Concept. One element, one vocabulary, all the way down.
The general rule is worth carrying to any element. If a value set is bound to two elements that answer different questions, it will eventually permit the very collapse the prose warns against. Bind one set per element, or accept that the rule is advice.
section.code is bound, and extensible on purpose¶
New in 1.22.0. section.code binds Composition Section: the C-CDA on FHIR and IPS section vocabulary restricted to what primary care actually produces -- problem list, vitals, allergies, medications, assessment and plan, and ten more.
Preferred would document a vocabulary and warn on nothing, which would not catch the error the binding exists for. Extensible warns on a code outside the set, and a document code in a section slot is exactly that.
The cost is accepted, out loud
A legitimate section kind the set does not yet name also warns. That is the trade: growing the set is one line, and the alternative is catching this class of error by eye forever.
This matters beyond tidiness. C-CDA on FHIR and IPS both make section.code the element a consumer navigates by. A note whose only statement of what a section is lives in one of this guide's extensions cannot be read by anyone who does not implement this guide.
The body is a stack of blocks¶
The note editor writes an ordered stack of independent pieces. One block is one section, and base R4 already carries three quarters of it.
Nothing records a position, because section is an ordered element in R4. A block raised and left blank is kept, because a gap between two written pieces is part of the shape of what the author wrote.
| A block needs | and R4 already has |
|---|---|
| identity | section.id |
| a heading | section.title |
| its narrative | section.text |
| its position | the order of section itself |
| which kind wrote it | nowhere -- hence one extension |
| that kind's own structured value | nowhere -- hence the other |
Two extensions, and nothing else. That restraint is the design: everything base R4 can carry, base R4 carries.
Blank blocks are kept, and the codes were chosen carefully
An empty-status narrative plus emptyReason notstarted satisfies three base FHIR invariants at once, and notstarted is the honest code -- unavailable and nilknown are claims about the patient, and a blank block asserts neither.
Narrative status is additional, not generated. Non-obvious and correct. generated tells a downstream renderer that the text was derived from structured data and may be thrown away and rebuilt. Here the provider's prose is the content and exists nowhere else in the resource, so a renderer that believed generated would discard it and render nothing.
Three questions per section, three elements¶
They are independent, and collapsing any two of them is the mistake this model most wants a producer to avoid. Two of the three are base R4 and portable; only the render axis is ours.
A problem-list section is 11450-4 whether an editor widget, a letter template or a harness graph produced it. A claims section renders identically however it was assembled. That is the whole reason the three document kinds reduce to one skeleton.
Block type is the render axis, not the authoring tool¶
composition-block-type says how to render a section, not which widget wrote it. The distinction is load-bearing.
Read as "which widget wrote this", a letter has no kind, because a template filled it, and a generated summary has no kind, because a graph wrote it. Two thirds of the sections in this estate become unrepresentable, and a consumer is pushed to infer rendering from author, which answers a different question.
Read as "how do I render this", an editor widget, a letter template and a harness graph are all producers of section shapes, and a consumer renders what they produce through one dispatch. A cited-claims section is the same render kind whether a graph or a person assembled it.
Three kinds are written today, one per stream committed to writing sections.
| Kind | Who writes it | What the render instruction means |
|---|---|---|
text |
the note editor | a run of narrative the author typed. No structured value beside it |
merge-field-body |
the letter composer | this body was filled rather than typed, so a consumer can offer to re-render it against current data, or show which runs came from the record |
cited-claims |
the summary graphs | every claim is backed by a citation. Surface the backing; do not render the claims as bare prose |
Every kind is equal, and text has no standing
It shipped first, that is all. A consumer that special-cases text and treats the rest as exceptions has the dispatch backwards.
An unknown block kind must survive you¶
The block-kind binding is extensible as of 1.22.0, on Note Block Types. It was required through 1.21.x, which made the guide a release gate: ship a vitals block, and every note carrying one is nonconformant until the guide cuts a release. Extensible turns a break into a documentation lag.
A binding cannot enforce what matters most here, so it is stated as a consumer obligation instead.
Render an unrecognised kind as text. Do not store text in its place
Render it as text -- you have no widget for it, the narrative is authoritative, and nothing clinical is lost. Then write the code back unchanged. Display and storage are different decisions. Rewriting an unrecognised kind to text on save is data loss, and leaves the record asserting that a specialised block was prose.
There is precedent both ways, and nobody closes this set. SDC's questionnaire-itemControl answers the same question about which control renders an item: extensible, with form fillers explicitly told to ignore controls they do not understand rather than rewrite them. openEHR, the one ecosystem that has solved composition-of-typed-authoring-units at scale, uses open-ended versioned archetype ids.
A section carrying no kind at all predates the extension and reads as text. That is tolerance for data filed before block authoring existed -- the four-section SOAP notes this EMR wrote for years among them -- not a default new data may lean on.
The contract between narrative and structured data¶
A section carries its content twice, on purpose. This is the most load-bearing rule in the profile and the easiest one to optimise away by accident.
The rule: a renderer shows the structured data and ignores the narrative. Nothing appears twice.
The consequence, and this is where producers get it wrong: both sides must be complete.
- Anything the UI needs must be structured. A field that exists only in prose is invisible to the surface that renders it.
- Anything clinically meaningful must also be in the narrative. Including negatives: "No active problems" is a finding, not filler, and a narrative that omits it reads as a section nobody filled in. Booleans and scalars count too -- a value that lives only inside a structured payload leaves every consumer who is not this UI reading an incomplete document.
Only genuinely absent facts are left out of both. Absent means the fact does not exist, not that it was inconvenient to render.
The two halves are easy to hear as a contradiction, so say them together
"The narrative is authoritative" and "the renderer shows the structured data" are one contract, not two rules competing. The narrative is authoritative for everyone who is not this EMR's own UI; the structured data is the display path for that UI only. Neither sentence is safe on its own.
The invariant to build on: a consumer that ignores every extension in this guide must still get a correct, readable document. A section that breaks it -- clinical content in the structured value and nothing in section.text -- is malformed however well it round-trips.
What a block's structured value rides in¶
The value rides in an Attachment on composition-block-data, not in a JSON string. Three candidate shapes were weighed, in this order of preference.
| Shape | Verdict | |
|---|---|---|
| 1 | SDC -- a Questionnaire plus a QuestionnaireResponse |
Reach for this first. Where a render kind is really a form -- vitals, a diabetes review, a flowsheet -- this is literally the problem SDC exists to solve, and it gets you section.entry promotion for free |
| 2 | valueAttachment, contentType: application/json |
What shipped. Self-describing envelope; what R4 already provides for an opaque payload |
| 3 | a complex extension with typed sub-extensions per kind | Viable only if the shapes turn out small and stable. Not taken |
| x | JSON in a valueString |
Rejected. Self-describing to nobody: a reader cannot tell what it holds without recognising the render kind first, and no published international IG carries editor state that way |
Unreadable is not absent
A value that cannot be parsed reads as no structured value rather than as an error -- one malformed section must not put a whole clinical document out of reach. But a writer that cannot parse a stored value must round-trip it untouched rather than drop it. Conflating unreadable with absent on a write path deletes the stored value.
Block data, or section.entry?¶
Minting real resources per keystroke would put editor state into the clinical record and make section.entry unqueryable for what it is for. That is the argument for keeping them apart today, and the two are expected to coexist on one section.
Whether the structured value belongs in an extension at all is genuinely undecided. What would settle it is the first render kind that promotes its own facts to section.entry on save; a vitals block is the obvious candidate. No consumer action follows from it being open: a writer that uses the extension today is correct either way, because a later move to entry would add a place to read the facts rather than change where the editor state lives.
Letters¶
A letter that has been sent is an ordinary Composition: class 51852-2 on type, a body in section.text, signed by a clinician like any other authored document. Letters are not exempt from the signature rule -- a letter goes out over somebody's name.
A letter body is merge-field-body: the render kind says the prose was filled rather than typed, which is what lets a consumer offer to re-render it against current data or show which runs came from the record.
A template is a different resource entirely. It is a List holding prototype resources in a contained transaction Bundle. Instantiating it clones the prototypes, fills in what the template could not know, rewrites the references between them, and saves the result as ordinary resources. Three reasons that shape was chosen:
- A
Listand not aBundle.Bundlederives directly fromResourcerather thanDomainResource, so it has no extensions, no narrative and no contained resources. A template needs a name, a lifecycle status and a tenancy stamp;Listcarries all three natively. - Not a marked-up
Composition. Marking a resource type "this one is a template" answers one case per resource type. A template that is a list of prototypes answers all of them: a letter template that grows a follow-upTaskgains an entry and changes no definitions. - The clinical profile was not relaxed. A prototype has no patient. Base R4 already permits
Composition.subjectto be absent, so the profile is not weakened to0..1, and no consumer of real letters has to carry a rule about templates.
Do not write sentinel values into a prototype
Where base R4 requires a value -- status, type, date, author, title -- supply the template's own real one and overwrite it at use time. A placeholder like 1900-01-01 validates cleanly and saves cleanly, which is exactly why it is dangerous: a letter dated 1900 is a defect no validator will report.
Migrating a letter classified with a local code
Letters classified with http://services.well.com/fhir/categories/letter-template migrate to LOINC 51852-2 on type. A nexus-letter-template code exists on this guide's own base if a template kind still needs naming -- it is a refinement, so it sits on category under the letter class rather than in the class slot -- but templates are a List, so check whether the code is needed at all before reaching for it.
At most one current List may carry a given template identifier
More than one is a corrupt state in which a consumer must raise an error rather than choose between them. Not newest-by-date, not first-result, not both shown to the user -- every such heuristic makes the defect permanent and invisible while the picker keeps working.
Enclosures: how a letter names what travels with it¶
A letter can go out with chart items attached -- recent bloodwork, the report that prompted a referral, an earlier letter from a colleague. The letter names them in one coded section, and that section is the only record there is.
The section is the enclosures slice on nexus-emr-core-composition: 0..1, coded LOINC 28562-7, with one entry per attachment packet. A packet is a List holding the items in order, each able to say which of its pages were left out. The letter points at the packet; the packet does not point back.
Composition (letter)
├── section[0] the body
└── section[enclosures] code = LOINC 28562-7, title = "Enclosures"
└── entry ──────────▶ List (Attachment Packet)
├── entry ──▶ DocumentReference consult summary
├── entry ──▶ DiagnosticReport CBC, pages 1 and 4 excluded
└── entry ──▶ DocumentReference filed form
Four rules follow, and each has a way of being got wrong:
- Resolve the section by its coding, not by its position. The body is the section at a known index; the enclosures section follows it and a producer that adds sections of its own may put anything between them. Match
systemandcodetogether:28562-7under another producer's system is that producer's section. Enclosuresis thesection.title, not the coding'sdisplay. LOINC 2.83 names28562-7"Chart section Set". Writing "Enclosures" intodisplayclaims a term the concept does not have; it is a heading, and headings live ontitle.28562-7is a section code.11503-0is not.11503-0is the document code for the rendered packet PDF and belongs onDocumentReference.type. A document code in a section slot is the mistake the section value set's extensible binding exists to catch.- Membership lives on the packet. The section names packets, never chart items directly. Which items a packet holds, in what order, and which of their pages are excluded are all on the
List-- which is why the section carries one entry per packet rather than one per document.
A section coded 28562-7 with no entry means nothing travels with the letter; a block raised and left blank is an ordinary empty section and carries section.emptyReason. The slice does not require an entry for exactly that reason.
What the packet records, and what it does not
A packet carries two assembly facts beside its membership: whether the render leads with a generated cover page (nexus-packet-cover-page -- absence there is not false), and which way out of the EMR it was built for (a meta.tag coding under nexus-send-channel). Both say what was chosen while the packet was assembled. Neither says anything went out. The record of a send is the rendered document stored at that moment, and this guide does not model that workflow yet.
Generated summaries¶
A generated summary is a Composition the AI harness writes and maintains. Three kinds, one class, and the one document nobody signs. Generated Summaries is the full treatment; what matters here is how it sits in the shared shape.
The business key carries no period, so regeneration converges on one resource instead of leaving the chart accumulating stale summaries nobody deletes. The exception is a summary whose period is part of what it is -- a day sheet for one date is not an update of yesterday's.
All three kinds are 34133-9 as a class, and no LOINC code distinguishes them, which is what the three local codes on category are for. They are not interchangeable. An encounter recap asserts that the encounter it covers took place. A day-sheet summary is written ahead of a clinic for a booked patient. An interval summary covers a span of time and may have no upcoming visit at all.
Nobody signs a summary. The profile requires a final document to name a legal or professional attester. Generated summaries are exempt, deliberately: final on a summary means this generation is complete, not a human has taken responsibility for the contents. Both alternatives were worse -- leaving them permanently preliminary would be false, and naming an attester who never attested would fabricate a medico-legal signature.
Citations are a separate grain from inputs. Provenance.entity records which resources the activity read; nexus-attributed-fact records which statement rests on which passage, with the verbatim quote. Each fact carries at least one citation by construction, and an invariant rejects an empty quote -- a citation that cites nothing satisfies the cardinality and leaves a fact nobody can audit.
An absent AIAST label means unknown, not human
Nothing backfills the AI attribution label. Read it as evidence a machine was involved, never as evidence one was not. And preserve it when writing a labelled resource onward -- dropping it turns machine-produced content into apparently human-authored content.
Billing, sharing and editor setup¶
Three extensions ride the document.
composition-billing carries what the clinician entered at note closeout: the service (fee) code, the diagnostic codes supporting it, and units where the fee code is charged per unit. dx repeats -- a claim routinely carries more than one, and a reader taking only the first has a different claim than the clinician entered. Absent units means the code is not charged per unit, not that one unit was billed.
composition-ehr-share carries one record per completed share of a signed note to an EHR outside the practice. It repeats: a note shared three times carries three. Completed only -- this is read back to patients as a disclosure record, and listing a pending share would tell a patient their record went somewhere it has not yet gone.
composition-note-setup carries the editor setup a note was opened on: the visit type the provider chose, and the template the note started from, so a draft reopens on the footing it was left on. Both sub-extensions or neither -- a template without its visit type cannot be offered again. It records what the note was opened from, never what it now contains: a provider may switch templates, delete every heading the template gave, or type their own, and the authoritative structure is always section.
Why billing rides the note rather than a Claim. R4 gives Composition no element for billing, and a separate Claim has its own lifecycle: submitted, adjudicated, reassessed, written off, all without the note changing. Carrying the billing on the note keeps what the clinician entered at closeout attached to what they wrote. A Claim raised from it answers a different question and is expected to exist alongside.
Why the share destination is a string. It is routinely an external system with no Organization in this practice's directory, and a reference that cannot resolve is worse than a name that can be read.
Why the setup template codes are not nexus-template. CodeSystem/nexus-template is the coding view of the stored template registry -- per-tenant slugs of template List resources, which a letter carries on meta.tag to say which stored template it was composed from. The setup codes name the fixed catalogue of note structures compiled into the application. Both are kebab-case slugs over different value spaces, and they are not interchangeable. Both setup bindings are extensible, for the same reason the block-kind binding is: a required binding would make this guide a release gate on the note editor.
Signing, amending, dating¶
The lifecycle is status. There is no extension for it and none should be created.
| Note state | status |
|---|---|
| draft, being typed | preliminary |
| signed | final |
| signed, then edited | amended |
| voided / created in error | entered-in-error |
Once status is final or amended, the document must carry an attester with mode legal or professional. A signed note with nobody identified as having signed it is not a record anyone can stand behind. Generated summaries are the one exemption; letters are not exempt.
Amendments are version history, not a second resource
Update the same resource in place and set status to amended. The business identifier does not change. Do not create a second Composition and relate it back: that leaves two resources where the chart must know to display one, and every consumer has to learn to filter. relatesTo is deliberately left unprofiled so it stays available if a true addendum workflow appears -- original untouched, new signed content appended with its own author. That is a different clinical act.
Three dates answer three different questions, and the one most readers want is not the one they find first.
event.period.start |
The clinical date. When the documented care actually happened. It does not move. Sort a chart timeline by this |
date |
the composition editing time, and it moves when a note is amended. It is only the fallback arm of the effective date, reached when event is absent -- so a note that leaves event empty re-positions on a timeline to the date of its own amendment |
attester.time |
when the signature happened. A third question again |
Populate event whenever the note documents care that happened at an identifiable time, which for a progress note, a consult note or a discharge summary is always. If you need a stable "when was this written", none of the three answers it: use the encounter's period, or the date of the earliest version in history.
Reconstructing the as-signed note is reliable only on the authoring server
Version history is server-local: _history does not travel in bundles, $everything, or sync pipelines, all of which move current versions only. Across a system boundary an amended note arrives as its amended text, with the as-signed content absent. meta.versionId is server-opaque in R4 -- consumers must not infer ordering, authorship or "which version was signed" by parsing or sorting it. A published versioning policy is what would make the amendment model safe to rely on across a boundary; this guide does not declare one yet.
AI-drafted notes
A Device may appear in author alongside the human. The attester is always the clinician, never the Device: generation and responsibility are different acts, which is why they are different elements. And absence of a Device author means origin unknown, not "typed" -- notes migrated from OSCAR Pro carry no origin information at all.
Where the canonicals live¶
Published canonical URLs are forever, which is why the free moment to move one is the moment before the first write.
composition-block-type and composition-block-data were first minted under services.well.com and moved to this guide's own base before publication. Those canonicals are settled and will not be renamed.
composition-billing, composition-ehr-share, composition-note-setup and UpdatedBy have live clinical data behind them, so the address cannot be changed by editing a constant.
Writers: write the canonicals this guide declares. Readers: expect both
These four are declared at https://fhir.apps.health/..., and stored records also carry them at the legacy http://services.well.com/fhir/StructureDefinition/.... Match on the URL suffix -- resolving only the declared canonical silently misses billing, sharing and the editor setup on every note filed under the legacy address. UpdatedBy is the long tail: it rides meta on every version of every resource ever saved, so history keeps the legacy address indefinitely even once new versions stop carrying it.
Which further artifacts under that host the guide adopts, which it redeclares at its own base, and what a migration costs where records already carry the old address, is not settled by declaring these six. It is stated here so that a reader who finds a services.well.com URL on the wire knows the guide is aware of it rather than silent on it.
The whole model on one page¶
| The document | |
|---|---|
type |
the CLASS: note, summary, letter |
category |
the refinement under it |
subject |
the patient. Exactly one |
status |
preliminary / final / amended |
attester |
who signed. Absent on summaries |
author |
a person, or a Device |
event.period.start |
the clinical date |
date |
the editing time. It moves |
identifier |
a business key, when there is one |
| The section | |
|---|---|
id |
the block's identity |
title |
its heading |
text |
its narrative. Always populated |
code |
what it clinically IS |
author |
who produced it |
entry |
structured clinical facts |
emptyReason |
why a raised block is blank |
| order | is the sequence. Nothing records it |
| Extensions this guide adds | |
|---|---|
composition-block-type |
how to render a section |
composition-block-data |
that render kind's own value |
composition-billing |
closeout billing |
composition-ehr-share |
one completed share |
composition-note-setup |
the visit type and template a draft was opened on |
meta.security |
AIAST, if a machine wrote it |
meta UpdatedBy |
who last saved a version |
The four invariants¶
- A consumer that ignores every extension still reads a correct document. Every section carries its narrative whatever its kind. Not machine-checkable.
- Both carriers are complete. Anything the UI needs is structured; anything clinically meaningful is also in the narrative, negatives included. Not machine-checkable.
- A signed document names its signer. Unless it is a generated summary, which nobody signs. Enforced.
- A section code is a section code, never a document code. Enforced, as a warning.
Two of the four are checkable by a machine and two are not. That is worth knowing when deciding what to test.
The narrative-in-FHIR model is what makes a note greppable, diffable and readable by a model without a second fetch. The moment clinical content moves into a payload only one UI can parse, that stops being true -- quietly, and for everyone downstream at once.
What is not settled¶
Most of the model above is stable. These are the parts that are not, with what would decide each.
- Whether a block's structured value belongs in an extension at all. The alternative is
section.entry, which the profile already nominates as a section's structured counterpart. They hold different things today: editor state versus separately-addressable clinical facts. Decided by: the first render kind that promotes its own facts toentryon save. - Which mechanism carries AI authorship detail.
AIASTsettles whether a machine was involved. Which agent, from what inputs, under whose supervision, is carried either by aDeviceinauthoror by aProvenancerecord. Neither is foreclosed. Decided by: what consuming code needs to read. - How a
Deviceauthor is referenced. Whether AI agents are registered asDeviceresources in the store, whether one staticDevicedenotes "a Nexus AI agent", or whether the reference is by identifier into the harness's own graph with no resource on the server. The profile deliberately does not constrain it. - Assessment: the note section, or the
ClinicalImpression? A note carrying an assessment section and aClinicalImpressionhas two homes for the same text. The guide does not yet rule which is authoritative. - Whether
typeshould be tightened. The class binding isextensibletoday, so a document classified with an off-vocabulary code is still conformant. Decided by: the letters migration -- while an off-vocabulary code is still in stored data, tightening would make existing records invalid.
Composition publishes no search parameters
The generated-summary design needs three -- subject, type and above all identifier. Without identifier, a producer cannot find the summary it wrote last time and update-in-place silently becomes duplicate-on-every-run. The CapabilityStatement lists the search interaction, which is easy to mistake for a guarantee about parameters. Measuring the three against the store and declaring what works is owed IG work.
What is deliberately not open. One resource for three document kinds. type as the classifier. Narrative in FHIR rather than behind a blob pointer. Amendment as version history. One section per block, ordered, not nested. Those shipped, they are what the examples demonstrate, and reopening them costs more than it buys.
Related¶
- Generated Summaries -- the harness-written summary in full: business keys, update-in-place, what a consumer must get right.
- Templates -- the
Listof prototypes, instantiation, and the letter template. - AI Attribution -- the
AIASTlabel and the boundary between authorship and conversion. - Composition profile and its element details -- the specification this page narrates.