# Sehat Coach — Next Steps

> Project: Sehat Sahoolat v2 · Status as of 2026-05-25
> Architecture & runbook: [[HEALTH_COACH]]
> Repo root: `/Users/tariqmustafa/Desktop/sehat_sahoolat/v2`

The Sehat AI Health Coach (floating avatar, chat over EMR, scheduled
reminders, web + Android + iOS push delivery, patient + doctor portals)
is shipped end-to-end. Three production-blockers and a polish backlog
remain. Tackle 1-3 before going live.

---

## 🔴 Pre-launch blockers — do FIRST

### 1. Rotate two leaked credentials

Both were pasted in chat during dev and must be considered compromised:

- [ ] **OpenAI key** (`sk-proj-UEjalE…`)
  - Revoke: https://platform.openai.com/api-keys
  - Generate a fresh `sk-proj-…` key
  - Swap via admin portal → AI Settings → paste new key → Save
  - Or directly: encrypt with `backend/.env`'s `ENCRYPTION_KEY` and `UPDATE ai_config SET api_key_encrypted = '…', api_key_iv = '…'`
- [ ] **Firebase Admin private key** (`c5245aa85c…`)
  - Delete: https://console.firebase.google.com/project/sehat-a4c23/settings/serviceaccounts/adminsdk
  - Generate new private key, download JSON
  - Replace `backend/secrets/firebase-admin.json` (file mode 0600)
  - Restart backend; on boot you should see `FCM (HTTP v1) configured for project sehat-a4c23` in the log

### 2. Set real `ENCRYPTION_KEY` in production

Today the backend falls back to a publicly-visible default in
`backend/src/config/app.config.ts`. **Every encrypted secret in
`ai_config` and `notification_config` is unwrappable by anyone with the
source.**

```bash
# Generate
openssl rand -hex 32

# Set in production backend/.env
ENCRYPTION_KEY=<the 64-hex-char output>

# Then re-PATCH every encrypted key via the admin endpoints so they
# re-encrypt under the new key:
#  POST /api/v2/ai/config              (paste LLM key + embedding key)
#  POST /api/v2/notifications/config   (paste Twilio + SMTP + FCM legacy)
```

### 3. Wire Firebase client config in Flutter so mobile push works

Backend FCM is ready. Mobile app just needs the Firebase client setup:

- [ ] Register Android + iOS apps in Firebase console for project `sehat-a4c23`
  - Android package name: check `mobile/android/app/build.gradle` `applicationId`
  - iOS bundle id: check `mobile/ios/Runner/Info.plist` `CFBundleIdentifier`
- [ ] Download config files:
  - `google-services.json` → `mobile/android/app/google-services.json`
  - `GoogleService-Info.plist` → `mobile/ios/Runner/GoogleService-Info.plist`
- [ ] Generate `firebase_options.dart`:
  ```bash
  cd mobile
  dart pub global activate flutterfire_cli
  flutterfire configure --project=sehat-a4c23 --platforms=ios,android \
    --out=lib/firebase_options.dart
  ```
- [ ] Update `mobile/lib/main.dart` to add before `runApp`:
  ```dart
  import 'firebase_options.dart';
  // inside main():
  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
  ```
- [ ] **iOS only**: upload APNS auth key to Firebase console → Project Settings → Cloud Messaging → Apple app config → APNS

Once these land, `PushService` auto-inits, requests notification
permission, fetches the FCM token, POSTs it to `/notifications/devices`
— and the coach jobs deliver as native OS notifications.

---

## 🟡 Polish / nice-to-have

### 4. Get a `.riv` Rive asset designed

Both web and Flutter currently fall back to a custom-painted brand-coral
face. With a real Rive file the avatar will feel more alive.

- Drop at `web/apps/patient-portal/public/brand/sehat-coach.riv` (web Rive integration is documented in `sehat-avatar.tsx` header but currently SVG-only)
- Drop at `mobile/assets/rive/sehat_coach.riv` (declare under `flutter.assets:` in pubspec)
- State machine must be named `State` with a numeric input named `state`:
  - 0 = idle (slow breath)
  - 1 = listening (outer ring ripples)
  - 2 = thinking (dots bounce, mouth flattens)
  - 3 = speaking (gentle nod + faster glow)

### 5. Doctor-coach scheduled jobs

Same `@Cron` pattern in a new `DoctorCoachJobsService`:
- "Tomorrow's panel summary" sent Sunday evening for Monday's
  appointments
- "Patient prep brief" sent 1 hour before each consultation

### 6. SMS digest channel

Twilio is already wired in `NotificationProvidersService.sendSms`. Could
add a daily SMS digest for low-bandwidth rural-PK users who don't keep
the app open. New `NotificationChannel.SMS_DIGEST` or just opt-in flag
on `health_coach_settings`.

### 7. Coach-aware unread badge

The `/messages` inbox sidebar entry exists; add an unread count on the
nav item so users see new coach messages at a glance.

### 8. Doctor patient prep on appointment open

When a doctor opens a future appointment in the doctor portal,
auto-prefill the coach panel with a 3-bullet patient summary so they
arrive prepared.

---

## What was just shipped (Phases 5 + 6, 2026-05-25)

**Phase 5 (multi-agent parallel)**
- **Doctor-portal coach mount** — backend `doctor-coach` module mirrors
  the patient `health-coach` shape; doctor-portal floating widget
  (single-tab, no settings); migration `1748400000000-DoctorCoachAgent`
  added `doctor_coach` value to `ai_conversations_kind_enum`
- **Patient inbox** at `/messages` route — surfaces every notification
  with `metadata.source === 'sehat_coach'` newest-first
- **Coach-tagged toaster** — `notification-toaster.tsx` recognizes coach
  notifications and renders with the SehatAvatar + coral accent
- **Handoff doc** at `docs/HEALTH_COACH.md` — architecture map,
  configuration, guardrail policy, scheduled jobs, operator runbook
  ("why isn't X working" with the right SQL probes), how to add a new
  seasonal alert

**Phase 6 (multi-agent parallel)**
- **Voice replies** wired on web (`window.speechSynthesis`) + Flutter
  (`flutter_tts: ^4.2.0`). Strips markdown before speaking; avatar state
  driven by TTS lifecycle; interrupts on new send.
- **First-run coach tour** at `components/sehat-coach/coach-tour.tsx`.
  Pulsing highlight follows the floating button's persisted position.
  Dismissed via localStorage `sehat-coach-tour-dismissed-v1`. "Replay
  coach intro" button added to the settings tab.
- **Per-city seasonal alert gating** — `SeasonalAlert.cities?: string[]`
  filters dengue (Karachi/Lahore/Hyderabad/Sindh), smog (Punjab urban),
  pollen (Islamabad/Rawalpindi). Heatstroke/flu/gastro stay everyone.
- **Quiet days** carved into the seasonal calendar (Mar 1-9, Apr 26-30,
  Nov 15-30) so the previously-dead `outOfSeason` branch fires for real.

**Test suite: 59 tests across 3 spec files, all green** (was 49 in Phase 5).

## Quick-reference paths

| What | Where |
|---|---|
| Backend coach module | `backend/src/modules/health-coach/` |
| Backend doctor-coach | `backend/src/modules/doctor-coach/` |
| Patient web UI | `web/apps/patient-portal/components/sehat-coach/` |
| Doctor web UI | `web/apps/doctor-portal/components/sehat-coach/` |
| Flutter UI | `mobile/lib/features/sehat_coach/` |
| Runbook | `docs/HEALTH_COACH.md` |
| Firebase creds (gitignored) | `backend/secrets/firebase-admin.json` |

## Operator quick commands

```bash
# Trigger all three jobs (admin auth required — see runbook for MFA bypass)
curl -X POST -H "Authorization: Bearer <admin token>" \
  http://localhost:3000/api/v2/health-coach/jobs/medication-reminders/run
curl -X POST -H "Authorization: Bearer <admin token>" \
  http://localhost:3000/api/v2/health-coach/jobs/seasonal-alerts/run
curl -X POST -H "Authorization: Bearer <admin token>" \
  http://localhost:3000/api/v2/health-coach/jobs/checkup-nudges/run

# Run the test suite (49 tests)
cd backend && npx jest --testPathPatterns=health-coach

# Restart backend cleanly (after multi-file edits)
cd backend && lsof -ti :3000 | xargs -r kill -9
ps aux | grep "nest start" | grep -v grep | awk '{print $2}' | xargs -r kill -9
rm -rf dist && npm run build && nohup npm run start:prod > /tmp/sehat-backend.log 2>&1 & disown
```

---

#sehat-sahoolat #health-coach #next-steps #2026-Q2 #phase-6-complete
