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.

Feature ID: CL-07
Status: ✅ Implemented
Last Verified: 2026-02-19
Spec Reference: CL-07-suicide-risk-screening-safety-planning.md
Last Updated: 2026-02-19 (PF-33 pattern decision formally documented; status updated to Implemented)

Overview

CL-07 defines suicide risk screening (C-SSRS, PHQ-9 Item 9, ASQ, SAFE-T), risk classification (low/moderate/high/imminent), Stanley-Brown safety planning (6 components), and environmental risk assessments for inpatient/residential settings. It depends on PF-01, PF-02, PF-10, PF-33, and CL-01. Cross-core: PF-10 (alerts), PF-33/SignatureCanvas (signatures), CL-01 (risk flag on chart), CL-13 (crisis intervention linkage).

Quick Reference

ItemValue
Core tablescl_risk_screenings, cl_safety_plans, cl_environmental_assessments (E-1)
RLS helpercl_has_org_access(organization_id, auth.uid()) — two-argument form (Errata E-4)
Alert triggerHigh/imminent risk → PF-10 real-time notification + cl_risk_screening_high_risk domain event
SignaturesPattern A: SignatureCanvas → base64 stored in patient_signature_data/clinician_signature_data (Errata E-6)
Risk flagcl_patient_charts.current_risk_level + risk_last_screened_at updated by DB trigger (Errata E-7)

Integration Points (from Spec)

DependencyPatternPurpose
PF-10 (Notifications)Domain Event → PF-10 INSERTReal-time alert to treatment team on high/imminent risk
PF-33/SignatureCanvasDirect (Pattern A)Patient and clinician safety plan signatures via SignatureCanvas
CL-01 (Patient Chart)Internal + DB triggerRisk level displayed as persistent flag on chart (current_risk_level column)
CL-13 (Crisis Intervention)Internal (bidirectional)Links to risk screening; crisis docs reference screening; CL-07 should be implemented before CL-13

Event / Notification Contracts

cl_risk_screening_high_risk (Errata E-5)

Trigger: Screening saved with risk_level IN ('high', 'imminent')
Registered in: fw_workflow_events (Task T1.6)
KnownEventName: 'cl_risk_screening_high_risk'
Payload:
{
  organization_id: string;   // required — tenant isolation
  chart_id: string;          // required — patient chart reference (no PHI)
  screening_id: string;      // required — cl_risk_screenings.id
  risk_level: 'high' | 'imminent';
  screened_at: string;       // ISO 8601 timestamp
  next_screening_due: string; // ISO 8601 — always set on save (not optional; see below)
}
// ⚠ next_screening_due is ALWAYS set when screening is saved — never optional in actual
// insert. Integration doc previously listed it as optional; corrected here (Errata E-5).
PF-10 Notification INSERT pattern:
// Insert into pf_notifications after screening save
await supabase.from('pf_notifications').insert({
  organization_id: orgId,
  user_id: careTeamMemberId,     // repeat for each care team member
  type: 'alert',                  // pf_notifications.type enum value
  channel: 'in_app',             // primary; add 'email' row for email channel if needed
  title: `${riskLevel.toUpperCase()} Suicide Risk Alert`,
  message: `Patient chart ${chartId} screened at ${riskLevel} risk. Next screening due ${nextScreeningDue}.`,
  priority: 'high',
  metadata: { chart_id: chartId, screening_id: screeningId, risk_level: riskLevel },
});

cl_safety_plan_signed (Errata E-5)

Trigger: Patient or clinician signature captured on safety plan
Registered in: fw_workflow_events (Task T1.6)
KnownEventName: 'cl_safety_plan_signed'
Payload:
{
  organization_id: string;
  chart_id: string;
  safety_plan_id: string;    // cl_safety_plans.id
  signer_type: 'patient' | 'clinician';
  signed_at: string;         // ISO 8601
}

PF-33 / SignatureCanvas — Pattern Decision & Implementation (Errata E-6)

Decision

Pattern A (SignatureCanvas) is used for CL-07 v1 safety plan signatures. Pattern B (pf_signature_requests formal workflow) is deferred to v2. Decision Date: 2026-02-19 (Errata E-6, Pre-Implementation Review FINDING-03)

Rationale

FactorAssessment
Clinical workflowSafety plan signing is in-person and synchronous — clinician and patient sit together at the point of care and both sign before the session ends. Pattern B’s dispatch-and-notify lifecycle (signer receives a link and signs asynchronously) does not match this workflow.
Pattern B overheadPattern B requires pf_signature_requests lifecycle management, signing_token generation, expiry cron jobs, and PF-10 notification dispatch — none of which are needed when both parties are physically present.
PrecedentPattern A is consistent with CL-02 (assessment sign) and CL-04 (progress note co-sign). It is the canonical in-session signature pattern for the CL core.
JCAHO complianceFor v1, *_signed_at timestamp + base64 image constitutes a sufficient record of consent for Joint Commission NPSG.15.01.01.
PHI handlingSignature image data is PHI — stored in JSONB on cl_safety_plans, never transmitted in event payloads or logs.

Pattern B Upgrade Path (v2)

If a future requirement emerges for remote/asynchronous patient signing (e.g., telehealth-delivered safety planning), upgrade to Pattern B:
  1. Add patient_signature_request_id UUID REFERENCES pf_signature_requests(id) and clinician_signature_request_id UUID REFERENCES pf_signature_requests(id) to cl_safety_plans.
  2. Retain patient_signature_data / clinician_signature_data JSONB columns for backward compatibility with v1 records.
  3. On completion, pf_signature_requests.signature_id links to the pf_signatures row; read back to display signed state.

Pattern A Implementation

  1. Render <SignatureCanvas> from @/platform/signatures within the safety plan form.
  2. On completion, capture base64 image output (dataUrl).
  3. Update cl_safety_plans row via useSignSafetyPlan:
    • Patient: patient_signature_data = { image: dataUrl, captured_at: ISO8601 }, patient_signed_at = now()
    • Clinician: clinician_signature_data = { image: dataUrl, captured_at: ISO8601 }, clinician_signed_at = now()
  4. Publish cl_safety_plan_signed domain event (payload: IDs only — no image data).
No pf_signature_requests rows are created in Pattern A.

Storage Schema (on cl_safety_plans)

ColumnTypeDescription
patient_signature_dataJSONB{ image: base64PNG, captured_at: ISO8601 } — NULL until signed
patient_signed_atTIMESTAMPTZTimestamp when patient signature was captured
clinician_signature_dataJSONB{ image: base64PNG, captured_at: ISO8601 } — NULL until signed
clinician_signed_atTIMESTAMPTZTimestamp when clinician signature was captured

CL-01 Risk Flag (Errata E-7)

The risk flag displayed on the patient chart is stored as a denormalized column on cl_patient_charts for performance (avoids N+1 on chart load). Schema addition (CL-07 Phase 1 migration):
ALTER TABLE cl_patient_charts
  ADD COLUMN IF NOT EXISTS current_risk_level TEXT
    CHECK (current_risk_level IN ('low', 'moderate', 'high', 'imminent')),
  ADD COLUMN IF NOT EXISTS risk_last_screened_at TIMESTAMPTZ;
Trigger: After INSERT on cl_risk_screenings, update parent chart:
UPDATE cl_patient_charts
SET current_risk_level = NEW.risk_level,
    risk_last_screened_at = NEW.screened_at
WHERE id = NEW.chart_id;
Cache invalidation: After screening insert, invalidate ['cl', 'patient_charts', chartId] query key.

CL-13 Bidirectional Relationship

CL-13 (Crisis Intervention) and CL-07 are bidirectionally coupled at the operational level:
  • CL-07 → CL-13: Risk screenings may trigger crisis intervention documentation (CL-13). Crisis episodes reference the triggering screening.
  • CL-13 → CL-07: cl_crisis_episodes.safety_plan_updated BOOLEAN references the existence of a CL-07 safety plan record for the same chart (soft relationship, no FK). CL-13’s scope explicitly includes “Safety plan creation/update during crisis (CL-07).”
Sequencing: CL-07 migrations MUST run before CL-13 migrations. Implement CL-07 before CL-13.

Security and RLS

  • Tables: cl_risk_screenings, cl_safety_plans, cl_environmental_assessments (per Errata E-1).
  • RLS: All three tables use cl_has_org_access(organization_id, auth.uid()) (two-argument form, Errata E-4) for SELECT/INSERT/UPDATE/DELETE. WITH CHECK required on UPDATE. No direct queries to RLS-protected tables in policies.
  • Permissions: cl.risk_screening.view/create, cl.safety_plan.view/create/sign (sign = approve category), cl.environmental_assessment.view/create. Enforce at application layer via useHasPermission.
  • PHI: Signature image data (*_signature_data) is PHI — never log or expose in event payloads. Event payloads contain IDs only.

Common Mistakes

MistakeFix
Using one-arg cl_has_org_access(organization_id) in RLSUse two-arg cl_has_org_access(organization_id, auth.uid()) (Errata E-4)
Re-creating cl_has_org_access in CL-07 migrationDO NOT re-create; function already exists from CL-01 migration
cl.safety_plan.sign permission category = 'create'MUST be 'approve' (signing is an approval action)
Auto-scoring SAFE-T instrumentSAFE-T has no numeric score; score column is NULL; risk_level entered by clinician (FR-1.6)
Using formal pf_signature_requests for safety planUse Pattern A (SignatureCanvas) only for v1 (Errata E-6)
Showing risk level via N+1 join on chart loadRead from cl_patient_charts.current_risk_level (denormalized, Errata E-7)
next_screening_due treated as optional in notification payloadAlways set on save; include in event payload (Errata E-5)
Starting CL-13 migration before CL-07CL-07 migrations MUST precede CL-13