Skip to content

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 description into note[0]. The opening body is already in the chain. Copying it stores the same statement twice and opens the conversation with something nobody said.
  • time is the ordering key, which is why the Task Note profile makes it 1..1 where base R4 leaves it optional. One undated turn makes the whole thread's order a guess.
  • Every turn is attributed. author[x] is 1..1 for 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:

  1. 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 intent to #order and writes nothing. See the three verbs.
  2. 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 supportingInformation and Provenance: the written resource cannot use basedOn to name the task, because most basedOn value sets exclude Task. A reader following that trail reaches the real resource and its version history rather than only the proposal.
  3. Say what was decided. The acceptance or the decline is a turn, authored and timed like any other. A decline is status = cancelled with a statusReason, and it retains both the task and its contained payload: what a machine surfaced and a clinician declined is part of the record.
  4. Hand it back to the requester. Reassign owner to 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 Task read. Loading a conversation is fetching one resource, not assembling a thread from a message table.
  • "Go back to that conversation" is a Task query, 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. Communication is 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.