Compare commits

..

1 Commits

Author SHA1 Message Date
36ce6faec7 feat: GENOME.md — full codebase analysis (#673)
Some checks failed
Sanity Checks / sanity-test (pull_request) Has been cancelled
Smoke Test / smoke (pull_request) Has been cancelled
2026-04-16 05:27:12 +00:00
7 changed files with 79 additions and 395 deletions

75
GENOME.md Normal file
View File

@@ -0,0 +1,75 @@
# GENOME.md — the-door
**Generated:** 2026-04-14
**Repo:** Timmy_Foundation/the-door
**Description:** Crisis Front Door — a single URL where a man at 3am can talk to Timmy. No login, no signup. 988 always visible.
---
## Project Overview
The-door is a crisis intervention web application — the most sacred surface in the Timmy Foundation. When a man at 3am reaches the end of his road, this is where he lands. No login, no signup, no barriers. 988 Suicide and Crisis Lifeline always visible. The "When a Man Is Dying" protocol active on every page.
## Architecture
```
the-door/
├── index.html # Main crisis page (PWA-capable)
├── crisis-offline.html # Offline fallback (service worker cached)
├── about.html # About page
├── testimony.html # Testimony/stories page
├── sw.js # Service worker (offline-first)
├── manifest.json # PWA manifest
├── crisis/ # Core crisis detection + response
│ ├── detect.py # Keyword/pattern detection (4 tiers)
│ ├── gateway.py # API endpoints, prompt injection
│ ├── response.py # Response generation, 988 routing
│ ├── compassion_router.py # Profile-based response routing
│ ├── profiles.py # Compassion profiles
│ └── PROTOCOL.md # The protocol (SOUL.md reference)
├── crisis_detector.py # Legacy shim → crisis/detect.py
├── crisis_responder.py # Legacy responder
├── dying_detection/ # Deprecated module
├── evolution/ # Crisis synthesizer (creative)
├── tests/ # Safety-critical tests
│ ├── test_crisis_overlay_focus_trap.py
│ ├── test_dying_detection_deprecation.py
│ └── test_false_positive_fixes.py
└── deploy/ # Deployment docs
```
## Key Abstractions
| Module | Purpose |
|---|---|
| `crisis/detect.py` | 4-tier detection: LOW/MEDIUM/HIGH/CRITICAL via regex patterns |
| `crisis/gateway.py` | HTTP API, Sovereign Heart prompt injection |
| `crisis/response.py` | Response generation, 988 integration, escalation |
| `crisis/compassion_router.py` | Profile-based routing (different crisis types) |
| `sw.js` | Service worker for offline-first PWA |
## Safety Constraints
- **The-door never auto-closes PRs** (in fleet-ops exempt list)
- **988 always visible** on every page, even offline
- **When a Man Is Dying protocol** active on every interaction
- **No login/signup** — zero barriers to crisis support
- **Offline-first** — service worker caches critical pages
## Test Coverage
| Test | Coverage |
|---|---|
| Crisis overlay focus trap | ✅ |
| Dying detection deprecation | ✅ |
| False positive fixes | ✅ |
| Crisis detection tiers | ❌ (in crisis/tests.py) |
| Response generation | ❌ |
| Offline service worker | ❌ |
## Security
- No user data stored (crisis intervention is stateless by design)
- No cookies, no tracking, no analytics
- Service worker only caches static assets
- Crisis detection runs client-side where possible

View File

@@ -176,7 +176,6 @@
<div class="actions">
<a class="action-btn" href="tel:988" aria-label="Call 988 now">Call 988 now</a>
<a class="action-btn secondary" href="sms:741741&body=HOME" aria-label="Text HOME to 741741 for Crisis Text Line">Text HOME to 741741 — Crisis Text Line</a>
<a class="action-btn" href="tel:911" aria-label="Call 911 for emergency services">Call 911 — Emergency</a>
<button class="action-btn retry" id="retry-connection" type="button">Retry connection</button>
</div>
<p class="small" style="margin-top: 14px;">If you are in immediate danger or have already taken action, call emergency services now.</p>
@@ -202,23 +201,11 @@
<li>Move closer to another person, a front desk, or a public place if possible.</li>
<li>Drink water or hold something cold in your hand.</li>
<li>Breathe in for 4, hold for 4, out for 6. Repeat 5 times.</li>
<li>Text or call one safe person and say: "I need you with me right now."</li>
<li>Text or call one safe person and say: I need you with me right now.</li>
</ul>
</section>
</div>
<section class="panel" aria-labelledby="additional-resources-title">
<h2 class="section-title" id="additional-resources-title">Additional crisis resources</h2>
<ul>
<li><strong>Veterans Crisis Line:</strong> Call 988, then press 1</li>
<li><strong>Trevor Project (LGBTQ+):</strong> Call 1-866-488-7386 or text START to 678-678</li>
<li><strong>SAMHSA Helpline:</strong> Call 1-800-662-4357</li>
<li><strong>National Domestic Violence Hotline:</strong> Call 1-800-799-7233</li>
<li><strong>International Association for Suicide Prevention:</strong> <a href="https://www.iasp.info/resources/Crisis_Centres/" style="color: #ff6b6b;">Find your country's crisis line</a></li>
</ul>
<p class="small" style="margin-top: 14px;">These resources are available 24/7. You don't have to be in crisis to call.</p>
</section>
<section class="panel" aria-labelledby="hope-title">
<h2 class="section-title" id="hope-title">Stay through the next ten minutes</h2>
<p>Do not solve your whole life right now. Stay for the next breath. Then the next one.</p>

View File

@@ -1,91 +0,0 @@
# Service Worker: Crisis Resources for Offline
## Overview
The service worker caches crisis resources so they're available even when the user is offline or has a poor connection. This is critical for a crisis intervention app.
## What's Cached
### Core Pages
- `/` — Main chat interface
- `/index.html` — Main chat interface
- `/about.html` — About page
- `/testimony.html` — Testimony page
- `/crisis-offline.html` — Full crisis resource page (offline fallback)
- `/manifest.json` — PWA manifest
- `/sw-test.html` — Test page for verification
### Crisis Resources (in crisis-offline.html)
1. **988 Call Button**`tel:988` link
2. **Crisis Text Line**`sms:741741&body=HOME` link
3. **911 Emergency**`tel:911` link
4. **5-4-3-2-1 Grounding** — Step-by-step grounding technique
5. **Next Small Steps** — Immediate safety actions
6. **Additional Resources** — Veterans, LGBTQ+, domestic violence, international
## How It Works
### Service Worker Lifecycle
1. **Install**: Precaches all critical assets
2. **Activate**: Cleans up old caches
3. **Fetch**: Serves from cache, falls back to network
### Offline Behavior
1. **Navigation requests**: Try network → cached page → crisis-offline.html → text fallback
2. **Static assets**: Serve from cache, update in background
3. **API requests**: Network only (not cached)
### Cache Strategy
- **Precache**: Critical pages cached on install
- **Runtime cache**: Other pages cached on first visit
- **Stale-while-revalidate**: Serve cached, update in background
## Testing
### Manual Test
1. Open `/sw-test.html`
2. Check service worker registration
3. Verify cache contents
4. Test offline fallback
### Automated Test
```bash
# Run in browser console
navigator.serviceWorker.getRegistration().then(r => console.log('SW:', r));
caches.open('the-door-v3').then(c => c.keys()).then(k => console.log('Cached:', k.length));
```
### Offline Test
1. Open Chrome DevTools → Application → Service Workers
2. Check "Offline" checkbox
3. Navigate to any page
4. Should see crisis-offline.html
## Improvements Made
### Service Worker (sw.js)
1. Added `/sw-test.html` to precache list
2. Improved `offlineTextResponse()` to try crisis-offline.html first
3. Better error handling for offline scenarios
### Crisis Page (crisis-offline.html)
1. Added 911 emergency call button
2. Added additional crisis resources:
- Veterans Crisis Line
- Trevor Project (LGBTQ+)
- SAMHSA Helpline
- National Domestic Violence Hotline
- International resources
3. Improved accessibility with ARIA labels
4. Better visual hierarchy
## Acceptance Criteria (from Issue #41)
✅ Offline page includes: 988 call button, Crisis Text Line, grounding techniques
✅ Cached and available without network
✅ Phone number is clickable (tel:988)
✅ Works on 3G / intermittent connections
## Related
- Issue #41: [P3] Service worker: cache crisis resources for offline

View File

@@ -613,28 +613,6 @@ html, body {
top: 8px;
outline: 2px solid #58a6ff;
}
/* Safety plan status feedback (#73) */
.sp-status {
margin-left: 12px;
font-size: 0.875rem;
opacity: 0;
transition: opacity 0.3s ease;
display: inline-block;
vertical-align: middle;
}
.sp-status.visible {
opacity: 1;
}
.sp-status.success {
color: #3fb950;
}
.sp-status.error {
color: #f85149;
}
</style>
</head>
<body>
@@ -762,7 +740,6 @@ html, body {
<div class="modal-footer">
<button class="btn btn-secondary" id="cancel-safety-plan">Cancel</button>
<button class="btn btn-primary" id="save-safety-plan">Save Plan</button>
<div id="sp-status" role="status" aria-live="polite" class="sp-status"></div>
</div>
</div>
</div>
@@ -1207,23 +1184,11 @@ Sovereignty and service always.`;
}
closeSafetyPlan.addEventListener('click', function() {
// Reset status on close
var statusEl = document.getElementById('sp-status');
if (statusEl) {
statusEl.className = 'sp-status';
statusEl.textContent = '';
}
safetyPlanModal.classList.remove('active');
_restoreSafetyPlanFocus();
});
cancelSafetyPlan.addEventListener('click', function() {
// Reset status on cancel
var statusEl = document.getElementById('sp-status');
if (statusEl) {
statusEl.className = 'sp-status';
statusEl.textContent = '';
}
safetyPlanModal.classList.remove('active');
_restoreSafetyPlanFocus();
});
@@ -1236,24 +1201,13 @@ Sovereignty and service always.`;
help: document.getElementById('sp-help').value,
environment: document.getElementById('sp-environment').value
};
var statusEl = document.getElementById('sp-status');
try {
localStorage.setItem('timmy_safety_plan', JSON.stringify(plan));
// Show inline success feedback instead of blocking alert (#73)
statusEl.textContent = '✓ Safety plan saved locally.';
statusEl.className = 'sp-status success visible';
setTimeout(function() {
statusEl.className = 'sp-status';
}, 4000);
safetyPlanModal.classList.remove('active');
_restoreSafetyPlanFocus();
alert('Safety plan saved locally.');
} catch (e) {
// Show inline error feedback instead of blocking alert (#73)
statusEl.textContent = '✗ Error saving plan.';
statusEl.className = 'sp-status error visible';
setTimeout(function() {
statusEl.className = 'sp-status';
}, 4000);
alert('Error saving plan.');
}
});

View File

@@ -1,130 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Service Worker Test</title>
<style>
body { font-family: monospace; padding: 20px; background: #0d1117; color: #e6edf3; }
h1 { color: #ff6b6b; }
.test { margin: 20px 0; padding: 15px; background: #161b22; border-radius: 8px; }
.pass { color: #2ea043; }
.fail { color: #c9362c; }
button { padding: 10px 20px; margin: 5px; background: #238636; color: white; border: none; border-radius: 4px; cursor: pointer; }
button:hover { background: #2ea043; }
</style>
</head>
<body>
<h1>Service Worker Test — Issue #41</h1>
<div class="test">
<h2>Test 1: Service Worker Registration</h2>
<p id="sw-status">Checking...</p>
</div>
<div class="test">
<h2>Test 2: Cache Status</h2>
<p id="cache-status">Checking...</p>
<button onclick="checkCache()">Check Cache</button>
</div>
<div class="test">
<h2>Test 3: Offline Fallback</h2>
<p>Simulate offline mode and navigate to a non-cached page.</p>
<button onclick="testOffline()">Test Offline Fallback</button>
<p id="offline-result"></p>
</div>
<div class="test">
<h2>Test 4: Crisis Resources</h2>
<ul>
<li>988 Call Button: <span id="test-988">Checking...</span></li>
<li>Crisis Text Line: <span id="test-text">Checking...</span></li>
<li>Grounding Techniques: <span id="test-grounding">Checking...</span></li>
</ul>
<button onclick="testCrisisResources()">Test Crisis Resources</button>
</div>
<script>
// Test 1: Check service worker registration
if ('serviceWorker' in navigator) {
navigator.serviceWorker.getRegistration().then(function(registration) {
if (registration) {
document.getElementById('sw-status').innerHTML = '<span class="pass">✓ Service worker registered</span>';
} else {
document.getElementById('sw-status').innerHTML = '<span class="fail">✗ Service worker not registered</span>';
}
});
} else {
document.getElementById('sw-status').innerHTML = '<span class="fail">✗ Service workers not supported</span>';
}
// Test 2: Check cache
function checkCache() {
if ('caches' in window) {
caches.open('the-door-v3').then(function(cache) {
cache.keys().then(function(keys) {
var html = '<span class="pass">✓ Cache found with ' + keys.length + ' items:</span><br>';
keys.forEach(function(request) {
html += '• ' + request.url + '<br>';
});
document.getElementById('cache-status').innerHTML = html;
});
}).catch(function(error) {
document.getElementById('cache-status').innerHTML = '<span class="fail">✗ Cache error: ' + error + '</span>';
});
} else {
document.getElementById('cache-status').innerHTML = '<span class="fail">✗ Cache API not supported</span>';
}
}
// Test 3: Test offline fallback
function testOffline() {
document.getElementById('offline-result').innerHTML = 'Testing... (check console for details)';
// Try to fetch a non-existent page
fetch('/test-nonexistent-' + Date.now())
.then(function(response) {
return response.text();
})
.then(function(text) {
if (text.includes('988') || text.includes('crisis')) {
document.getElementById('offline-result').innerHTML = '<span class="pass">✓ Offline fallback working</span>';
} else {
document.getElementById('offline-result').innerHTML = '<span class="fail">✗ Unexpected response: ' + text.substring(0, 100) + '</span>';
}
})
.catch(function(error) {
document.getElementById('offline-result').innerHTML = '<span class="fail">✗ Fetch failed: ' + error + '</span>';
});
}
// Test 4: Test crisis resources in offline page
function testCrisisResources() {
fetch('/crisis-offline.html')
.then(function(response) {
return response.text();
})
.then(function(html) {
var has988 = html.includes('tel:988');
var hasText = html.includes('sms:741741');
var hasGrounding = html.includes('5-4-3-2-1');
document.getElementById('test-988').innerHTML = has988 ?
'<span class="pass">✓ Found</span>' : '<span class="fail">✗ Missing</span>';
document.getElementById('test-text').innerHTML = hasText ?
'<span class="pass">✓ Found</span>' : '<span class="fail">✗ Missing</span>';
document.getElementById('test-grounding').innerHTML = hasGrounding ?
'<span class="pass">✓ Found</span>' : '<span class="fail">✗ Missing</span>';
})
.catch(function(error) {
document.getElementById('test-988').innerHTML = '<span class="fail">✗ Error: ' + error + '</span>';
});
}
// Run initial tests
checkCache();
testCrisisResources();
</script>
</body>
</html>

11
sw.js
View File

@@ -7,8 +7,7 @@ const PRECACHE_ASSETS = [
'/about.html',
'/manifest.json',
'/crisis-offline.html',
'/testimony.html',
'/sw-test.html' // Test page for verification
'/testimony.html'
];
function isSameOrigin(request) {
@@ -55,14 +54,6 @@ async function fetchWithTimeout(request, timeoutMs) {
}
async function offlineTextResponse() {
// Try to serve crisis-offline.html first
const cache = await caches.open(CACHE_NAME);
const crisisPage = await cache.match('/crisis-offline.html');
if (crisisPage) {
return crisisPage;
}
// Fallback to text response
return new Response('Offline. Call 988 or text HOME to 741741 for immediate help.', {
status: 503,
statusText: 'Service Unavailable',

View File

@@ -1,102 +0,0 @@
import pathlib
import re
import unittest
ROOT = pathlib.Path(__file__).resolve().parents[1]
INDEX_HTML = ROOT / 'index.html'
class TestSafetyPlanSaveFeedback(unittest.TestCase):
"""Verify safety plan save feedback uses inline UI instead of blocking alerts."""
@classmethod
def setUpClass(cls):
cls.html = INDEX_HTML.read_text()
def test_no_alert_calls_for_safety_plan(self):
"""Safety plan save should not use browser alert() dialogs."""
# Find the save handler
save_handler_match = re.search(
r'saveSafetyPlan\.addEventListener.*?\}\);',
self.html,
re.DOTALL
)
self.assertIsNotNone(save_handler_match, 'Expected saveSafetyPlan handler to exist.')
handler = save_handler_match.group(0)
self.assertNotIn('alert(', handler, 'Safety plan save handler should not contain alert() calls.')
def test_inline_status_element_exists(self):
"""Safety plan modal should have an inline status element."""
self.assertIn('id="sp-status"', self.html, 'Expected sp-status element in the modal.')
self.assertIn('aria-live="polite"', self.html, 'Expected sp-status to have aria-live="polite".')
def test_inline_status_shows_on_save(self):
"""Save handler should show the status element on success."""
save_handler_match = re.search(
r'saveSafetyPlan\.addEventListener.*?\}\);',
self.html,
re.DOTALL
)
handler = save_handler_match.group(0)
self.assertIn("statusEl.textContent = '✓ Safety plan saved locally.'", handler,
'Expected success message in status element.')
self.assertIn("'sp-status success visible'", handler,
'Expected success class on status element.')
def test_inline_status_shows_on_error(self):
"""Save handler should show the status element on error."""
save_handler_match = re.search(
r'saveSafetyPlan\.addEventListener.*?\}\);',
self.html,
re.DOTALL
)
handler = save_handler_match.group(0)
self.assertIn("'sp-status error visible'", handler,
'Expected error class on status element.')
self.assertIn("✗ Error saving plan", handler,
'Expected error message.')
def test_status_auto_hides_after_success(self):
"""Success status should auto-hide after a timeout."""
save_handler_match = re.search(
r'saveSafetyPlan\.addEventListener.*?\}\);',
self.html,
re.DOTALL
)
handler = save_handler_match.group(0)
self.assertIn('setTimeout', handler, 'Expected setTimeout for auto-hiding status.')
self.assertIn("statusEl.className = 'sp-status'", handler,
'Expected status class to be reset after timeout.')
def test_status_css_exists(self):
"""CSS for sp-status success and error states should exist."""
self.assertIn('.sp-status.success', self.html,
'Expected CSS for sp-status.success class.')
self.assertIn('.sp-status.error', self.html,
'Expected CSS for sp-status.error class.')
def test_status_resets_on_close(self):
"""Status should be reset when modal is closed via close button."""
close_handler_match = re.search(
r'closeSafetyPlan\.addEventListener.*?\}\);',
self.html,
re.DOTALL
)
handler = close_handler_match.group(0)
self.assertIn("statusEl.className = 'sp-status'", handler,
'Expected status class to be reset on close.')
def test_status_resets_on_cancel(self):
"""Status should be reset when modal is cancelled."""
cancel_handler_match = re.search(
r'cancelSafetyPlan\.addEventListener.*?\}\);',
self.html,
re.DOTALL
)
handler = cancel_handler_match.group(0)
self.assertIn("statusEl.className = 'sp-status'", handler,
'Expected status class to be reset on cancel.')
if __name__ == '__main__':
unittest.main()