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), PM (Practice Management)
Status: Draft
ADR Reference: ADR-004: CL-FW Event Patterns
Purpose: Define the event contracts for asynchronous communication between CL and PM cores, focusing on clinical documentation → charge capture, referral acceptance → care coordination, and financial program cost → outcomes reporting.
Event Contracts
Event 1: clinical_note_finalized
Summary: When a clinical progress note is signed/finalized in CL-04, PM-07 creates a charge capture suggestion.
| Field | Value |
|---|
| Event Name | progress_note_signed |
| Channel | cl_pm_events |
| Publisher | CL (CL-04: Progress Notes & Session Documentation) |
| Subscribers | PM (PM-07: Charge Capture), PF-10 (Notifications), FW (Audit) |
| Trigger | CL-04 note status transitions to finalized (co-signature complete if required) |
| Frequency | ~50-200/day per organization (based on clinician volume) |
| Latency SLA | < 5 seconds from finalization to PM charge suggestion |
Payload Schema:
interface ProgressNoteSignedEvent {
event: 'progress_note_signed';
publisher: 'CL';
subscriber: ['PM', 'PF', 'FW'];
payload: {
entity_id: string; // cl_progress_notes.id (UUID)
entity_type: 'progress_note';
chart_id: string; // cl_patient_charts.id
encounter_id: string; // pm_encounters.id (FK per ADR-002)
rendering_provider_id: string; // pf_profiles.id of signing clinician
service_date: string; // ISO date (YYYY-MM-DD)
billing_codes: string[]; // CPT codes derived from note type (e.g., ['90837', '90785'])
modifiers?: string[]; // Modifier codes (e.g., ['GT'] for telehealth)
duration_minutes: number; // Actual session duration
note_type: string; // 'individual' | 'group' | 'family' | 'crisis'
requires_cosignature: boolean;
cosigned: boolean;
};
metadata: {
organization_id: string;
site_id?: string;
user_id: string; // Signing clinician
timestamp: string;
correlation_id: string;
event_id: string;
};
}
PM Consumer Action:
- Validate
encounter_id exists in pm_encounters
- Check for duplicate
event_id in processed events (idempotency)
- Create charge suggestion in
pm_charge_suggestions with billing codes, modifiers, duration, and provider
- High-risk billing codes (compliance guard): Before auto-posting to
pm_charges, evaluate CPT/HCPCS in event.payload.billing_codes against a curated high-risk list. Default list (same list MUST be used everywhere this guard is implemented; extend only via org config): 90839, 90840, 90785, H0038, H2035, H0004, T1017, 96127. Org customization: pm_module_settings.auto_post_high_risk_codes_list (text[] or JSON array of codes) replaces the default when set; empty array = use default. Definition — “high-risk”: codes that (a) require strong clinical documentation / medical necessity evidence, (b) draw payer or regulatory audit scrutiny, or (c) commonly require prior authorization — orgs SHOULD align overrides with their compliance policy.
- Add boolean
auto_post_high_risk_codes to pm_module_settings (default false).
- Auto-post to
pm_charges only when: orgSettings.auto_post_enabled === true AND (no high-risk code in payload OR orgSettings.auto_post_high_risk_codes === true).
- Otherwise create or update
pm_charge_suggestions for manual review (PF-10 notify billing as today).
- If auto-post rules pass, create
pm_charges directly
- Send PF-10 notification to billing staff if manual review required
PHI Safety: No patient names, DOB, or narrative text. Only UUIDs and billing metadata.
Event 2: referral_accepted
Summary: When PM accepts an inbound referral, CL-12 creates a care coordination transition record.
| Field | Value |
|---|
| Event Name | pm_referral_accepted |
| Channel | cl_pm_events |
| Publisher | PM (PM-08: Referral Management) |
| Subscribers | CL (CL-12: Care Coordination), PF-10 (Notifications) |
| Trigger | Referral status transitions to accepted in PM |
| Frequency | ~5-20/day per organization |
| Latency SLA | < 30 seconds |
Payload Schema:
interface ReferralAcceptedEvent {
event: 'pm_referral_accepted';
publisher: 'PM';
subscriber: ['CL', 'PF'];
payload: {
entity_id: string; // pm_referrals.id (UUID)
entity_type: 'referral';
patient_id: string; // pm_patients.id
chart_id?: string; // cl_patient_charts.id (if chart already exists)
referring_provider_id: string;
accepting_provider_id: string;
referral_type: string; // 'inbound' | 'outbound' | 'internal'
service_type: string; // e.g., 'behavioral_health', 'substance_use', 'psychiatric'
priority: string; // 'routine' | 'urgent' | 'emergent'
authorization_number?: string;
};
metadata: {
organization_id: string;
site_id?: string;
user_id: string;
timestamp: string;
correlation_id: string;
event_id: string;
};
}
CL Consumer Action:
- Check if
cl_patient_charts exists for patient; if not, flag for chart creation
- Create
cl_care_transitions record with referral context
- Assign care coordinator via CL-12 assignment rules
- Send PF-10 notification to accepting provider and care coordinator
Event 3: clinical_program_cost_allocated
Summary: When FA allocates program costs, CL-10 outcomes can correlate clinical outcomes with financial investment.
| Field | Value |
|---|
| Event Name | fa_program_cost_allocated |
| Channel | fa_events |
| Publisher | FA (FA-35: Program Cost Allocation) |
| Subscribers | CL (CL-10: Outcomes, CL-15: Reporting) |
| Trigger | FA completes monthly/quarterly program cost allocation |
| Frequency | ~1-4/month per organization |
| Latency SLA | < 5 minutes (batch event, not time-critical) |
Payload Schema:
interface ProgramCostAllocatedEvent {
event: 'fa_program_cost_allocated';
publisher: 'FA';
subscriber: ['CL'];
payload: {
entity_id: string; // fa_cost_allocations.id
entity_type: 'cost_allocation';
program_id: string; // Program UUID
program_name: string; // e.g., 'IOP', 'PHP', 'Residential'
period_start: string; // ISO date
period_end: string;
total_cost: number; // In cents (integer) to avoid floating point
cost_per_patient_day?: number;
patient_count: number;
allocation_method: string; // 'direct' | 'step_down' | 'activity_based'
};
metadata: {
organization_id: string;
user_id: string;
timestamp: string;
correlation_id: string;
event_id: string;
};
}
CL Consumer Action:
- Store cost data in CL-10 outcomes context for cost-effectiveness reporting
- Update CL-15 program quality dashboard with cost-per-outcome metrics
- No direct database writes to FA tables (read-only consumption)
Event 4: cl_crisis_episode_resolved
Summary: When a crisis episode is dispositioned with an assigned billing code (CL-13), PM-07 creates a crisis-specific charge suggestion and links it to the episode for audit.
| Field | Value |
|---|
| Event Name | cl_crisis_episode_resolved |
| Channel | cl_pm_events |
| Publisher | CL (CL-13: Crisis Intervention) |
| Subscriber | PM (PM-07: Charge Capture) |
| Trigger | Crisis disposition recorded and billing_code assigned |
| Frequency | Variable (crisis volume) |
| Latency SLA | < 5 seconds to charge suggestion |
Payload Schema:
interface CrisisEpisodeResolvedEvent {
event: 'cl_crisis_episode_resolved';
publisher: 'CL';
subscriber: ['PM'];
payload: {
entity_id: string; // cl_crisis_episodes.id
entity_type: 'crisis_episode';
chart_id: string;
crisis_type: string;
service_date: string; // ISO date YYYY-MM-DD
billing_code: string; // e.g. S9484, S9485, H2011, 90839, 90840
duration_minutes?: number;
rendering_provider_id: string;
encounter_id?: string; // pm_encounters.id when linked
};
metadata: {
organization_id: string;
site_id?: string;
user_id: string;
timestamp: string;
correlation_id: string;
event_id: string; // Idempotency key for PM consumer
};
}
PM Consumer Action:
- Idempotency: same pattern as
progress_note_signed using metadata.event_id in fw_domain_events
- Create crisis-specific
pm_charge_suggestion with billing_code, service_date, rendering_provider_id
- For S9484 / H2011 and similar time-based codes, derive units from
duration_minutes per org/payer rules in pm_module_settings (or PM-07 configuration)
- Link suggestion to
cl_crisis_episodes via entity_id on suggestion metadata / extension table (implementation-specific) for audit
- Apply the same high-risk auto-post guard as Event 1 when codes overlap the high-risk list
Retry / idempotency: Treat event_id as the dedupe key; duplicates must not create a second suggestion.
Idempotency
All event handlers implement the following idempotency pattern:
async function handleEvent(event: DomainEvent): Promise<void> {
// 1. Check for duplicate event_id — maybeSingle() so 0 rows returns null (first-time OK)
const { data: existing } = await supabase
.from('fw_domain_events')
.select('id')
.eq('event_id', event.metadata.event_id)
.maybeSingle();
if (existing) {
logger.info(`Duplicate event ${event.metadata.event_id} — skipping`);
return;
}
// 2. Process event
await processEvent(event);
// 3. Record processed event (store full payload for audit / replay)
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>,
});
}
Dead-Letter Queue
Failed events follow the DLQ pattern from ADR-004:
- On failure: write to
fw_domain_events with status = 'failed', error message, retry count
- Cron edge function retries failed events (exponential backoff: 1s, 2s, 4s; max 3 retries)
- After final failure: PF-10 notification to system admin
- No retry on 4xx errors (except 429 rate limit)
E2E Test Plan
| Test Scenario | Steps | Expected Outcome |
|---|
| Note finalization → charge created | 1. Create encounter in PM; 2. Create progress note in CL-04; 3. Finalize note (sign) | PM charge suggestion exists with correct billing_codes, encounter_id, duration_minutes, and modifiers (when present in event payload) |
| Referral acceptance → care transition | 1. Create referral in PM; 2. Accept referral | CL-12 care transition record created; care coordinator assigned |
| Cost allocation → outcomes context | 1. Run FA cost allocation for program | CL-10 cost-per-outcome metrics updated |
| Duplicate event → no double processing | 1. Publish progress_note_signed twice with identical metadata.event_id | Exactly one charge suggestion; handler logs a message equivalent to “Duplicate event — skipping” (or Duplicate event ${event_id} — skipping); no uncaught exception |
| Failed event → DLQ | 1. Publish progress_note_signed with invalid encounter_id until max retries exhausted | Row in fw_domain_events with status = 'failed', retry_count = 3, error_message non-null/non-empty, processed_at IS NULL after final failure; PF-10 admin notification fired |