> ## 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.

# Suicide Risk Screening & Safety Planning — Integration

> 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-…

**Feature ID:** CL-07\
**Status:** ✅ Implemented\
**Last Verified:** 2026-02-19\
**Spec Reference:** [CL-07-suicide-risk-screening-safety-planning.md](../../../specs/cl/specs/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

| Item          | Value                                                                                                            |
| ------------- | ---------------------------------------------------------------------------------------------------------------- |
| Core tables   | `cl_risk_screenings`, `cl_safety_plans`, `cl_environmental_assessments` (E-1)                                    |
| RLS helper    | `cl_has_org_access(organization_id, auth.uid())` — two-argument form (Errata E-4)                                |
| Alert trigger | High/imminent risk → PF-10 real-time notification + `cl_risk_screening_high_risk` domain event                   |
| Signatures    | Pattern A: `SignatureCanvas` → base64 stored in `patient_signature_data`/`clinician_signature_data` (Errata E-6) |
| Risk flag     | `cl_patient_charts.current_risk_level` + `risk_last_screened_at` updated by DB trigger (Errata E-7)              |

***

## Integration Points (from Spec)

| Dependency                  | Pattern                     | Purpose                                                                                            |
| --------------------------- | --------------------------- | -------------------------------------------------------------------------------------------------- |
| PF-10 (Notifications)       | Domain Event → PF-10 INSERT | Real-time alert to treatment team on high/imminent risk                                            |
| PF-33/SignatureCanvas       | Direct (Pattern A)          | Patient and clinician safety plan signatures via `SignatureCanvas`                                 |
| CL-01 (Patient Chart)       | Internal + DB trigger       | Risk 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:**

```typescript theme={null}
{
  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:**

```typescript theme={null}
// 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:**

```typescript theme={null}
{
  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

| Factor                 | Assessment                                                                                                                                                                                                                                                                      |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Clinical workflow**  | Safety 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 overhead** | Pattern 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.                                                             |
| **Precedent**          | Pattern 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 compliance**   | For v1, `*_signed_at` timestamp + base64 image constitutes a sufficient record of consent for Joint Commission NPSG.15.01.01.                                                                                                                                                   |
| **PHI handling**       | Signature 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`)

| Column                     | Type          | Description                                                      |
| -------------------------- | ------------- | ---------------------------------------------------------------- |
| `patient_signature_data`   | `JSONB`       | `{ image: base64PNG, captured_at: ISO8601 }` — NULL until signed |
| `patient_signed_at`        | `TIMESTAMPTZ` | Timestamp when patient signature was captured                    |
| `clinician_signature_data` | `JSONB`       | `{ image: base64PNG, captured_at: ISO8601 }` — NULL until signed |
| `clinician_signed_at`      | `TIMESTAMPTZ` | Timestamp 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):**

```sql theme={null}
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:

```sql theme={null}
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

| Mistake                                                          | Fix                                                                                            |
| ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| Using one-arg `cl_has_org_access(organization_id)` in RLS        | Use two-arg `cl_has_org_access(organization_id, auth.uid())` (Errata E-4)                      |
| Re-creating `cl_has_org_access` in CL-07 migration               | DO 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 instrument                                   | SAFE-T has no numeric score; `score` column is NULL; risk\_level entered by clinician (FR-1.6) |
| Using formal `pf_signature_requests` for safety plan             | Use Pattern A (SignatureCanvas) only for v1 (Errata E-6)                                       |
| Showing risk level via N+1 join on chart load                    | Read from `cl_patient_charts.current_risk_level` (denormalized, Errata E-7)                    |
| `next_screening_due` treated as optional in notification payload | Always set on save; include in event payload (Errata E-5)                                      |
| Starting CL-13 migration before CL-07                            | CL-07 migrations MUST precede CL-13                                                            |

***

## Related Docs

* [CL-01 Patient Chart & Clinical Summary](./patient-chart-clinical-summary-integration.md) — Risk flag columns on chart
* [CL-02 Comprehensive Assessments](./comprehensive-assessments-integration.md) — CL-02 references CL-07 for risk workflows
* [CL-13 Crisis Intervention spec](../../../specs/cl/specs/CL-13-crisis-intervention-documentation.md) — Bidirectional coupling with CL-07 (no separate integration stub; see spec and CL-07 errata)
* [CROSS\_CORE\_INTEGRATIONS.md](./CROSS_CORE_INTEGRATIONS.md) — Matrix entry for CL-07
