Skip to main content

Workflow & Policy Engine

Clinical operations differ by ward, by encounter type, by insurance scheme, and by country. medOS handles that variation with configuration, not code: a deterministic rule engine that an operations admin can edit live, a workflow editor where the transition between two states is the unit of integration, and a direction of travel toward a single shared state-machine kernel that every hospital process plugs into.

Config
Rules are data
Deterministic
Gates evaluate predicates
Fail-open
Empty / unreachable = old behavior
Default-OFF
New surfaces opt-in
Realtime
Edits propagate live
Bilingual
Local + English messages

Three design rules

These hold across every part of the engine and are worth stating up front.

RuleWhat it means
DeterministicA gate is a pure predicate over a context object. The same inputs always produce the same decision. No randomness, no model in the decision path.
Fail-openIf the rule table is empty or unreachable, surfaces fall back to their prior hardcoded behavior. Day-one regression risk is zero.
Default-OFFNew rules ship as drafts; new editor surfaces sit behind feature flags. Nothing changes until an admin deliberately turns it on.

Policy gates — the rule engine

The original problem: workflow checks like "payment must be settled before a specimen is collected" were hardcoded and duplicated across dialogs. That default was correct for routine outpatient flows but wrong for emergencies, VIP patients, insurance pre-authorization, and per-country market packs — and every variant needed a code change and a redeploy.

Policy gates replace those hardcoded checks with admin-editable rules. Each rule is a row of JSON: scope (who/what it applies to), predicate (what must be true to pass), and action (what happens when it fails). Admins manage them at an in-app admin page, and changes propagate to every open clinical screen in well under a second.

Anatomy of a rule

PartHoldsExamples
ScopeThe dimensions a rule applies to — empty means "all"product category, department, clinic, facility, encounter type, patient class, benefit scheme
PredicateAn all / any tree of conditions over a contextorder.payment_status equals paid, patient.deposit_balance gte order.total
ActionWhat a failed predicate doesblock, warn, or require_override, each with a severity and a bilingual message
PriorityHigher number wins when rules conflicta priority-500 ER bypass overrides a priority-100 default gate

Predicate operators include equality, set membership, numeric comparison, and existence checks. Field paths are dot-notation against the evaluation context, and a value can itself be a path, so cross-field comparisons (deposit vs. order total) work without special-casing.

How evaluation works

1. Load active rules whose trigger matches the attempted action
2. For each rule:
scope doesn't match the context -> skip
predicate empty OR predicate passes -> skip (satisfied)
otherwise -> the rule has FIRED
3. No rule fired -> not blocked
4. One or more fired -> sort by priority, take the top:
block -> blocked
warn -> warning shown, action allowed
require_override -> blocked, but an authorized user may override

The evaluator is a pure function with no I/O, so it is trivially testable and can be reused on the server. Each clinical screen builds a context from the data it has, asks the engine, and renders accordingly — falling back to its legacy check if no rules are present.

Authoring

Admin page

Operations admins list, create, edit, and toggle rules in-app. A condition builder exposes the available fields, operators, and actions — no SQL, no deploy. Edits stream to open clinics over a realtime channel.

listcreateedittogglelive
Runtime

Per-screen evaluation

A screen builds a context and calls the gate hook; the decision drives the button, message, and severity.

blockwarnoverride
Scope

Multi-dimensional

One table holds default and per-region rules side by side; market packs seed country-specific defaults.

deptschemeencounterfacility
Conflict

Priority resolution

Higher priority wins. A bypass rule cleanly overrides a default gate without editing either.

bypass priority
target 95%
Safety

Fail-open fallback

Empty or unreachable rule table means each dialog degrades to its prior hardcoded behavior — no regression.

rules presenttable emptyoffline

The workflow editor — transitions as the unit of integration

A clinical workflow is not a bag of nodes with rules bolted on the side. The transition — the edge from one state to the next — is the thing that integrates everything. Every transition carries five slots:

SlotQuestion it answers
TriggerWhat causes the transition to be attempted — a user action, an event, a timer, a webhook?
ParticipantWho may fire it — which role, or a non-human automated actor?
GateA deterministic predicate that must pass for the transition to commit — the same policy-gate shape.
Action formWhich form or modal opens when the transition fires.
EffectsWhat the committed transition writes or emits downstream.

Gates, queues, and worklists are one model

Once the transition is the single artifact, the surfaces clinicians see every day turn out to be projections of it, not separate systems:

┌─────────────────────────────┐
│ Transition (the edge) │
│ trigger · who · gate · │
│ form · effects │
└──────────────┬──────────────┘
┌───────────────────────┼───────────────────────┐
▼ ▼ ▼
Policy gate Department queue Clinician worklist
= the gate slot = patients waiting at a = the outgoing transitions
node for an outgoing from my patients' nodes
transition that I may fire and that
pass their gates

So the same gate that blocks an action also drives whether the button appears on the doctor's worklist. There is one definition of "what is allowed here," and the queue and worklist are views over it. This is additive: an edge with no configured transition behaves exactly as it does today, and the legacy work-list path is left untouched.

One transition, several validators

Three legality checks already exist in the system and continue to do their jobs — they become validators and projections of the canonical transition rather than competing definitions:

CheckValidates
Design-time connection rulesWhich node types may be wired together in the editor
Runtime state machineWhich concrete state may follow which, at execution time
Worklist action mappingWhich button moves a row to which state

A transition may narrow what these allow, never widen it. None is removed; each is re-pointed to read the one shared object.

Toward a shared state-machine kernel

Pull the camera back and the same shape repeats across the hospital: admissions, transfers, discharge, every order type, blood bank, the operating room, labour, consults, claims, payments. Each is a state machine — states, transitions, guards, side-effects, and a timeline — and historically each was hand-built. That produced three recurring problems the kernel is designed to remove:

ProblemSymptomKernel answer
Inconsistent emissionSome status changes notify downstream systems; some mutate silently and desync the read modelOne emission choke point every status change funnels through
Colliding vocabulariesThe same concept gets several enums across servicesStatus-as-data — states defined in one table, mapped to standard vocabulary
Scattered timestampsAudit columns differ per entity; some processes have no timeline at allOne append-only timeline of record keyed by process and step

The kernel is shared conventions and infrastructure, not a monolith. Bounded contexts stay separate; they agree on a timeline of record, a status model, an emission choke point, and column/print projections — with idempotency, a concurrency guard, and append-only audit baked in. State buckets map onto FHIR R4 resource vocabulary (Task, Encounter, ServiceRequest, Procedure, Specimen, Condition, Appointment, and the financial resources) so the canonical names are standard, while internal storage is unchanged.

A deliberate non-goal: medOS does not re-platform onto an external code-first workflow engine. The visual editor plus an event-sourced projection layer already provide an engine that grows with the hospital without a deploy. A heavier saga engine is reserved only for the genuinely hard, compensating flows.

Where automation fits

Automated actors can occupy the participant or effects slot of a transition, but under strict guardrails:

  • Gates stay deterministic. An automated sensor may write a fact with provenance that a gate then reads; it never becomes the gate.
  • Recommender-first. Generated work lands as a proposal for a human to accept, edit, or reject — never auto-final.
  • Reversible-only and capped. An automated actor may only fire reversible, operational transitions, within an explicit autonomy cap, with mandatory provenance. Clinical-risk transitions stay human-only.

See also