- 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
379 lines
12 KiB
Markdown
379 lines
12 KiB
Markdown
# 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:
|
|
```javascript
|
|
// 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:
|
|
```javascript
|
|
// 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
|
|
```javascript
|
|
// 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
|
|
```json
|
|
"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:
|
|
```javascript
|
|
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)
|
|
|
|
4. **Expand crisis keyword list** — Add 12+ missing indicators
|
|
5. **Create /crisis-resources offline page** — Cached emergency info
|
|
6. **Add input validation/sanitization** — Security hardening
|
|
7. **Add crisis testing documentation** — Safe testing procedures
|
|
|
|
### 🟢 Medium Priority (Future Enhancement)
|
|
|
|
8. **Pattern detection for escalation** — Multi-message analysis
|
|
9. **International crisis line support** — Non-US users
|
|
10. **Export/print safety plan** — User convenience
|
|
11. **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
|
|
|
|
### Recommended Expanded Lists
|
|
|
|
```javascript
|
|
// 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.*
|