Pathology Architecture
Declarative graph in workflow_templates + live-editable policy_gates layer for pathology.
Overview
Pathology gets the same treatment as consultation/billing/pharmacy — a declarative graph in
workflow_templates, a live-editable rule layer inpolicy_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)
1. Footer (PathologyStateFooter) — recommended
Sticky bottom bar inside any pathology page. Reads:
- The active pathology workflow template
- The current encounter’s specimen state
- All
policy_gatesrules fortrigger 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_workflowcan be edited and saved - Rule editor: Existing
/admin/policy-gatescan target pathology triggers - Live propagation: A second tab running the app reflects changes within ~300ms
- Central button: The
PathologyStateFooteris 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/diagnosticchange + Docker deploy) encounter-orchestratorprojection of pathology state intoencounter_journey_cache(would require a Deno deploy)- Per-organisation tenancy of the pathology graph (uses the same
organization_idscoping 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) |