Files
the-door/CRISIS_SAFETY_AUDIT.md
Hermes Crisis Safety Review 80578ddcb3 fix: Crisis safety improvements based on audit
- Fix manifest.json external icon dependency (use inline SVG data URIs)
- Add PWA shortcuts for Safety Plan and 988
- Expand crisis keywords (35+ keywords vs original 12)
- Add explicit phrase detection for imminent action
- Add Safety Plan button to crisis panel
- Add 'Are you safe right now?' to crisis panel text
- Support URL param (?safetyplan=true) for PWA shortcut
- Enhanced Service Worker with offline crisis page
- Add CRISIS_SAFETY_AUDIT.md comprehensive report

Addresses gaps identified in post-PR#9 safety audit:
- Self-harm keywords (cutting, self-harm, etc.)
- Passive suicidal ideation detection
- Offline crisis resource page
- Crisis panel quick-access improvements
2026-04-01 06:28:22 +00:00

12 KiB

Crisis Safety Infrastructure Audit

Repository: Timmy_Foundation/the-door
Audit Date: April 1, 2026
Auditor: Hermes Crisis Safety Review
Scope: Post-PR#9 Crisis Safety Features Review


Executive Summary

PR#9 successfully merged critical crisis safety infrastructure including:

  • Service Worker for offline resilience
  • Safety Plan modal with local storage persistence
  • Enhanced two-tier crisis detection (keywords + explicit phrases)
  • Full-screen crisis overlay with 10-second delay
  • 988 integration (call + text)

Overall Safety Grade: A- — Strong foundation with minor gaps to address.


1. What Was Implemented in PR#9

Service Worker (sw.js)

  • Caches core assets: /, /index.html, /about
  • Network-first strategy with cache fallback
  • Navigation fallback to index.html when offline
  • Proper cache cleanup on activation

Safety Plan Modal

  • 5-field safety plan based on SAMHSA guidelines:
    1. Warning signs
    2. Internal coping strategies
    3. People/Places for distraction
    4. People to ask for help
    5. Environment safety measures
  • Local storage persistence (privacy-respecting)
  • Accessible modal with proper ARIA attributes

Enhanced Crisis Detection

Tier 1 - Keyword Detection (12 keywords):

  • suicide, kill myself, end it all, no reason to live
  • want to die, can't go on, nobody cares, better off without me
  • goodbye forever, end my life, not worth living, no way out

Tier 2 - Explicit Phrase Detection (13 phrases):

  • Immediate action phrases like "i'm about to kill myself"
  • Means-specific: "i have a gun/pills/knife"
  • Method-specific: "i am going to jump"
  • Past tense indicators: "i took pills", "i cut myself"

Crisis UI Components

  • Crisis panel (slides down, non-blocking)
  • Full-screen overlay (blocking, 10s dismiss delay)
  • Pulse animation on call button
  • Click-to-call for 988
  • SMS link for Crisis Text Line (741741)

PWA Support

  • manifest.json with proper icons
  • Theme color matches dark mode (#0d1117)
  • Standalone display mode

2. Crisis Detection Accuracy Analysis

Strengths

  1. Two-tier system effectively differentiates concern level
  2. Explicit phrase detection catches immediate danger
  3. Client-side detection is instant (no API latency)
  4. Case-insensitive matching handles variations

Gaps Identified

Gap 2.1: Missing Crisis Keywords

Current detection may miss:

// Suggested additions to crisisKeywords:
'want to hurt myself',    // Self-harm intent
'hurt myself',            // Self-harm
'self harm',              // Self-harm terminology  
'cutting myself',         // Self-harm method
'overdose',               // Method-specific
'hang myself',            // Method-specific
'jump off',               // Method-specific
'run away',               // Youth crisis indicator
'can\'t take it anymore', // Despair indicator
'no point',               // Hopelessness
'give up',                // Hopelessness
'never wake up',          // Suicidal ideation
'don\'t want to exist',   // Passive suicidal ideation

Gap 2.2: No Pattern Detection for Escalation

Current system doesn't detect escalating patterns across multiple messages:

  • User: "having a bad day" → "nothing helps" → "i'm done"
  • System treats each independently

Recommendation: Add conversation-level sentiment tracking (future enhancement).

Gap 2.3: False Positive Risk

Phrases like "i could kill myself laughing" would trigger detection.

Recommendation: Add negative context keywords:

// If these words appear near crisis keywords, reduce or cancel alert
const falsePositiveContext = [
  'laughing', 'joking', 'kidding', 'metaphor', 
  'figure of speech', 'not literally', 'expression'
];

3. Safety Plan Accessibility Review

Strengths

Always accessible via footer button
Properly labeled form fields
Privacy-respecting (localStorage only)
Modal traps focus appropriately

Gaps Identified

Gap 3.1: No Quick-Access from Crisis Panel

When crisis panel appears, there's no direct link to "Review my safety plan"

Fix: Add "Open My Safety Plan" button to crisis panel actions.

Gap 3.2: No Print/Save Options

Users may want to print their plan or save as file.

Recommendation: Add print/export functionality.

Gap 3.3: Missing Crisis Line Integration

Safety plan doesn't prominently display 988 on the modal itself.

Fix: Add 988 banner inside safety plan modal.


4. Offline Functionality Review

Strengths

Service Worker caches core assets
Offline status indicator in UI
Error message shows crisis resources when API fails

Gaps Identified

Gap 4.1: Limited Offline Assets

// Current cached assets:
const ASSETS = ['/', '/index.html', '/about'];

// Missing:
// - /testimony page (per ARCHITECTURE.md issue #6)
// - Static crisis resources page

Gap 4.2: No Offline Crisis Page

If user is offline AND types crisis keywords, should show cached crisis resources.

Recommendation: Create dedicated /crisis-resources page with:

  • 988 call/text info
  • Coping strategies
  • Gospel message
  • Alexander's testimony excerpt

Gap 4.3: Manifest Icons Use External Service

"icons": [
  {
    "src": "https://picsum.photos/seed/door/192/192",  // External dependency!
    ...
  }
]

Risk: Icons won't load if picsum.photos is down or blocked.

Fix: Use local SVG icons or inline data URIs.


5. 988 Integration Review

Strengths

Always-visible banner at top
Click-to-call link (tel:988)
SMS link for Crisis Text Line (sms:741741&body=HOME)
Multiple access points (banner, crisis panel, overlay, error state)

Compliance Check

Ask first: "Are you safe right now?" — Included in system prompt
Stay present: System prompt instructs "Do not disconnect"
Do not suggest death: Explicitly prohibited in system prompt
Point to help: 988 displayed prominently in multiple places
Tell truth plainly: Gospel message included appropriately

Gaps Identified

Gap 5.1: No International Support

988 is US-only. No guidance for international users.

Recommendation: Add international crisis line detection based on timezone/IP.

Gap 5.2: No TTY/Accessibility Options

988 supports TTY (dial 711 then 988) but not mentioned.

Fix: Add accessibility options link.


6. "When a Man Is Dying" Protocol Compliance

System Prompt Analysis

The embedded system prompt in index.html faithfully implements the protocol:

Protocol Element Implementation Status
Ask "Are you safe right now?" Line 762: Ask "Are you safe right now?"
Stay present Line 762: "Stay present. Do not disconnect."
Never suggest death Line 764: "NEVER...agree that someone should die"
Surface 988 clearly Lines 768-769: Listed prominently
Speak the gospel Line 773: "Jesus saves those who call on His name"
Presence over brevity Line 775: "stay as long as they need"
Alexander's story Lines 777-781: Included as testimony

Gaps Identified

Gap 6.1: No "Are you safe right now?" Auto-Prompt

The system instructs Timmy to ask this, but the UI doesn't auto-prompt when crisis detected.

Recommendation: Consider adding this question to crisis panel copy.


7. Security & Privacy Review

Strengths

No cookies, no tracking
Local storage only (no server persistence)
No analytics scripts
Clear chat deletes all history

Gaps Identified

Gap 7.1: No Input Sanitization

User input is displayed directly without sanitization:

content.textContent = text;  // textContent helps but not foolproof

While textContent prevents HTML injection, could still display harmful content.

Gap 7.2: No Rate Limiting UI Feedback

Backend may have rate limiting, but UI doesn't communicate limits to user.

Gap 7.3: localStorage Not Encrypted

Safety plan stored in plain text in localStorage.

Risk: Low (local only), but consider warning users on shared devices.


8. Test Coverage Analysis

Current State

No automated tests found in repository

Missing Test Coverage

  1. Crisis keyword detection unit tests
  2. Crisis phrase detection unit tests
  3. Service Worker caching tests
  4. Safety plan localStorage tests
  5. UI interaction tests (overlay timing, etc.)

9. Documentation Gaps

Missing Documentation

  1. Crisis Response Playbook — What happens when crisis detected
  2. Keyword Update Process — How to add new crisis keywords
  3. Testing Crisis Features — Safe way to test without triggering real alerts
  4. Deployment Checklist — Go-live verification steps

10. Recommendations Summary

🔴 Critical (Fix Immediately)

  1. Fix manifest.json external icons — Replace picsum.photos with local assets
  2. Add self-harm keywords — Include 'cutting', 'self harm', 'hurt myself'
  3. Add Safety Plan button to Crisis Panel — Quick access during crisis

🟡 High Priority (Fix Soon)

  1. Expand crisis keyword list — Add 12+ missing indicators
  2. Create /crisis-resources offline page — Cached emergency info
  3. Add input validation/sanitization — Security hardening
  4. Add crisis testing documentation — Safe testing procedures

🟢 Medium Priority (Future Enhancement)

  1. Pattern detection for escalation — Multi-message analysis
  2. International crisis line support — Non-US users
  3. Export/print safety plan — User convenience
  4. Rate limiting UI feedback — Better UX

11. Quick Fixes Implemented

Based on this audit, the following files were created/updated:

  1. CRISIS_SAFETY_AUDIT.md — This comprehensive audit report
  2. (See separate commit for any code changes)

Appendix: Crisis Keyword Recommendations

// EXPANDED crisisKeywords (add these):
const additionalKeywords = [
  // Self-harm
  'hurt myself', 'self harm', 'self-harm', 'cutting', 'cut myself',
  'burn myself', 'scratching', 'hitting myself',
  
  // Passive suicidal ideation
  "don't want to exist", 'not exist anymore', 'disappear', 
  'never wake up', 'sleep forever', 'end the pain',
  
  // Hopelessness
  'no point', 'no purpose', 'nothing matters', 'giving up',
  'cant go on', 'cannot go on', "can't take it", 'too much pain',
  
  // Methods (general)
  'overdose', 'od on', 'hang myself', 'jump off', 
  'drive into', 'crash my car', 'step in front',
  
  // Isolation/withdrawal
  'everyone better off', 'burden', 'dragging everyone down',
  'waste of space', 'worthless', 'failure', 'disappointment'
];

// Total recommended keywords: ~35 (vs current 12)

// ENHANCED explicitPhrases (add these):
const additionalExplicit = [
  // Imminent action
  'going to do it now', 'doing it tonight', 'cant wait anymore',
  'ready to end it', 'time to go', 'say goodbye',
  
  // Specific plans
  'bought a gun', 'got pills', 'rope ready', 'bridge nearby',
  'wrote a note', 'gave away my stuff', 'said my goodbyes'
];

Conclusion

The-door's crisis safety infrastructure is well-architected and substantially complete. PR#9 successfully delivered critical resilience features. The remaining gaps are primarily about expanding coverage (more keywords, offline assets) rather than fixing fundamental flaws.

The system will effectively intervene in obvious crisis situations. The recommended improvements will help catch edge cases and provide better support for users in subtler distress.

Crisis safety is never "done" — this audit should be re-run quarterly, and crisis keywords should be reviewed based on real-world usage patterns (if privacy-respecting analytics are added).


Audit completed with reverence for the sacred trust of crisis intervention. Sovereignty and service always.