medOS ultra

Pathology Architecture

Declarative graph in workflow_templates + live-editable policy_gates layer for pathology.

3 min read diagramsUpdated 2026-05-01docs/pathology/architecture.md

Overview

Pathology gets the same treatment as consultation/billing/pharmacy — a declarative graph in workflow_templates, a live-editable rule layer in policy_gates, and a central transition surface in the running app.

Today, pathology dialogs already call usePolicyGate() for collect_specimen / send_specimen triggers. What’s missing is the editor surface that lets a non-engineer change the flow + rules and see the change land live. This folder fills that gap.

End-to-End Flow

                ┌────────────────────────────────────┐
                │  /admin/pathology-workflow         │  ← editor surface (this folder)
                │  PathologyWorkflowEditorPage       │
                └──────────────┬─────────────────────┘
                               │ saves
                               ▼
   ┌───────────────────────────┴───────────────────────────┐
   │                                                       │
   ▼                                                       ▼
┌────────────────────┐                          ┌─────────────────────┐
│ workflow_templates │                          │   policy_gates      │
│ (graph: nodes,     │                          │ (rule: trigger,     │
│  edges, panels)    │                          │  predicate, action) │
└─────────┬──────────┘                          └──────────┬──────────┘
          │ Supabase realtime                              │ Supabase realtime
          │ (300ms debounce)                               │ (300ms debounce)
          ▼                                                ▼
┌──────────────────────────────────────────────────────────────────────┐
│                       Running clinical app                           │
│                                                                      │
│   PathologyStateFooter   ◀───  usePolicyGate({ trigger })            │
│   (central transition                                                │
│    button — "Footer")    ◀───  workflow_templates → state graph      │
│                                                                      │
│   GlobalModalRenderer    ◀───  universalTransitionModal              │
│   (modal injection)            (renders any pathology transition)    │
│                                                                      │
│   onAfterCollect, etc.   ◀───  aftersave hooks fire on success       │
└──────────────────────────────────────────────────────────────────────┘

Layers

Layer Source of Truth Used For
Write truth MongoDB (Moleculer labRequest service) Specimen state transitions
Event log Supabase hospital_events Append-only audit
Graph definition Supabase workflow_templates (JSONB nodes/edges) What the canonical states are, what transitions exist
Rule layer Supabase policy_gates Per-trigger gating (block/warn/allow)
Read model Supabase encounter_journey_cache.pending_tickets + department_queues UI rendering
Realtime channel Supabase realtime on both tables Cross-tab live updates

Three Integration Surfaces (the user’s three options)

Sticky bottom bar inside any pathology page. Reads:

  • The active pathology workflow template
  • The current encounter’s specimen state
  • All policy_gates rules for trigger LIKE 'pathology_%'

Renders: a single primary transition button for the next legal state, plus secondary actions (cancel, hold, reject). Editing a rule in /admin/policy-gates flips the button live.

2. GlobalModalRenderer injection

For transitions that need a confirmation form (e.g., “Sign Out” requires pathologist signature + diagnosis), the footer button doesn’t mutate directly — it dispatches to universalTransitionModal via openModal(). The modal registry already has the wildcard handler at web/src/common/components/medical/builder/modal/registry/modalRegistry.ts:129.

3. Aftersave hook

For inline mutations (e.g., specimen collect from a worklist row), the existing onAfterCollect callback at web/packages/diagnostics-kit/src/pathology/collect-specimens/components/dialog/dialog-specimen-collect/DialogSpecimenCollect.tsx:67 is invoked after a successful save. The footer subscribes to a small store that this callback writes to, so the next-state button updates without a refetch.

All three surfaces consume the same workflow_templates graph + policy_gates rules — no duplication.

What This Mock Demonstrates

  • Flow editor: The graph in pathology_workflow can be edited and saved
  • Rule editor: Existing /admin/policy-gates can target pathology triggers
  • Live propagation: A second tab running the app reflects changes within ~300ms
  • Central button: The PathologyStateFooter is the single place users hit “advance state” — no scattered buttons across miniapps

What This Mock Does NOT Do

  • Backend Moleculer event emission for pathology transitions (would require services/diagnostic change + Docker deploy)
  • encounter-orchestrator projection of pathology state into encounter_journey_cache (would require a Deno deploy)
  • Per-organisation tenancy of the pathology graph (uses the same organization_id scoping as other templates)

These are documented in integration-guide.md under “Deep Mock — Future Work”.

Key Files

File Purpose
web/src/common/components/medical/builder/pathology-workflow-editor/index.tsx Main editor page
web/src/common/components/medical/builder/pathology-workflow-editor/components/PathologyStateFooter.tsx Central transition button
web/src/common/components/medical/builder/pathology-workflow-editor/defaults/pathology-default-workflow.ts Seed nodes + edges
web/src/common/components/medical/builder/pathology-workflow-editor/types/index.ts PathologyNodeType enum
web/packages/medical-kit/src/medical-worklist/defaults/pathology-laboratory-workflow.json Existing JSON template (referenced)
web/packages/diagnostics-kit/src/pathology/type.ts EnumPathologyStatusVJRD runtime enum
docs/architecture/policy-gates.md Rule layer (reused)
docs/architecture/encounter-orchestrator-triggers.md Read-model layer (reused)
Ask Anything