Documentation · OpenMRS integration · updated 15 September 2026

What we are contributing to OpenMRS.

If you maintain or follow the OpenMRS chart-search modules, this page is for you. It lists what our three open pull requests change, why they are proposed in a particular order, and what has been reviewed so far. Nothing on this page is merged yet; it describes branches under review and the tests that check them.

Status: under review · heads QueryStore cfced36, ChartSearchAI d46f517, ESM 77f61c8 How we sequence the work: upstream merge roadmap

In plain words

Three repositories, three pull requests.

QueryStore is the OpenMRS module that indexes a patient's records so they can be searched and read back quickly. ChartSearchAI is the module that answers a clinician's question about a chart using a language model, either one bundled with the module or an optional external service (Med Agent Hub). The ChartSearchAI ESM is the piece of the OpenMRS web interface that shows the question, the answer, the records it drew on and the checks that ran.

The changes let ChartSearchAI use either answer source through one shared set of rules, and give QueryStore one well-defined way to hand over a patient's records with an honest statement of whether the set is complete. They are proposed in the order below because each depends on the one before it: the backend compiles against types the QueryStore change adds, and the interface renders events the backend change defines.

openmrs-module-querystore

#68 Patient-record read API and context slice

Size
+4,675 / -104 · 60 files
Tests
API 525 · OMOD 53 · 0 failures
CI
Java 8 / 11 / 17 / 21 green
Review
Three maintainer rounds so far; every thread has a reply
State
Mergeable

openmrs-module-chartsearchai

#157 Dual-provider clinical answer boundary

Size
+10,983 / -343 · 97 files
Tests
API 2,126 · OMOD 198 · 0 failures
CI
Paired Java 11 / 17 / 21 green
Review
Not yet reviewed by the maintainer
State
Resync scheduled after #68 publishes

openmrs-esm-chartsearchai

#23 Provider-neutral answer lifecycle

Size
+8,075 / -1,837 · 39 files
Tests
462 tests · lint · TypeScript · build
CI
Build green
Review
Not yet reviewed by the maintainer
State
Mergeable, current with main

QueryStore #68

One authorized way to read a patient's records, which says when the set is incomplete.

QueryStore takes on record projection, dates, completeness and question-aware selection. It does not compose prompts and knows nothing about models; that stays with the module asking.

Read API

  • GET /ws/rest/v1/querystore/patientrecord in three modes: the full chart by patient; a ranked top-K by patient and q, or q alone; a context slice with mode=context.
  • Every mode requires the OpenMRS Get Patients privilege. Unknown modes and malformed typed parameters return 400.
  • Paging honours the live webservices.rest.maxResultsAbsolute ceiling: an oversized limit is clamped, a ranked window that would extend past the ceiling returns 400, and links carry the effective page size.
  • All three exits send Cache-Control: private, no-cache, must-revalidate; the full chart also carries an ETag for revalidation.

Completeness

  • chartTruncated comes only from the backend's explicit signal: an Elasticsearch size limit, failed shard, timeout or early termination; a MySQL per-table read failure; a Lucene per-index failure or an index the enumerator could not open.
  • projectionComplete reports the deployment-wide bootstrap state; a failed lazy projection for one patient is disclosed on the read instead of returning an empty chart marked complete.
  • Snapshot identity and the ETag include both completeness bits, so a cache cannot serve a partial chart as the complete one.
  • The warning logged for an incomplete read names the signal that fired, so an operator chasing it looks at cluster health or paging, whichever applies.

Dates

  • Each record carries clinicalDate and dateKind (clinical_event, administrative, unknown) beside the sort date, so a temporal consumer never reads an administrative date as an event.
  • Providers of custom record types must declare date_kind; the architecture record carries the re-bootstrap advisory for deployments indexed before the fields existed.

Context slice

  • getContextSlice(patient, question, request) returns tier-tagged records: mandatory clinical core, recency anchor, typed completeness, similarity.
  • Each page carries chartSnapshotId (the same complete-chart fingerprint the full read publishes), sliceId, effectiveTypes and temporalApplied; a consumer combining a ledger with a later slice must match the snapshot.
  • Selection is deterministic when interpretation is off. interpret=true adds typed and temporal cues; lab-panel expansion and stopword stripping run in the store, and words that carry clinical meaning (negation, status, laterality, body site) are protected.

What the tests pin down

  • A handled backend failure is never reported as a complete empty chart.
  • Paging never links to a request the same controller rejects.
  • Existing in-JVM QueryStoreService implementations keep working unchanged.

ChartSearchAI #157

Two ways to produce an answer, one set of rules for both.

The bundled model stays the default for a fresh installation. Med Agent Hub is a separately configured option, never a silent fallback. ChartSearchAI keeps authorization, prompt assembly, sessions, audit and persistence in either case.

Provider boundary

  • ClinicalAnswerProvider with bundled and hub implementations; a registry driven by chartsearchai.providers.enabled (default bundled).
  • GET /providers lists enabled providers with readiness and a reason when unavailable; a picker is requested only when more than one is enabled.
  • An unknown, disabled or unready selection fails with an explicit turn error. There is no cross-provider fallback.
  • Declared capabilities gate optional features: answer check, review, In-Depth, structured blocks, multi-turn context, token streaming.

Turn lifecycle

  • One request, event and envelope model for Answer, validation, In-Depth, final evidence, errors and cancellation; a validator enforces stage order.
  • Three text channels stay distinct on the stream: a provisional preview with its own citation numbering, committed reasoning, and the answer.
  • Cancellation reaches a running inference. A terminal event is always delivered, and it waits for audit persistence, so a save failure emits one error rather than a success followed by an error.

Conversations

  • Persistent clinical conversations and turns with Hibernate mappings and Liquibase changesets; a conversation is bound to one provider.
  • POST /chat/new, GET /chat, POST /chat/stream; a restored session hydrates the same lifecycle the live stream produced.

Context and budgets

  • Query-scoped chart selection is delegated to QueryStore's context slice; the turn abstains on a truncated chart or when mandatory evidence cannot fit.
  • Exact token budgets from the inference server; an oversized prompt is rejected explicitly instead of being silently cut.

Safety and evidence

  • The safety badge reports the validation that actually ran, so it means the same thing on both providers.
  • Medication-rule and cross-reactivity findings are reported separately; a check with missing data, failed reads or partial scope reports limited or unavailable rather than a clean result.
  • The five answer-limit statements and the safety chip ride on every envelope-bearing event and on the persisted turn; In-Depth validation summaries are persisted for reload and review.

Hub pathway

  • One OpenAI-shaped chat completion request with extension fields (patient, session, request_id, require_product_profile, context), answered as completion chunks mapped onto the turn events.
  • The hub's validation, In-Depth and safety results are relayed, not reinterpreted in Java; GET /models relays hub product profiles.

What the tests pin down

  • Provider selection never changes silently; changing provider starts a new conversation.
  • Final citations, temporal checks, medication safety, audit records and restored sessions use one provider-neutral contract.
  • Until #68 publishes, continuous integration builds this branch against the exact QueryStore source under review; those paired jobs are removed once the published dependency exists.

ChartSearchAI ESM #23

The same answer, checking, evidence and safety display, whichever source answered.

The interface half of the backend change. Low-confidence, edited, rejected and withheld output stays visible for a person to review; nothing important lives only in a tooltip.

Turn lifecycle

  • One reducer for Answer, validation, In-Depth, final evidence, errors and cancellation, driven by /chat/stream.
  • Token streaming: answer and reasoning deltas accumulate while answering, the final event restates the whole answer; the preview is shown with its markers stripped and cleared at the first committed delta.
  • Stale or post-terminal events cannot leave the interface pending; the chat store outlives the panel.

Provider choice

  • A provider picker appears only when discovery returns more than one enabled provider.
  • An explicit or restored choice that becomes unavailable stays selected and visibly unavailable; only a deliberate switch changes provider, and it starts a new conversation.
  • A product-profile picker is backed entirely by Med Agent Hub metadata.

Evidence and safety rendering

  • Citation chips with per-finding severity badges and per-kind reference titles; a misattributed marker is struck through and does not navigate.
  • The five answer-limit statements render on both providers, reconciled with the upstream change that introduced them.
  • Medication-safety status reads Checked, Limited safety check or Safety check unavailable, with package id, version, review state and provenance visible without hover.
  • A rejected In-Depth section shows the concrete validation reason; low-confidence and edited content stay available for manual review.

Contract fixtures

  • The shared dual-provider conformance fixture is mirrored into the ESM and pinned by tests, byte-identical to the copies in QueryStore, ChartSearchAI and Med Agent Hub.

What the tests pin down

  • Both providers produce the same lifecycle semantics on screen.
  • Provider selection survives refresh and session hydration.
  • Cancellation and errors always reach a terminal state.

Review ledger

Every maintainer round, and what answered it.

RoundPull requestThreadsAnsweredCommits
5 to 6 Aug 2026QueryStore #68108 Sep 20268dc6ef9 and the paging remediation: strict mode, typed-parameter 400s, chartTruncated on the plain read, per-backend completeness overrides
9 Sep 2026QueryStore #68714 Sep 2026d675fe9 Lucene enumerator skip disclosed · 3057478 failed cold touch disclosed · 1b1c995 laterality stopwords · ba6bfc1 dead overloads · d8c1e17 live page ceiling and paging docs · d10e9e4 architecture-record corrections
15 Sep 2026QueryStore #68515 Sep 202669e12fe incomplete-read warning names its cause · 87619c4 body-site stopwords · 936bee7 unused constructors · cfced36 cache headers and the property-pinned ceiling test
none yetChartSearchAI #157 · ESM #230Automated review onlyAwaiting the maintainer; #157 resyncs when #68 publishes

Each thread carries a reply naming the commit and, where behaviour changed, the test that failed before the fix; threads are left open for the maintainer to resolve. For the two September rounds, every behaviour change was covered this way. Review is ongoing, and later rounds may find more.

Not carried, by decision

What the nine earlier pull requests had that these do not.

Nine earlier pull requests from May and June were closed on 14 September 2026, each with a note pointing at its successor. Most of their content lives on in the three above in a different shape; two ideas were set aside on purpose and are listed first.

Runtime swap of the bundled model, per-request override

#22, #25. Replaced by provider selection and hub product profiles; the bundled engine is configured through global properties.

Frozen-session chart in a stable prompt prefix

#20. Prompt-prefix reuse is deferred in the dual-provider roadmap and returns as its own change.

Reflective QueryStore access without the Maven dependency

#19. Reverted to the typed bridge; the paired build proves the pinned pair instead.

Startup provisioning of privileges and role bindings

#21. Privileges are declared in config.xml; role binding is a deployment concern.

QueryStoreClient seam

#72. Superseded by direct typed consumption of the context slice.

LM Studio picker grouping, inline per-model picker, legacy multi-turn client

ESM #11, #10, #9. Superseded by the provider picker, the hub-profile picker and the turn lifecycle.

Sources: the three pull requests, their continuous-integration results, and the integration branches as read on 15 September 2026. The merge sequence, acceptance criteria and iteration log live in the upstream merge roadmap; the requirements authority remains the dual-provider parity roadmap in the project documentation.

This page describes what is proposed, not what is decided: the maintainers of these repositories review and merge on their own schedule, and the branches may change in response. Test counts and continuous-integration results are the checks named above and nothing more; this page makes no claim about clinical validation or about how the software performs in a deployment.