medOS ultra

Hospital Decision Twin

Operational/strategic decision-intelligence: seeded DES/Monte-Carlo engine, 7 live agents, Readers boundary, recommender-first.

46 min read diagramsUpdated 2026-06-04docs/architecture/hospital-decision-twin.md

Status: P0–P5 substantially SHIPPED (2026-06-04). The twin HORUS lens (web/packages/intelligence-kit/src/horus/twin/, 32 files): seeded DES/Monte-Carlo engine, 7 live agents, Readers boundary (OQ-11), rule-row constraints + pruning (recommends 10 beds, budget-capped from 12), the optimizer + Guide outputs (readiness / investment memo / execution roadmap), scenario comparison (base/surge/worst), Capture (templates + brief + New-Decision wizard + stakeholders), Analyze (data readiness & coverage), the HITL decision gate (4 sign-offs release optimized→decided — the device firewall), a recommender-only narrative (Ollama, fail-soft), and a Portfolio Command preview — all on seed data. Persistence: migrations 20260604a/b/c (sync-health + integrity + bed_occupancy_spans + twin_*). Packaging: infrastructure/modules/hospital-twin/module.json. Demo: ?target=Twin (or ?target=HorusShell&lens=twin). Verified: tsc --strict clean (32 files) + engine/constraint runtime smoke (coherent + deterministic) + sandbox render smoke (0 console errors). Remaining (deploy + depth): live MedOS per-metric RPCs, AcknowledgementRequest fan-out + audit-lineage UI, standard ERP/HRIS connectors + full crosswalks, country rollup edge fn + market-pack rule rows, back-test harness + recalibration cron — see §14. Host decision: a new HORUS lens (twin) inside @ever-medos/intelligence-kit, not a new package — see §5. Read before any twin / simulation / capacity-optimization / decision-intelligence work, and read alongside horus-intelligence-ai-platform-master.md, modular-multicountry-deployment.md, and fpa-data-warehouse.md.


0. The one-paragraph thesis

“Innovera for hospitals” is not “AI recommends treatments.” Innovera’s atom is a business initiative under uncertainty, so the faithful hospital translation is the hospital’s strategic and operational initiatives — open a step-down unit, add a cath lab, renegotiate an NHSO/UCS scheme, stand up a dengue-surge plan, in-source vs. regionalize blood supply. Commercially this is close to one product: the buyer is the owner / C-suite, the pitch is the same de-risk-the-bet pitch, and in a for-profit hospital even a clinical pathway carries a P&L — so “business initiative” describes almost every decision in the building.

There is an inversion that runs in our favor. Innovera applies stochastic programming to words because its inputs are qualitative — it models opportunities it has no hard data on. We have the opposite problem: MedOS already emits the structured substrate (bed-state logs, vitals hypertables, OR costing, claims facts). So a hospital twin applies stochastic optimization to real operational data, which is more defensible and lands much closer to classical operations research — OR scheduling, bed/queue management, and blood inventory are textbook stochastic problems. The hospital instance is arguably a stronger version of the Innovera thesis than the original.

The one place the framing needed correction: the regulatory boundary does not track business-vs-clinical. It tracks the shape of the output. That single guardrail — and why it’s an asset, not a tax — is §1.


1. The atom and the device line

1.1 The atom: a hospital initiative under uncertainty

A twin is always anchored to a decision a manager is trying to make, not a patient. Canonical examples:

Initiative kind Decision variable(s) Objective Owner
capacity open unit? bed count, phased ramp risk-adjusted NPV ↑, boarding hours ↓ COO / dept head
staffing nurse/physician FTE per shift, roster mix overtime cost ↓, coverage ↑ nurse manager
supply reorder points, in-source vs regionalize stock-out risk ↓, wastage ↓ blood-bank / pharmacy lead
scheme accept/renegotiate payor terms margin ↑, denial rate ↓ revenue-cycle lead
surge activate surge plan thresholds, escalation tiers unmet demand ↓, cost ↓ incident commander
capital buy equipment? lease vs own, sequencing ROI ↑, payback ↓ CFO
service-line expand / launch a service line contribution margin ↑ service-line director

1.2 The axis that matters: output shape, not business-vs-clinical

The device line runs along the shape of the output, not whether money rides on it. Commercial weight is orthogonal — pushing a decision “more commercial” slides it sideways, not across the line.

   OUTPUT SHAPE ▲ population / resource-level output      ┌── THIS PRODUCT lives on the whole top band ──┐
   (the device  │   ┌────────────────────────┬────────────────────────┐
    axis)        │   │ open a cath lab,        │ set bundle pricing,     │   NON-DEVICE
                 │   │ plan the surge          │ optimize the payer mix  │   (ops / strategy / capital)
  ── DEVICE LINE ┤   ├────────────────────────┼────────────────────────┤
                 │   │ patient-level clinical  │ patient-level           │   DEVICE territory.
                 │   │ recommendation          │ MARGIN-AWARE nudge      │   bottom-right detonates:
                 │   │ (may fit CDS carve-out) │ (fails carve-out by def)│   trust + liability + SaMD
                 │   └────────────────────────┴────────────────────────┘
                 ▼ individual, named-patient output
                    clinical framing ◀──────── COMMERCIAL WEIGHT ────────▶ commercial framing
                                     (orthogonal — slides sideways, never across the line)
  • Top band (population / resource): open a cath lab, plan the surge, set bundle pricing, optimize the payer mix. Non-device. This is the entire product.
  • Bottom-left (patient-level, clinically framed): a transparent, non-time-critical recommendation a clinician can independently review can stay a non-device — the Cures Act clinical-decision-support carve-out in the US, with comparable (generally stricter) lines in the EU (MDR Rule 11), Thai FDA, and PMDA.
  • Bottom-right (patient-level, margin-aware): the danger quadrant. The margin-optimizing version fails the carve-out almost by definition, because the “basis” it can’t expose to the clinician is the hospital’s margin. It’s not only regulated — it detonates clinician trust and your liability exposure. Being a for-profit hospital makes a margin-aware patient-level nudge look worse, not better.

1.3 Why the guardrail is an asset, not a tax

The clinical/commercial fusion is an asset on the input side and a liability only on the output side. Hold that distinction and you get the best of both:

  • Clinical signals are exactly what make a private-hospital twin good — let them all in as inputs. Acuity, pathway adherence, complication and readmission rates feed throughput, margin, and capital outputs. You lose nothing by ingesting them; you gain the fidelity that makes the twin worth buying.
  • Holding the output line buys four things: speed (ship the strategy/ops product now, with no device clearance on the critical path), adoption (clinicians won’t touch a tool that visibly optimizes their orders for the house), liability containment, and the option to spin up a separately-cleared clinical track later if the market pulls you there.

The line isn’t a concession to public-sector squeamishness; it’s the thing that lets the commercial product move fast.

1.4 The firewall (hard invariants)

  1. Outputs are population / resource-level only. Every twin output is about beds, rooms, staff, supply, schemes, pricing, capital, thresholds — never “what to do for this named patient.” Clinical signals are welcome as inputs; they may never appear as a patient-directed output.
  2. Cohort, not patient. Where the twin reads acuity (e.g. EWS from vital_signs_ts), it does so only to size an eligible cohort for capacity/throughput math — never to triage, prioritize, or route an individual.
  3. Read-only on clinical data. The twin reads read-model tables and writes only to twin_* tables. Never to encounter_journey_cache, department_queues, clinical records, or any order.
  4. AI is recommender-only. Every number is computed deterministically by the simulator/optimizer; the LLM writes narrative + second-opinion commentary, never decides a number and never gates a decision. (Mirrors disease/progression.ts.)
  5. Human sign-off is mandatory. No recommendation becomes a “decision” without the required expert sign-offs. This is the SaMD firewall — the human owns the call.
  6. De-identified across boundaries. Calibration runs on in-region data; only de-identified aggregates cross a tier boundary (Invariant 6 of modular-multicountry-deployment.md).

⚠️ Conceptual map, not legal advice. The real classification is jurisdiction-specific across all six countries (TH/PH/JP/CN/US/EN). A private-hospital product wants a device-regulatory read before committing the architecture (see OQ-6). Conceptually, “commercially identical, with one output guardrail” is the right way to hold it.


2. Concept map (the three colors)

                         ┌───────────────────────────────────────────────────────┐
   PURPLE  (machinery)   │  CAPTURE ─▶ ANALYZE ─▶ MODEL ─▶ GUIDE ─▶ (PORTFOLIO)    │
   the journey + engine  │       twin engine: DES + Monte-Carlo + optimizer        │
                         └───────────────────────────▲───────────────────────────┘
                                                      │ compose over a shared clock
   TEAL  (the hospital    ┌─────────┬─────────┬───────┴───┬─────────┬─────────┬─────────┐
   modeled — 7 agents)    │ Demand  │Capacity │ Workforce │ Supply  │ Reimb.  │ Quality │ Compliance
                          │ &case-mix│ & flow │           │&pharmacy│         │ &safety │
                          └────▲────┴────▲────┴─────▲─────┴────▲────┴────▲────┴────▲────┴───▲──┘
                               │         │          │          │         │         │        │ calibrate()
   GRAY  (raw substrate)  ┌────┴─────────┴──────────┴──────────┴─────────┴─────────┴────────┴──┐
   the read-model tables  │ bed_status_log · er_bed_stay_log · or_case_costing · vital_signs_ts │
   (already emitted)      │ observations · department_queues · queue_history · admission_log… · │
                          │ blood_* · stock_balance_cache · gold_fact_claims · gold_monthly_kpi │
                          └────────────────────────────────────────────────────────────────────┘
  • Purple = the journey (Capture→Analyze→Model→Guide→Portfolio) + the optimization engine (net-new code).
  • Teal = the hospital’s reality being modeled: seven domain agents (see §7.6).
  • Gray = operational data MedOS already emits (read models).

3. Two products on one engine

The platform packages into two sellable products on a shared engine + governance layer:

Layer Product Buyer What it is
Initiative Workbench one decision, end-to-end teams, service lines, dept heads the Capture→Analyze→Model→Guide spine for a single initiative
Portfolio Command the roll-up + monitoring C-suite / board all initiatives by status/readiness/value-at-stake + actuals-vs-forecast
Twin engine (shared) agents + DES/Monte-Carlo/optimizer; both products call it
Platform / governance (shared) tenancy, residency/sovereignty, RBAC, model registry, audit/lineage

This separation lets you sell the Workbench to a service line now and add Portfolio Command when a multi-site customer materializes — the engine and governance are common to both.


4. Product surface — ~40 screens across seven areas

The full inventory, grouped by the journey. Each screen is tagged by product — [W] Initiative Workbench · [P] Portfolio Command · [X] cross-cutting/Platform — and marks the v1 cut (see §14). Everything untagged- can wait.

A · Workspace shell (cross-cutting) [X]

  • Home / My work — decisions assigned to me, what needs my sign-off, recent runs
  • Global search — find any decision, twin, report, or source across the org
  • Approvals & notifications inbox — sign-off requests, run-complete + drift alerts (reuses AcknowledgementInbox)
  • Org / facility / tier switcher — hospitals, countries, local→country→global tiers
  • Account & preferences

B · Capture — decision intake & framing [W]

  • New Decision wizard — pick a type, state the question
  • Template gallery — open a unit, add an OR/cath lab, surge plan, in-source vs. regionalize (blood, lab), payer/scheme renegotiation, capital equipment, service-line expansion, staffing-model change
  • Decision brief / canvas — objective, scope, constraints, success metrics, horizon, budget envelope, owner
  • Stakeholder & expert assignment — name the experts-in-the-loop + approvers per facet
  • Decision register — every framed decision, its status + owner (the queue)

C · Analyze — evidence & baseline [W]

  • Data source connectors — toggle MedOS FHIR feeds + external sources; show what’s wired
  • Evidence inbox — ingest market data, vendor quotes, internal docs; auto-extract figures (LLM-RAG, recommender-only)
  • Data readiness & coverage — what’s available per facet, gaps, freshness, quality flags
  • Baseline snapshot — current-state metrics (occupancy, OR utilization, case-mix, payer/scheme mix, TAT, complication + readmission rates)
  • Assumptions register — explicit, editable assumptions feeding the model

D · Model — Twin Studio & simulation [W]

  • Twin overview — the hospital twin map; the seven agent tiles + their status
  • Agent workspaces (one per facet) — inputs, model, levers, projected outputs (v1: 3 of 7 live)
  • Scenario builder — define base / surge / worst-case; set demand distributions + levers
  • Constraint & policy editor — staffing ratios, regulatory limits, scheme gates the twin must respect (rule rows)
  • Simulation runner — launch Monte-Carlo / DES runs; queue + progress
  • Scenario comparison — outcomes side-by-side
  • Sensitivity / driver analysis — tornado view of which variables move the result
  • Expert validation — HITL sign-off; flag implausible outputs + lock the twin

E · Guide — decision outputs [W]

  • Readiness Score — composite + per-facet sub-scores, each with rationale
  • Investment memo / business case — generated, editable document
  • Executive summary — the one-pager
  • Execution roadmap — phased plan, staffing ramp, milestones (Gantt)
  • Scenario playbook — contingency triggers + monitoring thresholds (if-then)
  • Recommendation & decision log — the recommendation, the human’s decision, rationale, audit trail

F · Portfolio Command — portfolio & monitoring [P] (all deferred past v1)

  • Portfolio dashboard — all initiatives by status, readiness, value-at-stake
  • Risk & opportunity heatmap — initiative × risk, or facility × initiative
  • Capital allocation — what’s funded, ROI tracking, sequencing
  • Actuals vs. forecast — post-launch monitoring; alerts when reality diverges from the twin
  • Cross-facility roll-up — multi-hospital + multi-country aggregation
  • Board / governance pack — generate the governance deck

G · Governance & platform admin [X] (mostly deferred; only the minimum for v1 ships early)

  • Org & facility management — tenants, jurisdictions, tier topology
  • Data governance & sovereignty — residency, PDPA/APPI/HIPAA mapping, right-to-deletion (planned — via pseudonym-vault key erasure per ai-training-corpus.md; there is no crypto-shred / EVFS infrastructure in the repo today — “EVFS” exists only as a UI label string in the sovereign-genome miniapp, and crypto-shredding is explicitly disavowed in order/addons/README.md)
  • Users, roles & approvers — RBAC, expert rosters, approval chains (minimum viable)
  • Agent & model registry — which models/versions per jurisdiction + the SaMD-safe output-boundary config
  • Audit & data lineage — every recommendation’s lineage + sign-off (the regulatory shield)
  • Integrations — MedOS/FHIR endpoints, external data sources
  • Template & playbook library — manage decision templates + contingency playbooks

5. Where it lives (host + packaging)

Host = a new HORUS lens. HORUS is already the repo’s decision-intelligence surface, and its extension contract is one entry + one self-contained module:

// web/packages/intelligence-kit/src/horus/views.tsx  (existing)
export interface HorusViewDef { id; label; labelTh; icon; Component: LazyExoticComponent }
export const HORUS_VIEWS: HorusViewDef[] = [ /* ask, kpi, insights, surveillance, fpa, capacity, queue, disease, atlas, security */ ]

Adding the twin = append { id: 'twin', … Component: lazy(() => import('./twin/TwinWorkspace')) } + drop a horus/twin/ module. Reuses the shell, theme, aggregate bar, cross-domain fact layer (network-facts.ts), the seed-fallback data pattern, and the SEIR stepSimulation engine as the DES template (§7). (Reality check from the recheck: network-facts.ts + most HORUS Ask domains are seed generators today — only the KPI branch reads live get_kpi_history — so the twin’s live readers/ are genuinely net-new; that’s exactly where the canonical-reader boundary of §6.2.1 belongs.)

web/packages/intelligence-kit/src/twin/          ← new module (additive; touches nothing existing)
├── TwinWorkspace.tsx          # lens entry — the Capture→Analyze→Model→Guide wizard + panels (areas A–E)
├── registry.ts                # TWIN_AGENTS registry (mirrors HORUS_VIEWS shape)
├── types.ts                   # Initiative, TwinAgent, Scenario, RunResult, Distribution
├── engine/                    # des.ts · montecarlo.ts · queueing.ts · optimize.ts · distributions.ts
├── agents/                    # demand · capacity · workforce · supply · reimbursement · quality · compliance
├── readers/                   # canonical-reader iface: MedOS read models OR migration-adapter output (+ seed fallback) — §6.2.1, OQ-11
├── narrative/                 # Ollama recommender-only explanation (mirrors progression.ts)
├── guide/                     # readiness score · memo · roadmap · decision log (area E)
└── seed/                      # deterministic seed data (mulberry32) so the demo never blanks

Packaging (P4): a module infrastructure/modules/hospital-twin/module.json (requires: ['read-model-core', …], flags: { VITE_HOSPITAL_TWIN_ENABLED }), gated by moduleEnabled() like fpa-dashboard/entitlements.ts. Per-country differences (currency, scheme rules, ramp norms, jurisdiction limits) ship as market-pack rule rows, zero code (Invariant 5 of modular deploy). The two products map to two entitlement flags (…_WORKBENCH, …_PORTFOLIO) on the one module.

Sandbox: demo-able at http://localhost:5179/?target=HorusShell&lens=twin (register a TwinWorkspace target in web/sandbox/registry.ts).


6. Two data halves — native substrate (easy) + external integration (hard)

The clinical and flow data is the easy half: MedOS-native, you own the FHIR substrate, ingestion is basically free. The expensive half — HR, workforce, supply, accounting — lives in systems you don’t own (HRIS, payroll, ERP/GL, rostering, SCM, CMMS, contract mgmt), and across the SEA markets those are often local tools or spreadsheets with no clean API. So a large chunk of the build is connectors + file/manual ingestion + a normalization layer, not FHIR — the same “Odoo for healthcare” integration muscle, pointed at non-clinical data. But you are not starting that muscle from zero: a read-only source-adapter + an EHR-adapter framework, a HOSxP/JHCIS market-pack adapter registry, seed code crosswalks, and a dbt/Airflow transform spine already exist in the repo (§6.2.1) — the twin promotes and points them rather than inventing them.

Tags: [MedOS] native · [ext: System] external connector/file-load · (derived) computed by joining feeds. These are what the Data source connectors + Data readiness & coverage screens (§4·C) expose.

6.1 Native substrate (gray) — already in the read model

Verified against migrations. Two names from the original concept sketch do not exist and must be derived/created:

Concept sketch said Reality Note
vital_observations observations (20260512) + vital_signs_ts / lab_results_ts hypertables (021) + aggregates vitals_hourly/vitals_daily use these for acuity/biomarker series
bed_occupancy_spans does not exist. bed_status_log is an append-only state machine with no duration column (043). P1 ships a bed_occupancy_spans view pairing consecutive rows into (bed, from→to, minutes). er_bed_stay_log already has a GENERATED duration_minutes.

Native calibration tables each agent can already read:

Agent Native tables Migration
Demand & case-mix admission_log_cache, encounter_journey_cache, department_queues/queue_history, gold_* case-mix 052 / 003 / 013
Capacity & flow bed_status_logbed_occupancy_spans, er_bed_stay_log, or_room_runtime_state, procedure_day_queue 043 / 20260526b / 20260514g / 079
Workforce staff_assignments, department_queues.assigned_to, user_tasks (thin — needs HRIS, OQ-7) 20260515
Supply & pharmacy blood_inventory_movements, blood_dispense_events, blood_bag_disposition, stock_balance_cache, OR supply/implant line items 20260325 / 034 / 20260424
Reimbursement gold_fact_claims, gold_monthly_kpi, gold_dim_scheme, or_case_costing, rcm_* 013 / 084 / 010
Quality & safety vital_signs_ts/observations (EWS), cds_rule_evaluations, readmission via admission history — cohort-level only 021 / 20260512 / 20260514b
Compliance policy_gates, facility_billing_rule (designed), market-pack rule rows — enforces constraints, doesn’t generate demand 20260425

6.2 The hard half — external feeds

Source system Carries Why it’s hard
HRIS / credentialing establishment, headcount, skill-mix, licenses, privileges often local; PII-heavy; privileges gate whether a new service line can even be staffed
WFM / rostering shift rosters, on-call usually a separate scheduling tool
T&A worked hours, OT, absence, agency/locum separate clock system
Payroll wage/OT rates, benefit loading, agency premiums the most sensitive PII
ERP / GL service-line P&L, cost centers, revenue net/gross, AR, CapEx canonical money truth; bespoke chart-of-accounts per hospital
SCM / materials mgmt procurement, lead times, prices, par levels vendor-specific
CMMS asset register, maintenance, downtime often offline
contract mgmt payer terms, bundle terms, escalators frequently documents, not data
incident / survey / registries / MoPH / census / market intel safety events, experience, outcomes, epi, benchmarks heterogeneous, external orgs

6.2.1 What’s already built — don’t start the hard half from zero

The recheck (2026-06-04) overturns the “build connectors + a normalization layer from scratch” framing: ~half the integration muscle is already scaffolded in the repo. The twin’s job is to promote and point it, not invent it.

Already in the repo Where What it gives the twin
Read-only SQL source adapter (MySQL/MariaDB/MSSQL/PG/Oracle) — header literally reads READ-ONLY. Never writes to source system. sql.adapter.ts the non-disruptive extraction discipline §6.2 argues for — already coded
EHR adapter framework → FHIR R4 (Epic / TrakCare / TakeCare / Cerner / Allscripts / Meditech / custom); syncMode: 'realtime' | 'polling' | 'bulk' | 'hybrid' + since incremental cursors ehr/adapter-framework.ts, ehrSync.controller.mixin.ts foreign HIS → canonical FHIR, with the realtime/incremental hooks already in the contract
Market-pack adapter registry — HOSxP 3.x + 4.x, JHCIS v2.x, oracle-custom schema-mapping packs (entityPriority, knownQuirks, complianceProfile, TIS-620 / Buddhist-era handling) infrastructure/market-packs/adapters/ the “build the HOSxP adapter once → unlock most Thai public hospitals” wedge is already scaffolded
Crosswalk seeds — ATC→TMT (drug), ICD10→ICD10TM (dx) + ICD10/TMT validators adapters/common/crosswalks/ partial coverage of the §6.4 code crosswalks (drug + ICD), already seeded
Claude-assisted mapping + reconcile / validation / dedup / dlq + country profiles (TH/JP/PH/SE: national-ID validators, Buddhist-era date-converter, TIS-620 encoding-handler) services/migration/ claude/, modules/, country/ schema discovery, field mapping, dbt generation, dedup — the bootstrapping toil §6.4 / OQ-10 worried about
dbt + Airflow + gold medallion (staging→gold star schema for revenue/claims) infrastructure/dbt/, infrastructure/airflow/dags/ an existing transform/semantic substrate to build the canonical layer on, not beside
Connector + sync-stats substratemigration_jobs (source/extracted/loaded/error + RAG green/yellow/red counts, 12-state status), migration_tenants (country_code ISO-2 + market_pack_code + tenant_id RLS), connector-registry/dispatch-log pattern (coding_connectors) 017_migration_tables.sql, 021_migration_multi_tenant.sql, 041_coding_connectors.sql the twin’s Data readiness & coverage / ingestion-run stats — already persisted, already multi-country, already RLS-isolated; the twin reads it, doesn’t rebuild it
Production sync appliance — ever-sync-adapter (sibling @ever repo, not in this tree): Electron edge node, HIS→MOPH-Cloud, live at 15 hospitals; read-only Direct-DB pull (Oracle/MySQL/MSSQL/PG) + per-mode field-map + normalize (BE→CE/datetime) + FHIR R5 transform; sync-stats = log_jobs (atomic-action + parent_run_id + trigger_source + retry/heartbeat/crash + deep-grain children) + immutable sync_events hash-chain (tamper-evident) sibling repo ever-sync-adapter (3× SQLcipher SQLite, Kysely) the production reference for the twin’s sync-stats + integrity model (supersedes the migration_jobs-only shape — OQ-12) and the Thailand instance of the per-country edge-adapter tier (multi-country)

Net-new (genuinely absent — verified): CDC/Debezium, an object-store lake (MinIO), OMOP CDM, Great Expectations, ClickHouse/DuckDB. The clinical-canonical gap is OMOP; everything in the table above is reuse.

The delta, restated: (a) promote the migration/EHR adapters from one-time-ETL + on-demand-sync into a continuous live read-only overlay (the contract already supports realtime/since; what’s missing is running it as a loop); (b) build the semantic layer on the existing dbt star schema + crosswalk seeds, filling OMOP as the clinical-canonical gap. Much smaller than “from zero.”

Sync-stats are already multi-country — reuse, don’t fork. The twin’s connector + ingestion-run observability (its Data readiness & coverage screen, §4·C) maps almost 1:1 onto the migration appliance’s existing substrate: migration_jobs (per-run source/extracted/loaded/error + RAG green/yellow/red counts + 12-state status) + migration_tenants (country_code ISO-2, market_pack_code, tenant_id + RLS via app.tenant_id) + the connector-registry/dispatch-log pattern (coding_connectors). That’s already keyed by market-pack / country / tenant with row-level isolation — the exact two-axis + per-region-residency shape of §9. So twin_connectors/twin_ingestion_runs collapse to a twin-scoped view over it (per facet × connector × market-pack: coverage / freshness / error-rate), not a parallel store — and residency falls out for free (raw per-hospital sync rows stay behind tenant RLS in-region; only de-identified coverage/freshness aggregates roll up, Invariants 5/6). NetworkHub’s node-table / metrics / globe components are the visualization to feed — but it is a mock shell today, not a data source. Open sub-decision: generalize migration_jobs “migration job” → “ingestion run” semantics vs. a same-pattern sibling table (OQ-12).

The production reality — ever-sync-adapter (the @ever portfolio’s deployed sync engine). Health-data sync is not hypothetical in this ecosystem. ever-sync-adapter (sibling repo) is an Electron edge appliance live at 15 hospitals, syncing HIS → MOPH Cloud (Thailand’s national exchange) — read-only Direct-DB pull (Oracle/MySQL/MSSQL/PG) + per-mode field-map + normalize (BE→CE/datetime) + FHIR R5 transform. It is the outbound twin of the medOS migration service’s inbound (foreign-HIS→medOS) adapters — the same connector machinery, opposite direction (OQ-13) — and a production realization of the “promote the adapters to a continuous read-only overlay” delta above. Two consequences for this design:

  1. Adopt its sync-stats shape as canonical (revamps the OQ-12 lean). Its model is richer than migration_jobs: a two-tier log_jobs (atomic-action + parent_run_id + trigger_source + retry/heartbeat/crash recovery + deep-grain per-file/per-batch/per-visit children) plus an immutable sync_events hash-chain (prev_hash/record_hash, tamper-evident, ADR-060). The twin’s Data readiness & coverage model should mirror that — it adds an integrity dimension (hash-chain-verified) the migration_jobs shape lacks.
  2. Multi-country falls straight out. ever-sync-adapter is the Thailand instance of a per-country national-cloud sync adapter (PH→PhilHealth/DOH, JP→MHLW/kaigo, …) — each a local edge node that keeps raw data in-country (SQLcipher SQLite) and pushes only to its national cloud. So the twin consumes a per-(country × connector) sync-health aggregate emitted by each adapter — coverage / freshness / error-rate / hash-chain-verified — and never reaches into a local appliance store. That is exactly the §9 residency model (raw stays local; only de-identified aggregates roll up — Invariants 5/6). Integration is therefore a thin aggregate bridge, not coupling — the adapter already has system:health + system:metrics + the log_jobs aggregates to source it from. The model + the bridge are now specified in ever-sync-adapter-integration.md and shipped as migration 20260604a (in medOS — the adapter repo is untouched).

The seam this exposes — lock it before more readers/ land. Because the EHR adapters already normalize foreign HIS → FHIR R4 → MedOS, the twin’s readers/ (§5) should target a canonical-reader interface that resolves to either MedOS read models (native deploy) or a migration-adapter’s normalized output (foreign-HIS deploy) — never hard-binding the twin to raw MedOS table names. Otherwise every reader silently welds the twin to MedOS and the “sit on HOSxP first, sell the migration later” wedge dies in the data layer. The migration-adapter framework is the natural place to put that boundary. This is the one decision worth resolving before P1’s readers/ get written (OQ-11).

6.3 Three ingestion paths (plan for hospitals with no integratable systems)

Many SEA targets have no connectable HRIS/ERP — it’s local software or Excel. So every external feed needs three paths, surfaced in the Analyze screens:

  1. Standard connector — for the major ERPs/HRIS.
  2. CSV/Excel batch upload.
  3. Manual-entry fallback.

The Data readiness & coverage screen then honestly shows which facets are modelable and which run on stale or hand-entered data — and degrades derived KPIs accordingly (Invariant 7).

6.4 The unglamorous core — master-data crosswalks & a semantic layer

The real work is joins, not feeds. “Doctor workload” and “cost per case” don’t exist as a feed — they exist only as a join between MedOS activity and an HR/GL feed. Required crosswalks:

  • Provider identity — clinical ID ↔ HR ID ↔ billing ID.
  • Org hierarchy — cost center ↔ department ↔ ward ↔ service line.
  • Codes — item, drug, payer-scheme, DRG/TDRG canonical mappings.

Build a canonical data model / semantic layer everything normalizes into (twin_crosswalks, §11). Expect this to be the bulk of the integration effort — and the gate on whether the Workforce and Reimbursement agents can compute anything at all.

Starting point (not zero): the migration service already seeds ATC→TMT (drug) + ICD10→ICD10TM (diagnosis) crosswalks and ships a claude-assisted schemaAnalyzer/fieldMapper (§6.2.1). So drug + ICD code mapping starts warm; provider-identity, cost-center→ward→service-line, payer-scheme, and DRG/TDRG are the genuinely net-new maps — and those are exactly the ones the Workforce + Reimbursement agents gate on.

6.5 Minimize HR/payroll PII

Staff data is sensitive personal data under PDPA/APPI exactly like patient data. The twin doesn’t need it raw — it needs aggregates: FTEs, role-level cost rates, unit rosters, never individual payslips. Ingest at role/unit aggregation; keep individual compensation out of the twin entirely (Invariant 10). Smaller compliance surface, and the right governance posture for the sovereign architecture.

6.6 Per-agent data-source matrix

The artifact you hand an integration team. Type: N=native [MedOS] · E=external connector/file · D=derived (join) · M=mixed · C=config.

Agent Feed Source Type Freq v1
Demand & case-mix Historical encounter volumes MedOS ADT/OPD/ED/IPD N daily
Demand & case-mix Case-mix & coding MedOS DRG/TDRG/ICD-10 N daily
Demand & case-mix Scheduled/booked demand MedOS bookings/appts/waitlist N near-RT
Demand & case-mix Referrals in/out MedOS + ext: referral network M daily
Demand & case-mix Epidemiology & seasonality ext: MoPH/weather/calendars E daily
Demand & case-mix Catchment & demographics ext: census/market E quarterly
Capacity & flow Bed inventory & occupancy MedOS bed_occupancy_spans N near-RT
Capacity & flow LOS & discharge MedOS N daily
Capacity & flow OR/procedure utilization (incl. cath/endo) MedOS N daily
Capacity & flow ED flow (door-to-doc, boarding, LWBS) MedOS N near-RT
Capacity & flow Ancillary TAT (lab/imaging/pharmacy) MedOS N near-RT
Capacity & flow Equipment/room availability MedOS + ext: RTLS/asset M near-RT
Capacity & flow Transfers (internal + inter-facility) MedOS N near-RT
Workforce Establishment & headcount ext: HRIS E monthly
Workforce Credentials & privileges ext: HRIS/credentialing E monthly
Workforce Shift rosters & on-call ext: WFM/rostering E weekly
Workforce Time & attendance (actuals) ext: T&A E weekly
Workforce Labor cost rates (role-aggregated) ext: payroll E monthly
Workforce Provider workload/productivity MedOS ÷ HRIS FTE D daily
Workforce Vacancy/attrition/burnout ext: HRIS + derived M monthly
Workforce Training/competency pipeline ext: HRIS/L&D E quarterly
Supply & pharmacy Inventory & consumption MedOS + ext: materials mgmt M daily
Supply & pharmacy Blood bank inventory & usage MedOS N near-RT
Supply & pharmacy Pharmacy/formulary usage MedOS N daily
Supply & pharmacy Procurement & lead times ext: ERP/SCM E weekly
Supply & pharmacy Consumable cost per case ext: ERP × MedOS D daily
Supply & pharmacy Asset register & maintenance ext: CMMS + finance E monthly
Reimbursement Claims & coding (43-files) MedOS N daily
Reimbursement Payer/scheme mix MedOS + ext: contract mgmt M daily
Reimbursement Fee schedules & DRG weights ext: ref tables (NHSO/TDRG) E on release
Reimbursement Service-line P&L / cost accounting ext: ERP/GL E monthly
Reimbursement Revenue, net vs gross ext: ERP/GL + MedOS billing M monthly
Reimbursement AR & cash ext: finance/AR E weekly
Reimbursement Capital budget & CapEx pipeline ext: finance E quarterly
Reimbursement Contract terms ext: contract mgmt E on event
Quality & safety Outcomes (mortality/cx/readmit/HAI/falls) MedOS + ext: registries M monthly
Quality & safety Deterioration/early warning (NEWS2/PEWS/MEOWS) MedOS vital_signs_ts/observations N near-RT
Quality & safety Incidents & safety events ext: incident-reporting E on event
Quality & safety Patient experience ext: survey tool E monthly
Quality & safety Quality/accreditation indicators (JCI/HA) MedOS + ext: quality system M monthly
Quality & safety Pathway variation (order-set adherence) MedOS D monthly
Compliance Jurisdiction rule set (PDPA/APPI/HIPAA) config/regulatory KB C on release
Compliance Licensing & accreditation status ext: admin E on event
Compliance Mandatory reporting (43-files/LIFE/kaigo) MedOS + ext: regulatory M per cycle
Compliance Consent & data-sharing ledger MedOS (consent ledger planned; no EVFS) M on event
Compliance Output/SaMD classification config config C on release
Cross-cutting Strategic plan & targets internal docs E on event
Cross-cutting Market & competitive intelligence ext E quarterly
Cross-cutting Peer benchmarks ext E quarterly
Cross-cutting Macro/policy & FX (6 countries) ext E daily

6.7 v1 externals (the single capacity decision)

For the step-down unit, everything clinical/flow comes free from MedOS; the must-add externals are exactly three:

  1. HR establishment + rosters — [ext: HRIS / WFM]
  2. Labor cost rates (role-aggregated) — [ext: payroll]
  3. GL service-line cost / P&L — [ext: ERP/GL]

Everything else phases in behind those. v1 ingestion for all three = CSV/Excel + manual fallback — no standard connector required to ship.

Pattern (HORUS house style): every reader is live read → deterministic seed fallback, with an explicit isLive/“estimated” badge. Demos never blank; estimates never masquerade as measured.


7. The engine (purple)

Net-new — the dig confirmed zero OR/simulation/optimization code or libraries in the repo (no glpk, lp-solver, jstat, simple-statistics, mathjs). The one existing forward model — the SEIR engine — establishes the house style we copy.

7.1 The template that already works

// web/packages/intelligence-kit/src/seir-globe/engine/seir-model.ts  (existing, pure)
export function stepSimulation(nodes, edges, params): SeirNode[] {
  // …advance each node one tick; couple nodes via weighted edges;
  // history: [...node.history.slice(-89), newCompartments]   ← rolling window
}

A pure step function + driver loop + parameter panel + forward-fold for curves + live-data seeding (SeirControlPanel.tsx, useSeirSurveillance.ts). The twin generalizes this from a fixed compartmental ODE to a discrete-event engine over composable agents.

7.2 Contracts

// twin/types.ts
type Rng = () => number;                                   // mulberry32(seed) — like shared/seed.ts
type AgentId = 'demand'|'capacity'|'workforce'|'supply'|'reimbursement'|'quality'|'compliance';

interface Distribution { sample(rng: Rng): number }        // empirical | lognormal | exp | poisson
interface SimEvent { t: number; kind: string; entity: string; payload?: unknown }
interface SimState { clock: number; resources: ResourcePool; queues: QueueState; metrics: Metrics }

interface TwinAgent<P = unknown> {                          // ← TEAL. one per subsystem.
  id: AgentId;
  calibrate(readers: Readers, scope: Scope): Promise<P>;    // fit params from read-model history
  seedEvents(p: P, sc: Scenario, horizon: number, rng: Rng): SimEvent[];  // e.g. Poisson arrivals
  step(state: SimState, evt: SimEvent, p: P, rng: Rng): SimEvent[];       // advance + schedule follow-ons
  kpis(state: SimState): Record<string, number>;
}

interface Initiative {
  id: string; title: string; kind: 'capacity'|'staffing'|'supply'|'scheme'|'surge'|'capital'|'service-line';
  scope: Scope;                                            // facility, wards, service line, cohort def
  decisionSpace: DecisionVar[];                            // e.g. beds:int[0..12], ramp:Schedule
  objective: RuleRow;                                      // reuse policy-gate evalCondition/readPath
  constraints: RuleRow[];                                  // budget, nurse:bed ratio, rooms, jurisdiction
  scenarios: Scenario[];                                   // base | surge | worst
  status: 'draft'|'analyzing'|'simulating'|'optimized'|'in_review'|'decided'|'monitoring'|'archived';
}

7.3 The three engine pieces

function runScenario(agents, params, sc, horizon, seed): RunResult          // des.ts — pure + seeded
function monteCarlo(agents, params, sc, horizon, seeds[]): {p10;p50;p90;…}  // montecarlo.ts — N runs
function erlangC(arrivalRate, serviceRate, servers): {waitProb; meanWait}   // queueing.ts — fast bounds

7.4 Objectives & constraints are rule rows, not code

They reuse the shipped, pure predicate evaluators: policy-gate.service.ts evalCondition/readPath (dotted-path conditions over {candidate, result}); RUDS evaluate.ts evalPredicate + scoreDelta (recursive all/any + weighted accumulation ⇒ a natural weighted-objective evaluator); CDS token grammar (cdsEngineDb.ts) for numeric constraint lines.

7.5 The optimizer & where compute runs

function optimize(space, objective, constraints, simulate): RankedCandidate[]
//   grid (small) → simulated annealing (ramps/mixed) → (Bayesian opt later); each candidate scored
//   by monteCarlo() across scenarios; constraint-infeasible candidates pruned.

Output = ranked candidates with P10/P50/P90 risk bands + recommended phased plan + ROI. Compute runs in a Web Worker for P1–P2 (client-side, demo-friendly); the pure kernel ports to a Deno edge fn / microservice unchanged if runs get heavy (OQ-2).

✅ Shipped (2026-06-04): guide/score.ts recommend() sweeps the bed candidates, scores each by net value (revenue − boarding penalty − bed opex), and ranks with P10/P50/P90; engine/optimize.ts holds gridOptimize + simulatedAnnealing for larger spaces. The Guide generators (guide/{readiness,memo,roadmap}.ts) turn the recommendation into a readiness score + investment memo + phased ramp, rendered by GuidePanel.tsx. Runs client-side in the lens today.

7.6 The seven agents (teal)

Agent Models v1?
Demand & case-mix arrivals, case-mix, seasonality
Capacity & flow beds, OR rooms, queues, LOS, boarding/diversion
Workforce staffing, rosters, utilization, overtime
Supply & pharmacy blood/drug/consumable stock, wastage, stock-out risk
Reimbursement revenue/case, denial, payer mix, payment lag, ROI
Quality & safety acuity, complication/readmission, pathway adherence — cohort sizing only, never per-patient output
Compliance regulatory limits, scheme gates, policy constraints the twin must respect

8. The journey (purple) — Capture → Analyze → Model → Guide → Portfolio

The user-facing journey wraps the engine’s internal phases:

Journey (product) Area Engine phase What happens
Capture B Frame author the Initiative: type, question, objective, constraints, scope, experts
Analyze C (baseline) connectors + evidence inbox; baseline snapshot; assumptions register
Model D Simulate agents calibrate(); DES + Monte-Carlo run base/surge/worst ⇒ distributions; expert validation
Guide E Optimize + Decide rank candidates; readiness score, memo, roadmap, decision log; sign-off releases the gate
Portfolio F Monitor post-decision actuals-vs-forecast, drift alerts, cross-facility roll-up

9. Three-tier rollup (the twin’s overlay on the platform’s per-region residency)

Framing correction (recheck 2026-06-04): the platform is two orthogonal axes — country-agnostic modules × per-country market packs (modular-multicountry-deployment.md §1) — not a literal local→country→global topology. The three tiers below are the twin’s own governance rollup, which reuses the platform’s per-region residency + de-identified-aggregate federation (Invariant 6 of that doc). The tiers are the twin’s; the residency they sit on is the platform’s.

Tier Where Holds Sees
Local node (ward/dept) the facility’s in-region Supabase twin_* tables, raw calibration on PHI-bearing read models initiative-level intelligence; PHI stays here
Country country gold layer (twin_gold_initiative_outcomes, the gold_*/fpa_fact_* pattern) de-identified initiative kind, service line, scenario, predicted+realized KPIs — no patient rows cross-facility benchmarking
Global (C-suite/board) Portfolio Command on the fpa-dashboard surface, entitlement-gated portfolio of initiatives across countries ROI-ranked portfolio, de-identified only

Rollup is an additive edge function (mirrors gold-layer-refresh); it emits aggregates upward, never patient data.


10. Human-in-the-loop (the device firewall, made concrete)

Phase Guide reuses shipped infrastructure: acknowledgement_requests + the global AcknowledgementInbox FAB (fan out a sign-off request per role), and policy_gates (a twin_decide gate that blocks optimized → decided until all required sign-offs are present, hard_stop).

Role Validates Keeps the twin honest about…
Medical director acuity / cohort assumptions clinical realism of cohort sizing
Nurse manager staffing ratios, ramp feasibility whether the plan is operable
Revenue-cycle lead reimbursement, scheme rules, ROI inputs the money math
Compliance officer output-shape attestation (population/resource, never patient-directed) that the initiative stayed on the top band

Experts do double duty: keep the twin from hallucinating and their sign-off holds the product on the right side of the device line.


11. Persistence (writes only here)

twin_initiatives        (id, title, kind, scope jsonb, decision_space jsonb, objective jsonb,
                         constraints jsonb, scenarios jsonb, status, created_by, created_at, updated_at)
twin_baselines          (id, initiative_id, facet, snapshot jsonb, captured_at)          -- Analyze
twin_assumptions        (id, initiative_id, key, value, rationale, editable_by, updated_at)-- Analyze
twin_evidence           (id, initiative_id, source, kind, extracted jsonb, ingested_at)   -- Analyze (RAG)
twin_agent_params       (id, initiative_id, agent_id, scope jsonb, params jsonb, fitted_at,
                         source_window tstzrange, is_live bool)                            -- calibration cache
twin_scenario_runs      (id, initiative_id, candidate jsonb, scenario, seed bigint, kpis jsonb,
                         percentiles jsonb, created_at)                                    -- reproducible, append-only
twin_recommendations    (id, initiative_id, rank, candidate jsonb, score, risk_bands jsonb,
                         narrative text, readiness jsonb, created_at)                      -- Guide
twin_signoffs           (id, initiative_id, role, user_id, facet, decision, attestation text,
                         ack_request_id, signed_at)
-- integration layer (the hard half — §6). REUSE the existing migration-appliance sync-stats
-- substrate; do NOT stand up a parallel store (§6.2.1, OQ-12). It is already multi-country + RLS:
--   • ingestion-run stats → `migration_jobs` (market_pack_code + hospital_code + tenant_id; source/
--                           extracted/transformed/loaded/error counts; RAG green/yellow/red; 12-state
--                           status; started/completed) — append-only run history, already per-country
--   • connector registry  → `coding_connectors` / `migration_connector_manifests` pattern
--                           (module/capability/provider/status/hospital_code) — extend, don't fork
--   • multi-country dim    → `migration_tenants` (country_code ISO-2, market_pack_code, tenant_id,
--                           RLS via app.tenant_id) — the residency boundary the twin tiers sit on (§9)
--   • canonical shape     → mirror `ever-sync-adapter`'s `log_jobs` (operational) + `sync_events`
--                           (tamper-evident hash-chain) — the production model (§6.2.1); adds an
--                           integrity dim (hash-chain-verified). For edge-adapter-fed countries the
--                           view's source is the adapter's emitted sync-health aggregate, NOT a local
--                           store (thin bridge, not coupling — OQ-12/OQ-13)
twin_readiness_v        (SHIPPED — migration 20260604a; VIEW over sync_health_aggregate, twin-scoped: per
                         (country, connector, facet) coverage / freshness / error-rate / hash-chain-verified
                         — Data readiness screen. Fed by per-country edge adapters' de-identified
                         SyncHealthSummary. Canonical raw model = sync_run + sync_event (hash-chain).
                         See ever-sync-adapter-integration.md)
twin_crosswalks         (id, domain 'provider'|'cost_center'|'item'|'drug'|'payer_scheme'|'drg',
                         source_system, source_code, canonical_id, mapping jsonb, confidence)  -- the semantic layer
                         -- seed from adapters/common/crosswalks/ (ATC→TMT, ICD10→ICD10TM); §6.2.1
twin_gold_initiative_outcomes  (de-identified; initiative_kind, service_line, scenario,
                                predicted jsonb, realized jsonb, decision, facility_hash, period)  -- country tier

All twin_*: RLS on, service-role writes, frontend reads via realtime. twin_scenario_runs is append-only (reproducibility/audit).


12. Worked example — the first twin: cardiac step-down unit

“Should Vajira open a 10-bed cardiac step-down unit, and how do we phase the staffing ramp?” — exercises every phase and output, and never touches an individual treatment decision (top band, §1.2).

  • Capture. Decision space { open: bool, total_beds: 0..12, ramp: [(month, beds, nurses)] }. Objective = maximize risk-adjusted NPV; secondary = minimize boarding hours. Constraints = capex/opex budget, nurse:bed ratio, physical rooms. Scope = Vajira, cardiac service line.
  • Analyze. Baseline snapshot (current cardiac occupancy, OR utilization, payer mix, readmission rate); assumptions register (ramp norms, discount rate).
  • Model (v1: 3 of 7 agents). Demand & case-mix fits cardiac arrivals + case-mix; Capacity & flow fits LOS/occupancy from bed_status_log-derived spans + post-op inflow from or_case_costing/er_bed_stay_log; Reimbursement fits revenue/case from gold_fact_claims. (Quality & safety is the likely 4th — cohort sizing from EWS.) DES + Monte-Carlo run base / surge (seasonal MI, dengue) / worst (pandemic).
  • Guide. Rank bed counts × ramp schedules by NPV with P10/P50/P90; output a recommended ramp (e.g. 4 → 7 → 10 beds over 6 months) + staffing plan + payback period + investment memo. Med director / nurse manager / RCM lead / compliance sign off; the twin_decide gate releases.
  • Portfolio (post-go-live). Back-test realized occupancy/boarding/revenue vs predicted; drift feeds recalibration.

13. Invariants

  1. Outputs are population / resource-level only — never a patient-directed recommendation (the device firewall). Clinical signals are welcome as inputs.
  2. Read-only on clinical data — writes only to twin_* tables.
  3. Numbers are deterministic — the LLM writes narrative only; it never decides a number or gates a decision.
  4. No decision without sign-offoptimized → decided is gated on the required expert sign-offs.
  5. Data stays local; only aggregates roll up — calibration uses in-region data; de-identified aggregates only cross a tier boundary.
  6. Every run is reproducible — seeded RNG + pinned params + recorded inputs; twin_scenario_runs is append-only.
  7. Fail-soft, never silent — missing live data ⇒ seed fallback + explicit “estimated” badge; never a blank, never a silent zero.
  8. Off by default — feature-flagged + role-gated; disabled ⇒ invisible, zero behavior change.
  9. Back-test before trust — predictions validated against held-out history before recommendations carry decision weight.
  10. HR/finance data enters at role/unit aggregation — never individual compensation or staff PII into the twin (PDPA/APPI surface minimization).
  11. No silent derived KPI — a derived feed (e.g. provider workload = activity ÷ FTE) renders only when its crosswalk + both inputs are present; otherwise it shows “needs data,” never a fabricated number (the Data readiness screen owns this).

14. Checklist roadmap

The realistic v1 = one template (a capacity decision like the step-down unit), Twin Studio with 3 of 7 agents live, and the Guide outputs. All of F (Portfolio Command) and most of G (governance) wait. The tags in §4 mark the v1 screens.

P0 — Foundations & demo shell ✅ SHIPPED 2026-06-04 (demo-able: ?target=Twin / ?target=HorusShell&lens=twin)

  • [x] This design doc (regulatory framing, contracts, 7 agents, screen inventory)
  • [x] Contracts in twin/types.ts: Initiative, TwinAgent, Scenario, RunResult, Distribution, Readers (+ agents/, readers/, seed/)
  • [x] Pure seeded DES kernel (engine/des.ts + montecarlo.ts + distributions.ts + queueing.ts + optimize.ts) + mulberry32 seed
  • [x] HORUS twin lens registered (HORUS_VIEWS entry) + TwinWorkspace shell on seed data (+ live base-case bed sweep)
  • [x] Sandbox target registered in web/sandbox/registry.ts (Twin)
  • [x] Capture (area B): Template gallery (1 live template + 3 framed), Decision brief New Decision wizard + Stakeholder assignment deferred to P1

P1 — Live calibration & base-case simulation (core SHIPPED 2026-06-04; live-DB wiring remains)

  • [x] bed_occupancy_spans view (pairs bed_status_log rows) — migration 20260604b
  • [x] Readers (twin/readers/) live→seed-fallback with isLive badge — Readers boundary + METRIC_SOURCES
  • [ ] External ingestion (v1 trio): HR establishment+rosters [ext: HRIS/WFM], labor cost rates [ext: payroll, role-aggregated], GL service-line P&L [ext: ERP/GL] — via CSV/Excel batch + manual fallback (no connector needed to ship); twin_connectors/twin_ingestion_runs
  • [ ] Master-data crosswalks (twin_crosswalks): provider identity + cost-center→ward→service-line (gates Reimbursement + Workforce)
  • [x] Demand & case-mix, Capacity & flow, Reimbursement agents calibrate() — wired to the Readers boundary; reading seed today, per-metric live MedOS series (RPCs) is the remainder
  • [x] Distribution fitting (engine/distributions.ts) — empirical + lognormal/exp/poisson + method-of-moments fitters
  • [x] Base-scenario DES + Monte-Carlo → P10/P50/P90
  • [ ] Analyze (area C): Data source connectors , Data readiness & coverage , Baseline snapshot , Assumptions register
  • [x] Model (area D): Twin overview (7 agents) — Agent workspaces + Simulation runner UI remain

P2 — Optimization & outputs (optimizer + Guide + scenario compare SHIPPED early, 2026-06-04)

  • [x] Optimization engine (engine/optimize.ts gridOptimize/simulatedAnnealing + guide/score.ts recommend()) — sweep → score (net value = revenue − boarding penalty − bed opex) → rank w/ P10/P50/P90
  • [ ] Objective/constraint rule rows (reuse evalCondition/evalPredicate)
  • [x] Base / surge / worst scenario sweep
  • [x] Model: Scenario comparison Scenario builder + Constraint & policy editor UI remain
  • [x] Guide (area E): Readiness Score , Investment memo , Executive summary (memo §), Execution roadmap guide/{score,readiness,memo,roadmap}.ts + GuidePanel.tsx

P3 — HITL & the device firewall (core SHIPPED 2026-06-04)

  • [x] twin_signoffs table (migration 20260604c) + the DecisionGate sign-off flow — AcknowledgementRequest fan-out (cross-app) remains
  • [x] twin_decide gate logic (decide/evalDecideGate blocks optimized → decided) — the live policy_gates row + hard_stop wiring remains
  • [x] Guide: Recommendation & decision log (the gate’s decision log)
  • [x] Ollama recommender-only narrative (narrative/narrateMemo, fail-soft; mirrors progression.ts)
  • [x] Compliance output-shape attestation step (the compliance-officer sign-off checkbox)
  • [ ] Platform (area G minimum): Users/roles/approvers , Audit & data lineage
  • [ ] Shell (area A): Home/My work , Approvals & notifications inbox

P4 — Full agent roster, packaging, country tier (agents + migration + module SHIPPED 2026-06-04)

  • [x] Workforce, Supply & pharmacy, Quality & safety, Compliance agents (live; quality is cohort-sizing only)
  • [ ] Standard connectors for the major ERPs/HRIS (per design-partner systems — OQ-8); remaining external feeds (SCM/CMMS/contracts/incident/survey/registries)
  • [~] Full crosswalk coverage (twin_crosswalks table shipped in 20260604c; item/drug/payer-scheme/DRG seed maps remain)
  • [x] twin_* migration (20260604c — all tables in §11; connectors/ingestion superseded by the sync-health substrate)
  • [x] infrastructure/modules/hospital-twin/module.json + VITE_HOSPITAL_TWIN_ENABLED + …_WORKBENCH/…_PORTFOLIO flags
  • [ ] Country rollup gold table (twin_gold_initiative_outcomes shipped) + rollup edge fn (de-identified) — edge fn remains
  • [ ] Market-pack rule rows per country (seed-twin-rules.sql); jurisdiction limits in Compliance agent
  • [ ] Remaining Capture/Analyze/Guide screens (Decision register, Evidence inbox, Scenario playbook, Sensitivity)

P5 — Portfolio Command & monitoring (second product; preview SHIPPED 2026-06-04)

  • [ ] Back-test harness (predicted vs realized) + drift metric
  • [ ] Recalibration cron in the cron_jobs registry
  • [x] Portfolio (area F): Portfolio dashboard + actuals-vs-forecast (PortfolioPanel preview) — Risk/opportunity heatmap, Capital allocation, Cross-facility roll-up, Board pack remain
  • [ ] Remaining governance (area G): Org/facility mgmt, Data governance & sovereignty (residency + right-to-deletion; no EVFS — see the §4·G note), Agent & model registry, Integrations, Template/playbook library

15. How to extend

  • Add a domain → one agent module under twin/agents/<facet>/ implementing TwinAgent + one entry in TWIN_AGENTS (mirrors the HORUS_VIEWS contract).
  • Add a country → drop rule rows in infrastructure/market-packs/medos-<country>/seed-twin-rules.sql (currency, scheme rules, ramp norms, jurisdiction limits). Zero TypeScript.
  • Add an initiative kind → a new kind + decision-space template; the engine and agents are kind-agnostic.
  • Add a template → a Template-gallery entry mapping a kind to a pre-filled Initiative skeleton.

16. Open questions

  • OQ-1 — distribution fitting. Hand-roll (empirical CDF + method-of-moments, ~150 LOC, no dep) vs. add simple-statistics. Lean: hand-roll for P1.
  • OQ-2 — compute placement. Web Worker (P1–P2) vs. Deno edge fn / microservice. The pure kernel ports either way. Lean: Worker first.
  • OQ-3 — overlap with seed-only HORUS lenses. Capacity/Queue/FP&A lenses are seed-only today; the twin’s live readers could wire them too. Lean: shared twin/readers/.
  • OQ-4 — back-test windowing. How much held-out history before a twin is “trusted” (Invariant 9)? Per-agent or per-initiative? Defer to P5.
  • OQ-5 — doc index. Add a row to the root CLAUDE.md Key Files table. Offer, don’t auto-edit the shared file.
  • OQ-6 — device-regulatory read. Jurisdiction-specific classification across TH/PH/JP/CN/US/EN (Cures Act CDS carve-out, EU MDR Rule 11, Thai FDA, PMDA). Commission before committing the architecture of any patient-level surface; the population/resource product is unaffected.
  • OQ-7 — Workforce data depth. Staffing/roster data is thinner than beds/claims (staff_assignments + user_tasks). May need a roster source before the Workforce agent is trustworthy.
  • OQ-8 — connector build order. Which ERPs/HRIS/WFM get standard connectors first depends on what the design-partner hospitals actually run. Until known, v1 leans on CSV/Excel + manual; the connector backlog is demand-driven, not speculative.
  • OQ-9 — semantic layer: build vs. buy & where it lives. The canonical model / crosswalks (twin_crosswalks) could live as Supabase tables + RPCs, the existing dbt transform layer (infrastructure/dbt/ — already a staging→gold star schema), or the migration service (which already owns the adapter crosswalk seeds + claude-assisted field mapping). The recheck makes the lean clearer: reuse dbt + the migration service rather than stand up a fourth substrate. Decide before P1’s crosswalk work. See §6.2.1.
  • OQ-10 — crosswalk bootstrapping. Initial provider/cost-center/code mappings are manual or fuzzy-matched. Who authors and signs off the canonical mappings (data-governance role), and what confidence threshold lets a derived KPI render (Invariant 11)? (The cold-start is warmer than it looks: the migration service already ships ATC→TMT + ICD10→ICD10TM seed crosswalks + a claude-assisted fieldMapper/schemaAnalyzer, so drug/ICD mapping starts warm; provider-identity + cost-center + payer-scheme + DRG are the genuinely cold maps§6.2.1.)
  • OQ-11 — canonical-reader contract (the one to lock before P1 readers). The twin’s readers/ should read a canonical boundary that resolves to MedOS read models or a migration-adapter’s normalized FHIR output (§6.2.1), so the twin stays HIS-agnostic — an overlay on HOSxP / foreign HIS, not welded to MedOS table names. The twin design doc currently wires readers directly to specific MedOS tables (§5/§6.1); that’s fine for a MedOS-native demo but must move behind the interface before the “sell the migration later” wedge can survive. The migration-adapter framework is the natural seam. Decide before more readers/ land — this is the schedule-risk decision, more than any single agent or screen. → SHIPPED (P0): the Readers boundary is built — Readers in twin/types.ts + makeSeedReaders/makeMedosReaders/resolveReaders in twin/readers/ (live→seed fallback, isLive badge, reads twin_readiness_v). Remaining = wiring each metric’s live MedOS series (P1).
  • OQ-12 — sync-stats canonical shape + integration (REVAMPED after reviewing ever-sync-adapter). The richer, production-proven model is not migration_jobs — it is the @ever portfolio’s deployed appliance ever-sync-adapter (15 sites): log_jobs (atomic-action + parent_run_id + trigger_source + retry/heartbeat/crash + deep-grain children) + an immutable sync_events hash-chain (tamper-evident) — §6.2.1. Lean: the twin’s sync-stats / data-readiness model mirrors that shape (incl. a hash-chain-verified integrity dim), and integrates via a thin sync-health aggregate bridge — each per-country edge adapter emits coverage/freshness/error-rate/integrity per (market-pack × connector); the twin consumes the aggregate, never the local SQLite. Open: (a) the aggregate-bridge contract + transport (export file vs. signed summary endpoint vs. a roll-up into migration_jobs-style rows); (b) whether EHRSyncResult / migration_jobs (inbound medOS ingestion) converge on the same shape as log_jobs (outbound), or stay separate (→ OQ-13). → First cut SHIPPED: the canonical model (sync_run + sync_event hash-chain) + the de-identified bridge (sync_health_aggregate + twin_readiness_v + the SyncHealthSummary emit contract) landed in migration 20260604a; see ever-sync-adapter-integration.md. Remaining = the emit transport (a).
  • OQ-13 — shared connector kernel (ever-sync-adapter ⇄ medOS migration service). ever-sync-adapter (outbound HIS→MOPH) and services/migration (inbound foreign-HIS→medOS) are two directions of one machinery: read-only DB source adapters (Oracle/MySQL/MSSQL/PG) + per-mode field-map + normalize (BE→CE/datetime) + FHIR transform + operational-logging + integrity. Today they are separate repos with separate implementations. Decision: extract a shared connector kernel (an @ever/* package) vs. have the medOS migration service adopt ever-sync-adapter’s log_jobs/sync_events model vs. leave them parallel. Affects long-term portfolio maintenance, not the twin’s P1. Portfolio-level call — surface to the user; do not auto-decide. The ever-sync-adapter repo has strict TDD/ADR/HOTFIX discipline; any change there is its own scoped effort, not a side-effect of twin work. → Resolved (first arm): the shared model now lives in medOS (sync_run/sync_event, migration 20260604a); the adapter conforms by emitting the de-identified SyncHealthSummary, untouched. The shared-code @ever/* kernel extraction stays deferred. See ever-sync-adapter-integration.md §1.

17. File & symbol references

Thing Path / symbol
Lens registry (extension point) web/packages/intelligence-kit/src/horus/views.tsxHORUS_VIEWS, HorusViewDef
Shell web/packages/intelligence-kit/src/horus/HorusShell.tsx
Sim template (pure step fn) web/packages/intelligence-kit/src/seir-globe/engine/seir-model.tsstepSimulation
Param panel / live-seed templates seir-globe/SeirControlPanel.tsx, seir-globe/hooks/useSeirSurveillance.ts
Recommender-only stance web/packages/intelligence-kit/src/horus/disease/progression.ts
AI tool-loop + guardrail web/src/services/ai/shared/runner-engine.tsrunAgentLoop, SeenMatches
Ollama config VITE_LOCAL_LLM_URL (qwen2.5:14b-instruct), services/llm microservice, PH-demo mistral:7b
Rule evaluators (objectives/constraints) web/src/services/policy-gate.service.ts, web/packages/security-kit/src/ruds/evaluate.ts, web/src/services/cds/cdsEngineDb.ts
Event spine infrastructure/medbase/functions/encounter-orchestrator/index.ts
Module system infrastructure/modules/, resolve-modules.mjs, install-module.sh, moduleEnabled()
Gold layer / FP&A infrastructure/medbase/migrations/013_gold_layer.sql, fpa-data-warehouse.md
Source adapters (read-only) services/migration/.../adapters/sql.adapter.ts, adapters/ehr/adapter-framework.ts
Market-pack adapter registry infrastructure/market-packs/adapters/ (HOSxP 3.x/4.x, JHCIS; common/crosswalks/ ATC→TMT, ICD10→ICD10TM)
Transform / analytics spine infrastructure/dbt/ (staging→gold), infrastructure/airflow/dags/
Production sync appliance (sibling repo) ever-sync-adapterlog_jobs + sync_events hash-chain (canonical sync-stats + integrity model), MophCloudClient/Pc1BundlePushClient (push), Modes A/B/C connectors; .agents/AGENTS.md is its canonical contract
HITL infra acknowledgement_requests + AcknowledgementInbox, policy_gates
Ask Anything