Conversation Threading¶
Every conversation in Nexus EMR runs on one mechanism. A colleague's question, a machine suggestion
a clinician accepts, a chart question put to an AI agent — all three are the same thing: a Task,
whose opening body is description and whose subsequent turns are note.
There is no message resource, no chat store, and no AI-specific conversation model. If you can read one of these threads you can read all of them, and a surface built for one renders the others without new code.
This page is the mechanism and the three flows that use it. Clinical Task Design is the profile detail.
The chain: description, then the notes¶
Task.description the opening body (required; stable for the life of the task)
Task.note[0] the first turn after it
Task.note[1] a reply, a forward's covering question, a reason for hold
Task.note[n] ...
description is 1..1 and note is 0..*. The opening body is a required, single field
written by whoever created the task; the turns are what has been said since. A rendered thread is
description followed by the notes in order — the opening body is the first thing a reader sees,
not a separate header above the conversation.
Four rules follow from that shape, and each of them is a bug somewhere if you skip it:
- A task with no notes is an ordinary state. It is new work nobody has commented on, not a malformed record. A renderer that requires at least one turn will fault on the most common task in the system.
- Do not copy
descriptionintonote[0]. The opening body is already in the chain. Copying it stores the same statement twice and opens the conversation with something nobody said. timeis the ordering key, which is why the Task Note profile makes it1..1where base R4 leaves it optional. One undated turn makes the whole thread's order a guess.- Every turn is attributed.
author[x]is1..1for the same reason. An unattributed turn in a clinical conversation is worse than no turn: a statement in the record with nobody behind it.
Whose turn it is¶
Task.owner is the turn signal, and it is never more than one party. Replying is one write:
append the turn, and hand the task to whoever is next. The owner at any instant is who the
conversation is waiting on — a practitioner, a role, a CareTeam, or an agent graph.
A conversation with no owner is waiting on the clinic rather than on a person (an arrangement not
yet settled -- see Ownership):
owner is 0..1, and
its absence means responsibility has not been narrowed to anyone. That is an ordinary state for a
task nobody has picked up yet, and unassigned tasks are visible to every user by default — see
Ownership.
This is why the turns live on the task rather than in resources gathered up by query. A task handed to you is only intelligible if the conversation travels with it, and the conversation is still readable a year later when someone asks what happened.
Notes are what people said. relevantHistory is what happened — every reassignment,
recorded as Provenance whether or not anyone chose to comment. They are kept apart on purpose,
because an audit trail must not depend on somebody having decided to write a sentence.
The same shape, three conversations¶
A colleague asks a question¶
A practitioner creates a task, writes the ask into description, and assigns it. intent is
#order: authorized work someone is expected to do. The recipient answers by appending a note and
reassigning it back. Nothing here is special, and that is the point — it is the baseline the other
two flows are instances of.
A suggestion is accepted, and the agent hears about it¶
A harness graph files a suggestion as a task: itself as requester, the suggested chart entry in a
contained transaction Bundle named from input, intent = #proposal,
meta.security carrying AIAST, a business key under
nexus-harness-key, and the
thread id of the run that produced it.
Because the suggested resource is contained, a type-level search does not return it: an unapproved
suggested medication is not a MedicationRequest in the store.
Task.code says nothing about any of this. It is a task category — what
kind of work this is — and carrying a suggested resource is a different axis from what the work is.
There is no suggestion code; the containment is the marker.
Accepting it is a round trip, and all four steps matter:
- Instantiate the payload. Fill the prototypes in and POST the contained Bundle as an
ordinary FHIR transaction. Expect this to need a real editor rather than a silent write —
reconciling a suggestion against what is already in the chart (which existing problem does this
update? which of two near-identical medications does this match?) is the normal case, not the
edge case, and the ambiguity is frequently one a clinician must resolve rather than the producer.
Note that instantiating is a separate act from approving: approve moves
intentto#orderand writes nothing. See the three verbs. - Record what was actually created. The written resource may differ from what was suggested,
because the clinician edited it on the way through — so the contained payload is not a reliable
account of what ended up in the chart. Traceback runs on the shared business identifier, plus
supportingInformationandProvenance: the written resource cannot usebasedOnto name the task, because mostbasedOnvalue sets excludeTask. A reader following that trail reaches the real resource and its version history rather than only the proposal. - Say what was decided. The acceptance or the decline is a turn, authored and timed like any
other. A decline is
status = cancelledwith astatusReason, and it retains both the task and its contained payload: what a machine surfaced and a clinician declined is part of the record. - Hand it back to the requester. Reassign
ownerto the agent graph that asked. This is the whole notification mechanism — the harness is run-scoped and nothing sits waiting between invocations, so assignment is the signal. A watcher observes the reassignment, reads the thread id off the task, and resumes that thread with the outcome.
A suggestion never leaves the surface on its own
A suggestion is cleared by an explicit accept or an explicit decline, and by nothing else. It does not expire, and it is not silently dropped on chart reload. Anything that removes work from a clinician's view without a recorded decision destroys the audit trail that the accept/decline distinction exists to create.
Suggesting is not an AI capability
Nothing above is specific to a machine. One practitioner can suggest a chart entry to another —
a supervising physician reviewing a resident's note is the obvious case — and the model is
identical, with a person as requester. Read "agent" here as "whoever asked", and the flow
holds.
A question is put to an agent¶
Asking the harness something is not an API call from the chart's point of view. It is a task assigned to the agent, with the question as the opening body — semantic chart search, "what changed since I last saw this patient", "where is the imaging that mentions this". The agent answers by appending a note and handing the task back. A follow-up question is the next note, and the exchange continues on the same task.
The consequences are worth being explicit about, because they are what this modelling buys:
- Chat history is a
Taskread. Loading a conversation is fetching one resource, not assembling a thread from a message table. - "Go back to that conversation" is a
Taskquery, on exactly the machinery every other worklist uses. - A conversation with an agent is auditable the same way as a conversation with a colleague — same attribution, same timing, same provenance on every handoff.
- The store is the record. Read the conversation from FHIR, not from the harness. The harness thread is working memory; the thread id is the traceback into it, and is never rendered.
Authoring a 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.
An agent is an identifier-only logical reference: author.identifier under the
nexus-harness-graph naming system, an
author.display a human can read, and no author.reference and no Reference.type. R4
does not permit Device as an Annotation.author target and a profile cannot widen a type list,
so the identifier's system carries the identity instead: anything under that system is a graph, by
construction. The nexus-note-agent-author invariant enforces the shape.
PractitionerRole and CareTeam cannot author a turn
Annotation.author in R4 permits Practitioner, Patient, RelatedPerson and Organization
— and Task.owner permits PractitionerRole and CareTeam, which the annotation does not. A
task can therefore be owned by a role or a team while every turn on it is authored by a
person, and a writer that reuses the same reference in both places produces an invalid note.
R5 adds PractitionerRole; it does not add Device. This is a durable constraint, not a bridge
to a later release.
What does not go in the thread¶
- Structured payload. A turn carries prose. A turn that needs to cite chart resources cites
them from the task —
focus,input,supportingInformation— not from inside the annotation. - What happened. Reassignments, status changes and the rest are
relevantHistory, not notes. - A separate conversation resource. Task turns are
Task.note.Communicationis not used for them.
Migrating from 1.18.0 and earlier
Earlier releases modelled task conversation as Communication resources linked back to the
task with email-style threading. A consumer written against that shape reads turns from the
wrong place and will find none. The turns are on the task itself, and the ordering that used to
require a threading model is now the repeat order of a single element.
Open¶
Whether an instantiated task should point at what it produced. Today a completed
suggested-resource task carries focus as the chart element it was filed against, and the
written resource is reached through the shared identifier and Provenance. Setting focus to the resource actually created
would make "what came of this?" a single read instead of a search. What decides it is whether
focus can carry both meanings without becoming ambiguous for the surfaces that already read it as
"which chart element is this task on" — every permitted focus target is also a plausible
promotion target, so the two readings cannot be told apart by type.
Whether the thread id should also ride Provenance, so a chart artifact traces back to its
generating run through the audit trail rather than only through the task, is undecided. What would
decide it is a traceback requirement that survives the task being deleted or archived; if that
lands, the existing extension's context widens rather than a second extension being minted.
Where to go next¶
- Clinical Tasks: the idea — the one-rail model these conversations run on.
- Clinical Task Design — the profile, the state machine, ownership transfer, and how tasks, the inbox and the AI harness fit together.
- Task Note profile — the normative turn: author, time, text, and the agent-authorship invariant.
- Task profile — the normative element definitions and invariants.