Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.encoreos.io/llms.txt

Use this file to discover all available pages before exploring further.

Contract Version: 1.0 Created: 2026-03-24 Owning Cores: CL (Clinical & EHR), FW (Forms & Workflow) Status: Draft ADR Reference: ADR-004: CL-FW Event Patterns
Purpose: Define the event contracts for CL clinical events consumed by FW-16 (Event-Based Workflow Triggers) for automated task creation, notifications, and escalation workflows.

Automated event publishers (system actor)

Cron jobs and other non-human publishers must set metadata.user_id to a well-known platform system profile so audit trails stay consistent (no NULL actor, no arbitrary strings).
ItemValue
Reserved pf_profiles.id00000000-0000-0000-0000-000000000001
Usernamesystem (stored in pf_profiles.custom_fields.username and auth raw_user_meta_data)
Seedingsupabase/migrations/20260325141500_pf_system_automation_profile.sql — inserts matching auth.users + auth.identities (non-login password) and pf_profiles with email = system@encore.local, full_name = System Automation, first_name / last_name = System / Automation, custom_fields = {"platform_system_actor": true, "username": "system"}.
ConsumersEdge functions such as cl-assessment-expiry-checker, cl-moud-monitoring-checker, cl-care-gap-rules, care-gap closure (closed_by), pf_notifications and any publisher that sets metadata.user_id on domain events — all MUST use this UUID.
All automated events MUST use this UUID for metadata.user_id unless a spec explicitly documents a different service principal.

Event Catalog

Event 1: cl_assessment_expired

FieldValue
Channelcl_events
PublisherCL (cron edge function: cl-assessment-expiry-checker)
SubscribersFW (task creation)
TriggerAssessment next_due_date has passed without a new assessment being completed
FW ActionCreate reassessment reminder task assigned to treating clinician
Payload:
{
  event: 'cl_assessment_expired';
  publisher: 'CL';
  subscriber: ['FW'];
  payload: {
    entity_id: string;          // cl_assessments.id
    entity_type: 'assessment';
    chart_id: string;
    assessment_type: string;    // 'comprehensive', 'calocus', 'asam', 'phq9', etc.
    original_due_date: string;  // ISO date
    days_overdue: number;
    severity: 'warning';
    action_required: 'Schedule and complete reassessment';
    assigned_clinician_id: string;
  };
  metadata: {
    organization_id: string;
    user_id: string; // Use platform system profile UUID for cron (see "Automated event publishers" above)
    timestamp: string;
    correlation_id: string;
    event_id: string;
  };
}
FW Automation Rule:
  • Template: reassessment_reminder
  • Task: “Complete overdue assessment”
  • Assignee: assigned_clinician_id
  • Priority: high if days_overdue > 7, medium otherwise
  • Due date: 3 business days from task creation

Event 2: cl_moud_monitoring_overdue

FieldValue
Channelcl_events
PublisherCL (cron edge function: cl-moud-monitoring-checker)
SubscribersFW (care team alert)
TriggerMOUD monitoring checkpoint overdue (e.g., UDS not completed within monitoring window)
FW ActionAlert care team; create follow-up task
Payload:
{
  event: 'cl_moud_monitoring_overdue';
  publisher: 'CL';
  subscriber: ['FW'];
  payload: {
    entity_id: string;          // cl_moud_enrollments.id
    entity_type: 'moud_enrollment';
    chart_id: string;
    monitoring_type: string;    // 'uds', 'vital_signs', 'lab_work', 'clinical_review'
    expected_date: string;
    days_overdue: number;
    medication: string;         // 'buprenorphine', 'naltrexone', 'methadone'
    severity: 'warning';
    action_required: 'Complete overdue MOUD monitoring';
    assigned_clinician_id: string;
    care_team_ids: string[];
  };
  metadata: { organization_id, user_id, timestamp, correlation_id, event_id };
}
FW Automation Rule:
  • Template: moud_care_team_alert
  • Alert: PF-10 notification to all care_team_ids
  • Task: “Complete for MOUD patient” assigned to assigned_clinician_id
  • Priority: high
  • Escalation: If not resolved in 48 hours, escalate to supervisor (Event 3)

Event 3: cl_moud_adherence_risk

FieldValue
Channelcl_events
PublisherCL (triggered by adherence scoring logic in CL-21)
SubscribersFW (escalation workflow)
TriggerPatient’s MOUD adherence score drops below threshold (e.g., 2+ missed UDS, missed appointments)
FW ActionEscalate to clinical supervisor; create urgent review task
Payload:
{
  event: 'cl_moud_adherence_risk';
  publisher: 'CL';
  subscriber: ['FW'];
  payload: {
    entity_id: string;          // cl_moud_enrollments.id
    entity_type: 'moud_enrollment';
    chart_id: string;
    risk_factors: string[];     // ['missed_uds', 'missed_appointment', 'dose_reduction']
    adherence_score: number;    // 0-100 scale
    threshold: number;          // Score that triggered the alert
    severity: 'critical';
    action_required: 'Urgent clinical review — MOUD adherence risk';
    assigned_clinician_id: string;
    supervisor_id: string;
  };
  metadata: { organization_id, user_id, timestamp, correlation_id, event_id };
}
FW Automation Rule:
  • Template: moud_escalation
  • Alert: PF-10 critical notification to supervisor_id
  • Task: “Urgent MOUD adherence review” assigned to supervisor_id
  • Priority: critical
  • Due date: Same business day

Event 4: cl_care_gap_identified

FieldValue
Channelcl_events
PublisherCL (cron edge function: cl-care-gap-rules)
SubscribersFW (task creation)
TriggerCare gap rule identifies an open gap for a patient (e.g., missed HEDIS measure)
FW ActionCreate task for care manager to close gap
Payload:
{
  event: 'cl_care_gap_identified';
  publisher: 'CL';
  subscriber: ['FW'];
  payload: {
    entity_id: string;          // cl_care_gaps.id
    entity_type: 'care_gap';
    chart_id: string;
    gap_type: string;           // 'hedis_amm', 'hedis_fuh', 'hedis_fum', 'hedis_iet', 'preventive_screening'
    measure_id: string;         // HEDIS measure identifier
    gap_description: string;    // Human-readable (e.g., "AMM: Antidepressant medication management — acute phase")
    due_date?: string;          // Measurement year end or clinical due date
    severity: 'info';
    action_required: 'Review and close care gap';
    care_manager_id?: string;   // If attributed, assigned care manager
  };
  metadata: { organization_id, user_id, timestamp, correlation_id, event_id };
}
FW Automation Rule:
  • Template: care_gap_task
  • Task: “Close care gap: ” assigned to care_manager_id or unassigned pool
  • Priority: medium (elevated to high if due_date < 30 days)
  • Due date: 14 business days or due_date, whichever is earlier

Event 5: cl_crisis_episode_opened

FieldValue
Channelcl_events
PublisherCL (CL-13: Crisis Intervention)
SubscribersFW (supervisor notification), GR (incident awareness)
TriggerNew crisis episode created with risk_level = ‘high’ or ‘imminent’
FW ActionImmediate notification to clinical supervisor and care team
Payload:
{
  event: 'cl_crisis_episode_opened';
  publisher: 'CL';
  subscriber: ['FW', 'GR'];
  payload: {
    entity_id: string;          // cl_crisis_episodes.id
    entity_type: 'crisis_episode';
    chart_id: string;
    crisis_type: string;        // 'walk_in', 'mobile', 'observation_23hr', 'phone', 'telehealth'
    risk_level: string;         // 'high' or 'imminent' (only these trigger FW)
    severity: 'critical';
    action_required: 'Crisis episode opened — immediate supervisor review';
    assigned_clinician_id: string;
    supervisor_id?: string;
  };
  metadata: { organization_id, site_id, user_id, timestamp, correlation_id, event_id };
}
FW Automation Rule:
  • Template: crisis_supervisor_notification
  • Alert: PF-10 critical notification to supervisor_id and site clinical lead
  • No task created (crisis is actively managed by assigned clinician)
  • If crisis_type = 'observation_23hr', also create 6 interval assessment tasks (4-hour cadence)

Event 6: cl_restraint_event_initiated

FieldValue
Channelcl_events
PublisherCL (CL-13: Crisis Intervention — restraint/seclusion workflow)
SubscribersGR (incident auto-create), FW (automation template restraint_incident_creation)
TriggerRestraint or seclusion physically initiated (initiation_time recorded per 42 CFR 482.13)
FW ActionCreate incident-tracking workflow + PF-10 supervisor alert (critical)
GR ActionCreate or update GR-09 incident record; map payload entity_id to cl_restraint_seclusion_events.id
Payload:
{
  event: 'cl_restraint_event_initiated';
  publisher: 'CL';
  subscriber: ['GR', 'FW'];
  payload: {
    entity_id: string;              // cl_restraint_seclusion_events.id
    entity_type: 'restraint_seclusion_event';
    chart_id: string;
    event_type: string;             // e.g. 'restraint' | 'seclusion' | 'both'
    patient_age_group: string;    // e.g. 'adult' | 'adolescent' | 'child'
    max_duration_hours: number;    // Age-based regulatory limit
    initiation_time: string;       // ISO 8601
    severity: 'critical';
    action_required: string;        // e.g. 'Restraint initiated — verify monitoring and face-to-face compliance'
    crisis_episode_id?: string;
    supervisor_id?: string;
  };
  metadata: {
    organization_id: string;
    site_id?: string;
    user_id: string;
    timestamp: string;
    correlation_id: string;
    event_id: string;
  };
}
FW automation: Template name restraint_incident_creation — drives GR incident stub + PF-10 supervisor notification per org rules. GR / PF-10: GR-09 consumes the event for regulatory incident creation; PF-10 delivers the critical alert to supervisor_id (or site escalation path). Canonical event name: cl_restraint_event_initiated (physical start / initiation_time). Do not alias to cl_restraint_event_documented without updating EVENT_CONTRACTS.md, GR-09, and CL-GR-CLINICAL-INCIDENT-INTEGRATION.md — those documents MUST stay aligned with this contract.

FW-16 consumer spec: restraint_incident_creation automation template

FW-16 MUST register a workflow template named restraint_incident_creation (referenced by fw_workflow_events.auto_task_template for cl_restraint_event_initiated). Implementation MUST satisfy GR-09 incident auto-create and compliance testing.
AreaRequirement
Payload validationValidate required fields: entity_id, entity_type === 'restraint_seclusion_event', chart_id, initiation_time (ISO 8601), organization_id from metadata. Reject or DLQ events missing any required field; do not create tasks on invalid payloads.
PHI / PII strippingFollow ADR-004: payloads use UUID identifiers only for patients/charts; no free-text clinical content in FW task titles or notification bodies beyond org-approved templates. Strip or hash any unexpected string fields before persisting to FW tables or PF-10.
Audit logging (42 CFR 482.13)Log every automation execution to PF-04 pf_audit_logs (or append-only clinical audit per org policy) with: organization_id, user_id = reserved system profile UUID, action = e.g. fw.restraint_incident_automation.executed, timestamp, new_values JSONB containing event_id, correlation_id, entity_id, template = restraint_incident_creation, and outcome (created_task_ids, gr_incident_id if returned). Retention MUST meet restraint/seclusion record requirements.
GR-09 integrationTemplate nodes SHALL create or idempotently update the GR-09 regulatory incident record linked to cl_restraint_seclusion_events.id = entity_id, then route PF-10 critical notification to supervisor_id or org escalation path.
Seed note: The template restraint_incident_creation MUST be defined in the workflow templates seed (or equivalent FW-16 registry) with nodes implementing the table above: (1) validate payload, (2) call GR-09 incident upsert, (3) enqueue PF-10 supervisor alert. Until the template exists in seed, deployments SHOULD treat auto_task_template as a documentation contract and implement the template before enabling production automation for this event.

Event 7: cl_restraint_event_terminated

FieldValue
Channelcl_events
PublisherCL (CL-13)
SubscribersGR (GR-09 auto-close / escalation), FW (FW-16 debriefing task — e.g. template keyed to post-restraint debrief)
TriggerRestraint/seclusion ended (termination_time recorded)
FW ActionOptional debriefing task trigger (FW-16); link to cl_restraint_seclusion_events
GR ActionGR-09 may auto-close draft incident or escalate if duration_exceeded or regulatory flags set
Payload (minimum):
{
  event: 'cl_restraint_event_terminated';
  publisher: 'CL';
  subscriber: ['GR', 'FW'];
  payload: {
    restraint_id: string;                 // same as entity_id / cl_restraint_seclusion_events.id
    patient_id: string;                   // chart-linked patient UUID (identifier only)
    initiation_time: string;
    termination_time: string;
    termination_reason: string;
    face_to_face_required_flag: boolean;  // true if post-termination face-to-face still required
    scheduled_face_to_face_deadline?: string; // ISO 8601 — when F2F must be completed if deferred
    duration_exceeded?: boolean;
    crisis_episode_id?: string;
  };
  metadata: { organization_id, site_id?, user_id, timestamp, correlation_id, event_id };
}

Event 8: cl_care_gap_closed

FieldValue
Channelcl_events
PublisherCL (CL-35 Population Health — auto-close cron or manual closure)
SubscribersFW (optional follow-up tasks), analytics
TriggerCare gap transitions to closed (closed_at set)
FW ActionOptional workflow (e.g. close related tasks, notify care manager)
Payload:
{
  event: 'cl_care_gap_closed';
  publisher: 'CL';
  subscriber: ['FW'];
  payload: {
    entity_id: string;          // cl_care_gaps.id
    entity_type: 'care_gap';
    chart_id: string;
    gap_type: string;
    closure_type: string;       // e.g. service_completed, clinician_override (see CL-35)
    closed_by: string;          // pf_profiles.id (use platform system UUID for cron)
    closed_at: string;          // ISO 8601
  };
  metadata: { organization_id, user_id, timestamp, correlation_id, event_id };
}

Event Registration Seed Data

All CL→FW events must be registered in fw_workflow_events for FW-16 to consume them:
-- Seed: CL-FW event automation registration
-- Run after FW-16 migration creates fw_workflow_events table

INSERT INTO fw_workflow_events (event_name, source_core, channel, description, auto_task_template, is_active)
VALUES
  ('cl_assessment_expired', 'CL', 'cl_events',
   'Assessment overdue — create reassessment task for clinician',
   'reassessment_reminder', true),

  ('cl_moud_monitoring_overdue', 'CL', 'cl_events',
   'MOUD monitoring overdue — alert care team and create follow-up',
   'moud_care_team_alert', true),

  ('cl_moud_adherence_risk', 'CL', 'cl_events',
   'MOUD adherence risk — escalate to supervisor with urgent review',
   'moud_escalation', true),

  ('cl_care_gap_identified', 'CL', 'cl_events',
   'Care gap identified — create task for care manager',
   'care_gap_task', true),

  ('cl_crisis_episode_opened', 'CL', 'cl_events',
   'Crisis episode opened — notify supervisor immediately',
   'crisis_supervisor_notification', true),

  ('cl_restraint_event_initiated', 'CL', 'cl_events',
   'Restraint initiated — notify GR and create incident tracking',
   'restraint_incident_creation', true),

  ('cl_restraint_event_terminated', 'CL', 'cl_events',
   'Restraint ended — update timers and close related FW tasks',
   NULL, true),

  ('cl_care_gap_closed', 'CL', 'cl_events',
   'Care gap closed — optional FW cleanup / analytics',
   NULL, true)
ON CONFLICT (event_name) DO UPDATE SET
  description = EXCLUDED.description,
  auto_task_template = EXCLUDED.auto_task_template,
  is_active = EXCLUDED.is_active;

Drift Prevention

To ensure event contracts don’t diverge between CL publishers and FW consumers: Add to CI pipeline: validate that every pg_notify('cl_events', ...) call in CL code matches a registered event in fw_workflow_events seed data.
# scripts/check-event-drift.sh
# Extract event names from pg_notify calls in CL migrations/functions
# Compare against fw_workflow_events seed INSERT statements
# Fail if any unregistered events found

Manual Audit (Current)

  • When adding a new CL event, update this contract and the seed SQL
  • Update EVENT_CONTRACTS.md with the new event entry
  • PR template includes checklist item: “Event contract updated if new events added”

Idempotency & DLQ

Summary aligns with ADR-004. Canonical handler pattern (copy-paste for FW/edge consumers):
// import { sanitizeErrorMessage } from '@/shared/lib/error-utils';

async function handleEvent(event: DomainEvent): Promise<void> {
  // 1. Idempotency: 0 rows must NOT throw — use maybeSingle(), not single()
  const { data: existing } = await supabase
    .from('fw_domain_events')
    .select('id')
    .eq('event_id', event.metadata.event_id)
    .maybeSingle();

  if (existing) {
    // Structured log in production — do not treat as error
    logger.info(`Duplicate event ${event.metadata.event_id} — skipping`);
    return;
  }

  try {
    await processEvent(event);
    await supabase.from('fw_domain_events').insert({
      event_id: event.metadata.event_id,
      event_name: event.event,
      correlation_id: event.metadata.correlation_id,
      status: 'processed',
      processed_at: new Date().toISOString(),
      event_payload: event as unknown as Record<string, unknown>, // full payload for audit / replay
    });
  } catch (err) {
    await supabase.from('fw_domain_events').insert({
      event_id: event.metadata.event_id,
      event_name: event.event,
      correlation_id: event.metadata.correlation_id,
      status: 'failed',
      retry_count: 0,
      error_message: sanitizeErrorMessage(err),
      processed_at: null,
      event_payload: event as unknown as Record<string, unknown>,
    });
    throw err;
  }
}
DLQ / retry:
  • On failure, row in fw_domain_events uses status = 'failed', increment retry_count per retry attempt, populate error_message.
  • Cron retries with exponential backoff (e.g. 1s, 2s, 4s) up to 3 additional attempts; after max retries: retry_count = 3, processed_at remains NULL, PF-10 notifies system admin.
  • No retry on 4xx except 429 (rate limit).