Local-first Flutter app that identifies the single behavioral change most likely to extend lifespan using hazard-based modeling. Features: - Risk engine with hazard ratios from meta-analyses - 50 countries mapped to 4 mortality groups - 6 modifiable factors: smoking, alcohol, sleep, activity, driving, work hours - SQLite local storage (no cloud, no accounts) - Muted clinical UI theme - 23 unit tests for risk engine Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
277 lines
7.4 KiB
Markdown
277 lines
7.4 KiB
Markdown
# Add Months
|
||
|
||
Local-first Flutter app that identifies the single behavioral change most likely to extend lifespan using hazard-based modeling.
|
||
|
||
## Quick Start
|
||
|
||
```bash
|
||
flutter pub get
|
||
flutter test test/risk_engine/ # 23 unit tests
|
||
flutter run # Debug mode
|
||
flutter run --release -d <device> # Release build
|
||
```
|
||
|
||
## Architecture
|
||
|
||
```
|
||
lib/
|
||
├── main.dart # App entry, routing logic
|
||
├── theme.dart # Muted clinical color palette
|
||
├── models/
|
||
│ ├── enums.dart # Sex, SmokingStatus, AlcoholLevel, etc.
|
||
│ ├── user_profile.dart # Age, sex, country, height, weight, diagnoses
|
||
│ ├── behavioral_inputs.dart # Modifiable behaviors
|
||
│ └── result.dart # LifespanDelta, RankedFactor, CalculationResult
|
||
├── risk_engine/
|
||
│ ├── hazard_ratios.dart # HR constants from meta-analyses
|
||
│ ├── mortality_tables.dart # 50 countries → 4 mortality groups
|
||
│ └── calculator.dart # Core ranking algorithm
|
||
├── screens/
|
||
│ ├── welcome_screen.dart # Onboarding
|
||
│ ├── baseline_screen.dart # Demographics, BMI, conditions
|
||
│ ├── behavioral_screen.dart # Modifiable factors input
|
||
│ └── results_screen.dart # Dominant challenge display
|
||
└── storage/
|
||
└── local_storage.dart # SQLite persistence
|
||
```
|
||
|
||
## Core Principles
|
||
|
||
1. **Local-first**: All data on device, no cloud, no accounts, no analytics
|
||
2. **Evidence-based**: Hazard ratios from peer-reviewed meta-analyses
|
||
3. **Privacy**: Delete All Data = full wipe including encryption keys
|
||
4. **Neutral tone**: "Exposure", "Factor", "Estimated gain" — no moral language
|
||
|
||
## Risk Engine
|
||
|
||
### Hazard Ratio Model
|
||
|
||
Combined HR = Smoking × Alcohol × Sleep × Activity × BMI × Driving × WorkHours
|
||
|
||
Capped at 4.0 to prevent unrealistic compounding.
|
||
|
||
### Key Hazard Ratios
|
||
|
||
| Factor | Level | HR |
|
||
|--------|-------|-----|
|
||
| Smoking | Never | 1.0 |
|
||
| | Former | 1.3 |
|
||
| | Current (<10/day) | 1.8 |
|
||
| | Current (10-20/day) | 2.2 |
|
||
| | Current (>20/day) | 2.8 |
|
||
| Alcohol | None/Light | 1.0 |
|
||
| | Moderate (8-14/wk) | 1.1 |
|
||
| | Heavy (15-21/wk) | 1.3 |
|
||
| | Very Heavy (21+/wk) | 1.6 |
|
||
| Sleep | 7-8 hrs | 1.0 |
|
||
| | 6-7 hrs | 1.05 |
|
||
| | <6 hrs | 1.15 |
|
||
| | >8 hrs | 1.10 |
|
||
| | + Inconsistent | ×1.05 |
|
||
| Activity | High | 1.0 |
|
||
| | Moderate | 1.05 |
|
||
| | Light | 1.15 |
|
||
| | Sedentary | 1.4 |
|
||
| BMI | 18.5-25 | 1.0 |
|
||
| | 25-30 | 1.1 |
|
||
| | 30-35 | 1.2 |
|
||
| | 35-40 | 1.4 |
|
||
| | 40+ | 1.8 |
|
||
| Driving | <50 mi/wk | 1.0 |
|
||
| | 50-150 | 1.02 |
|
||
| | 150-300 | 1.04 |
|
||
| | 300+ | 1.08 |
|
||
| Work Hours | <40 | 1.0 |
|
||
| | 40-55 | 1.05 |
|
||
| | 55-70 | 1.15 |
|
||
| | 70+ | 1.3 |
|
||
|
||
### Existing Conditions (Non-modifiable)
|
||
|
||
| Condition | HR Multiplier |
|
||
|-----------|---------------|
|
||
| Cardiovascular | 1.5 |
|
||
| Diabetes | 1.4 |
|
||
| Cancer (active) | 2.0 |
|
||
| COPD | 1.6 |
|
||
| Hypertension | 1.2 |
|
||
|
||
### Delta Calculation
|
||
|
||
```dart
|
||
// Simplified Gompertz-style approximation
|
||
rawDeltaYears = baselineYears × (1 - modifiedHR/currentHR) × 0.3
|
||
|
||
// Convert to months with uncertainty range
|
||
lowMonths = rawDeltaYears × 12 × 0.6
|
||
highMonths = rawDeltaYears × 12 × 1.4
|
||
```
|
||
|
||
### Ranking Algorithm
|
||
|
||
1. For each modifiable behavior:
|
||
- Compute HR with behavior set to optimal
|
||
- Calculate delta months gained
|
||
2. Sort by midpoint delta descending
|
||
3. Filter out behaviors already at optimal
|
||
4. Return ranked list with confidence levels
|
||
|
||
### Confidence Levels
|
||
|
||
| Factor | Confidence | Rationale |
|
||
|--------|------------|-----------|
|
||
| Smoking | High | Extremely well-documented |
|
||
| Alcohol (heavy) | High | Strong epidemiological data |
|
||
| Physical Activity | High | Large meta-analyses |
|
||
| BMI (extreme) | High | Well-established |
|
||
| Sleep | Moderate | Growing evidence, some confounding |
|
||
| Work Hours | Moderate | Decent studies, cultural variation |
|
||
| Driving | Emerging | Harder to isolate, regional variation |
|
||
|
||
## Mortality Tables
|
||
|
||
### Country Groups
|
||
|
||
| Group | LE at Birth (M) | Countries |
|
||
|-------|-----------------|-----------|
|
||
| A | 81 | Japan, Switzerland, Singapore, Spain, Italy, Australia, Iceland, Israel, Sweden, France, South Korea, Norway |
|
||
| B | 77 | USA, UK, Germany, Canada, Netherlands, Belgium, Austria, Finland, Ireland, New Zealand, Denmark, Portugal, Czech Republic, Poland, Chile, Costa Rica, Cuba, UAE, Qatar, Taiwan |
|
||
| C | 72 | China, Brazil, Mexico, Russia, Turkey, Argentina, Colombia, Thailand, Vietnam, Malaysia, Iran, Saudi Arabia, Egypt, Ukraine, Romania, Hungary, Peru, Philippines |
|
||
| D | 65 | India, Indonesia, South Africa, Pakistan, Bangladesh, Nigeria, Kenya, Ghana, Ethiopia, Myanmar, Nepal, Cambodia |
|
||
|
||
Female LE = Male LE + 4.5 years
|
||
|
||
### Remaining Life Expectancy
|
||
|
||
```dart
|
||
// Survivors have higher LE than birth cohort suggests
|
||
survivorBonus = currentAge × 0.15 // capped at 5
|
||
remainingLE = (leAtBirth - currentAge) + survivorBonus
|
||
```
|
||
|
||
## Storage
|
||
|
||
### SQLite Schema
|
||
|
||
```sql
|
||
CREATE TABLE user_data (
|
||
key TEXT PRIMARY KEY,
|
||
value TEXT NOT NULL, -- JSON
|
||
updated_at INTEGER NOT NULL
|
||
)
|
||
```
|
||
|
||
### Stored Keys
|
||
|
||
- `profile`: UserProfile JSON
|
||
- `behaviors`: BehavioralInputs JSON
|
||
- `lastResult`: CalculationResult JSON
|
||
|
||
### Delete All Data
|
||
|
||
```dart
|
||
await db.delete('user_data'); // Wipes all rows
|
||
```
|
||
|
||
## UI Theme
|
||
|
||
### Colors (Muted Clinical)
|
||
|
||
```dart
|
||
primary: #4A90A4 // Muted teal
|
||
primaryDark: #2D6073
|
||
primaryLight: #7BB8CC
|
||
surface: #F8FAFB
|
||
textPrimary: #1A2B33
|
||
textSecondary: #5A6B73
|
||
success: #4A9A7C
|
||
warning: #B8934A
|
||
error: #A45A5A
|
||
```
|
||
|
||
### Typography
|
||
|
||
- Headlines: SF Pro Display style, tight letter-spacing
|
||
- Body: 16px, 1.5 line height
|
||
- Labels: 600 weight
|
||
|
||
## Testing
|
||
|
||
```bash
|
||
# Run all risk engine tests
|
||
flutter test test/risk_engine/
|
||
|
||
# 23 tests covering:
|
||
# - Hazard ratios for each behavior
|
||
# - Mortality table lookups
|
||
# - Combined HR calculation
|
||
# - Ranking algorithm
|
||
# - Confidence assignments
|
||
# - Existing conditions impact
|
||
```
|
||
|
||
Widget tests require SQLite mocking — integration test on device.
|
||
|
||
## App Icon
|
||
|
||
Generated programmatically: muted teal tree on white background.
|
||
|
||
```bash
|
||
dart run tool/generate_icon.dart
|
||
dart run flutter_launcher_icons
|
||
```
|
||
|
||
## Model Versioning
|
||
|
||
```dart
|
||
const modelVersion = '1.0';
|
||
```
|
||
|
||
Stored with each calculation result. Future updates can show:
|
||
"Results updated under model v1.1"
|
||
|
||
## Screen Flow
|
||
|
||
```
|
||
Welcome → Baseline → Behavioral → Results
|
||
↑ ↓
|
||
└────── Recalculate ───┘
|
||
```
|
||
|
||
Results screen shows:
|
||
- Dominant Challenge (largest gain)
|
||
- Estimated Gain range (e.g., "36-60 months")
|
||
- Confidence level (High/Moderate/Emerging)
|
||
- Secondary factor
|
||
- All other factors (if any)
|
||
- Delete All Data button
|
||
|
||
## Key Design Decisions
|
||
|
||
1. **BMI is baseline only** — affects calculation but not shown as a "challenge"
|
||
2. **Cigarettes/day** — slider with haptic at 20 (one pack), max 40
|
||
3. **Country** — full dropdown (50 countries), mapped internally to groups
|
||
4. **No gamification** — no streaks, badges, or progress tracking
|
||
5. **No notifications** — user controls when to recalculate
|
||
|
||
## Dependencies
|
||
|
||
```yaml
|
||
dependencies:
|
||
sqflite: ^2.3.0 # Local database
|
||
path: ^1.8.3 # Path utilities
|
||
flutter_secure_storage: # Encryption key storage (future)
|
||
|
||
dev_dependencies:
|
||
flutter_launcher_icons: ^0.14.1
|
||
```
|
||
|
||
## Future Enhancements (Out of MVP Scope)
|
||
|
||
- Partner mode (compare two profiles)
|
||
- Export PDF summary
|
||
- Drug use factor
|
||
- Diet quality factor
|
||
- Stress/mental health factor
|
||
- Location-based mortality refinement
|
||
- Longitudinal tracking
|