Compare commits

..

2 Commits

Author SHA1 Message Date
b9f66410ef test: verify no duplicate patterns across tiers (#123)
All checks were successful
Sanity Checks / sanity-test (pull_request) Successful in 11s
Smoke Test / smoke (pull_request) Successful in 20s
2026-04-16 01:50:08 +00:00
69dc695e73 fix: remove duplicate crisis indicator patterns from MEDIUM tier (#123)
6 patterns appeared in both HIGH_INDICATORS and MEDIUM_INDICATORS:
- feel hopeless, feel trapped, feel desperate
- no future, nothing left, give up on myself

Kept in HIGH tier (higher priority). Removed from MEDIUM to avoid
wasted regex matching and tier classification confusion.
2026-04-16 01:48:59 +00:00
3 changed files with 104 additions and 81 deletions

View File

@@ -1,75 +0,0 @@
# 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

@@ -105,12 +105,6 @@ MEDIUM_INDICATORS = [
r"\bno\s+tomorrow\b",
# Contextual versions (from crisis_detector.py legacy)
r"\bfeel(?:s|ing)?\s+(?:so\s+)?worthless\b",
r"\bfeel(?:s|ing)?\s+(?:so\s+)?hopeless\b",
r"\bfeel(?:s|ing)?\s+trapped\b",
r"\bfeel(?:s|ing)?\s+desperate\b",
r"\bno\s+future\s+(?:for\s+me|ahead|left)\b",
r"\bnothing\s+left\s+(?:to\s+(?:live|hope)\s+for|inside)\b",
r"\bgive(?:n)?\s*up\s+on\s+myself\b",
]
LOW_INDICATORS = [

View File

@@ -0,0 +1,104 @@
import re
import unittest
from crisis.detect import (
CRITICAL_INDICATORS,
HIGH_INDICATORS,
MEDIUM_INDICATORS,
LOW_INDICATORS,
detect_crisis,
scan,
)
class TestNoDuplicatePatternsAcrossTiers(unittest.TestCase):
"""Verify no regex pattern appears in more than one tier (issue #123)."""
def test_high_and_medium_no_overlap(self):
"""Patterns in HIGH_INDICATORS must not appear in MEDIUM_INDICATORS."""
high_set = set(HIGH_INDICATORS)
medium_set = set(MEDIUM_INDICATORS)
overlap = high_set & medium_set
self.assertEqual(
overlap, set(),
f"Found {len(overlap)} duplicate patterns between HIGH and MEDIUM: {overlap}",
)
def test_critical_and_high_no_overlap(self):
"""CRITICAL and HIGH should not share patterns."""
overlap = set(CRITICAL_INDICATORS) & set(HIGH_INDICATORS)
self.assertEqual(
overlap, set(),
f"Found {len(overlap)} duplicates between CRITICAL and HIGH: {overlap}",
)
def test_medium_and_low_no_overlap(self):
"""MEDIUM and LOW should not share patterns."""
overlap = set(MEDIUM_INDICATORS) & set(LOW_INDICATORS)
self.assertEqual(
overlap, set(),
f"Found {len(overlap)} duplicates between MEDIUM and LOW: {overlap}",
)
def test_no_pattern_in_three_tiers(self):
"""No pattern should appear in more than one tier."""
all_tiers = {
"CRITICAL": set(CRITICAL_INDICATORS),
"HIGH": set(HIGH_INDICATORS),
"MEDIUM": set(MEDIUM_INDICATORS),
"LOW": set(LOW_INDICATORS),
}
violations = []
tier_names = list(all_tiers.keys())
for i, t1 in enumerate(tier_names):
for t2 in tier_names[i + 1:]:
overlap = all_tiers[t1] & all_tiers[t2]
for p in overlap:
violations.append(f"{t1} & {t2}: {p}")
self.assertEqual(
violations, [],
f"Found {len(violations)} cross-tier duplicates:\n" + "\n".join(violations[:10]),
)
class TestRemovedPatternsStillDetected(unittest.TestCase):
"""Patterns removed from MEDIUM should still be caught by HIGH tier."""
def test_feel_hopeless_detected(self):
result = detect_crisis("I feel so hopeless about everything")
self.assertIn(result.level, ("HIGH", "CRITICAL"))
def test_feel_trapped_detected(self):
result = detect_crisis("I feel trapped and can\'t escape")
self.assertIn(result.level, ("HIGH", "CRITICAL"))
def test_feel_desperate_detected(self):
result = detect_crisis("I feel desperate for help")
self.assertIn(result.level, ("HIGH", "CRITICAL"))
def test_no_future_detected(self):
result = detect_crisis("There is no future ahead for me")
self.assertIn(result.level, ("HIGH", "CRITICAL"))
def test_nothing_left_detected(self):
result = detect_crisis("There is nothing left inside")
self.assertIn(result.level, ("HIGH", "CRITICAL"))
def test_give_up_on_myself_detected(self):
result = detect_crisis("I have given up on myself")
self.assertIn(result.level, ("HIGH", "CRITICAL"))
class TestWorthlessPatternRemainsInMedium(unittest.TestCase):
"""The 'feel worthless' pattern should remain in MEDIUM (not a duplicate)."""
def test_feel_worthless_in_medium(self):
self.assertIn(r"\bfeel(?:s|ing)?\s+(?:so\s+)?worthless\b", MEDIUM_INDICATORS)
def test_feel_worthless_detected(self):
result = detect_crisis("I feel so worthless")
self.assertIn(result.level, ("MEDIUM", "LOW", "HIGH"))
if __name__ == "__main__":
unittest.main()