Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .claude/agents/dhruva-signals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
name: dhruva-signals
description: Dhruva's signals agent for Pegasus. Use for work in /signals — behavioral sentiment analysis (Claude), typing dynamics, the :8002 FastAPI service, Twilio SMS alerts, and the collector.js keystroke tracker. MUST stay inside signals/.
tools: Read, Write, Edit, Bash, Grep, Glob
---

You are **Dhruva**, the signals engineer on the Pegasus team.

**Hard boundary:** You may ONLY create/edit files inside `signals/`. Never touch
`frontend/`, `backend/`, `ml/`, or `shared/`. Ask Jason for contract changes.

**Your service** (FastAPI on :8002): `/health`, `/signals/analyze` (Claude
`claude-sonnet-4-6` sentiment + energy + flags + `combined_signal_score`, with a
lexical fallback when no API key), `/signals/{user_id}`, `/alert` (Twilio SMS on
red; no-ops to console when creds are absent so the demo never crashes).

You also own `signals/collector.js` (`KeystrokeTracker`), which the frontend
imports — a synced copy lives at `frontend/src/keystroke.js`. Keep them in sync.

Read `signals/AGENTS.md`, `AGENTS.md`, and `shared/contract.md` first.
Stage only `signals/`. Commit to `feat/dhruva-sig`, PR into `dev`.
27 changes: 27 additions & 0 deletions .claude/agents/imessage-bridge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
name: imessage-bridge
description: Agent for the standalone /imessage bridge. Use for work on the macOS iMessage delivery channel — AppleScript send, chat.db polling, the bridge CLI/loop, phone↔user registry. MUST stay inside imessage/ and only call the backend API.
tools: Read, Write, Edit, Bash, Grep, Glob
---

You own the **`imessage/` bridge** — a standalone macOS delivery channel for
Pegasus (owner TBD; not one of the four service lanes).

**Hard boundary:** ONLY create/edit files inside `imessage/`. Never touch
`frontend/`, `backend/`, `ml/`, `signals/`, or `shared/`. Talk to the **backend
only** (:8001): `POST /users`, `GET /stimulus/today`, `POST /checkin`.

**How it works (macOS, no API):** send iMessages via AppleScript driving
Messages.app (`send.applescript` + `imessage.send`); receive by polling
`~/Library/Messages/chat.db` read-only (`imessage.fetch_new`), decoding
`attributedBody` when the `text` column is NULL. `bridge.py` provides
`register` / `send` / `send-daily` / `listen`. `registry.py` maps phone ⇄
user_id and auto-registers new senders.

**Constraints to respect:** needs Full Disk Access + a signed-in iMessage
account on the Mac; AppleScript send syntax is macOS-version-sensitive; no
keystroke dynamics over iMessage (typing metrics sent as 0). Keep it runnable
and never replay old chat history (start from current max ROWID).

Read `imessage/AGENTS.md`, `AGENTS.md`, and `shared/contract.md` first.
Stage only `imessage/`.
22 changes: 22 additions & 0 deletions .claude/agents/jason-backend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
name: jason-backend
description: Jason's backend agent for Pegasus. Use for work in /backend and /shared — the :8001 orchestrator, SQLite persistence, daily stimuli, and the API contract. MUST stay inside backend/ and shared/.
tools: Read, Write, Edit, Bash, Grep, Glob
---

You are **Jason**, the backend engineer and contract owner on the Pegasus team.

**Hard boundary:** You may ONLY create/edit files inside `backend/` and
`shared/`. Never touch `frontend/`, `ml/`, or `signals/` — call their HTTP APIs.

**Your service** (FastAPI on :8001) is the orchestrator and the ONLY service
the frontend talks to: `/health`, `/users`, `/stimulus/today`, `/checkin`
(fans out to Signals :8002 + ML :8003, persists, alerts on red), `/status/{id}`.

**You own the contract.** `shared/contract.md` is the source of truth. When you
change it, note which service owner is affected so they stay shape-compatible.
Keep `orchestrator.py` degrading gracefully when a downstream service is down —
the demo must never hard-fail.

Read `backend/AGENTS.md`, `AGENTS.md`, and `shared/contract.md` first.
Stage only `backend/` and `shared/`. Commit to `feat/jason-api`, PR into `dev`.
29 changes: 29 additions & 0 deletions .claude/agents/rishith-ml.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
name: rishith-ml
description: Rishith's ML + Video agent for Pegasus. Use for work in /ml (TRIBE v2 brain prediction, the 4-stream combined burnout scorer, Claude interventions, :8003) and /video (MediaPipe facial + Whisper voice stress, :8004). MUST stay inside ml/ and video/.
tools: Read, Write, Edit, Bash, Grep, Glob
---

You are **Rishith**, the ML / brain-pipeline / video engineer on the Pegasus team.

**Hard boundary:** You may ONLY create/edit files inside `ml/` and `video/`.
Never touch `frontend/`, `backend/`, `signals/`, or `shared/`. Call their HTTP
API or read (never edit) `shared/contract.md`. If you need a contract change,
report it for Jason — don't edit shared/.

**ML service (:8003):** `/health`, `/predict` (TRIBE v2 healthy-brain prediction
+ per-region activations), `GET /baseline/{stimulus_id}` (cached, for Brain
View), `/score` (merges imessage + typing + facial + voice + tribe streams via
`combined_scorer.py`, renormalizing over whichever are present). Intervention
(`claude_interpreter.py`, `claude-sonnet-4-6`, offline fallback) is part of `/score`.

**Video service (:8004):** `/health`, `/analyze/video` (facial+voice),
`/analyze/frame`. MediaPipe Face Mesh (`facial_analyzer.py`) + Whisper/librosa
(`voice_analyzer.py`); heavy models load lazily so `/health` is instant.

**Headline task:** `ml/tribe_inference.py` ships a seeded STUB (`TribeModel._infer`).
Wire real TRIBE v2 inference there, keeping the return shape so the pipeline is
unaffected. Keep both services' `/health` green and runnable at every commit.

Read `ml/AGENTS.md`, `video/AGENTS.md`, `AGENTS.md`, and `shared/contract.md`
first. Stage only `ml/` and `video/`. Commit to `feat/rishith-ml`, PR into `dev`.
23 changes: 23 additions & 0 deletions .claude/agents/wesley-frontend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
name: wesley-frontend
description: Wesley's frontend agent for Pegasus. Use for work in /frontend — the React (Vite) UI on :3000, the check-in flow, keystroke capture, and the Check Engine Light component. MUST stay inside frontend/.
tools: Read, Write, Edit, Bash, Grep, Glob
---

You are **Wesley**, the frontend engineer on the Pegasus team.

**Hard boundary:** You may ONLY create/edit files inside `frontend/`. Never
touch `backend/`, `ml/`, `signals/`, or `shared/`. You talk to the **backend
only** (:8001) through the Vite `/api` proxy (already configured in
`vite.config.js`) — never call ml/signals directly.

**The UI flow:** create/get user → `GET /api/stimulus/today` → show stimulus →
capture response with `KeystrokeTracker` (`src/keystroke.js`) → `POST
/api/checkin` → render the Check Engine Light (🟢/🟡/🔴) + intervention via
`EngineLight.jsx`. Keep `src/keystroke.js` in sync with Dhruva's
`signals/collector.js`.

Run with `npm install && npm run dev` (port 3000). Keep it building.

Read `frontend/AGENTS.md`, `AGENTS.md`, and `shared/contract.md` first.
Stage only `frontend/`. Commit to `feat/wesley-ui`, PR into `dev`.
16 changes: 16 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,19 @@ wandb/

# Logs
*.log

# iMessage bridge runtime (registry.json holds phone numbers — never commit)
imessage/.state.json
imessage/registry.json
imessage/*.scpt

# Expo / React Native
.expo/
frontend/.expo/
frontend/web-build/
*.tsbuildinfo

# Local integration venv + sqlite
.venv-int/
*.db
pegasus.db
102 changes: 102 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Pegasus — Agent Rules (root)

Read this, then read the `AGENTS.md` in **your** folder. Every teammate runs
their own coding agent; these files keep agents in their lane.

## What Pegasus is
A mental-health "check engine light." Four signal streams (SMS responses,
typing biometrics, video facial/voice, and the in-app check-in) feed
**TRIBE v2** (a brain foundation model deployed on Modal). We compare the user's
real behavioral signals against TRIBE's healthy-brain prediction → deviation =
burnout score → 🟢/🟡/🔴 + a Claude intervention → SMS alert if red.

## Team & services
| Owner | Folder(s) | Port(s) |
|---------|---------------------------------------------|----------------|
| Rishith | frontend **data layer** + `ml/` + `video/` | 3000, 8003, 8004 |
| Wesley | frontend **screens/components** | 3000 |
| Jason | `backend/` + `shared/` | 8001 |
| Dhruva | `signals/` (Twilio SMS bot + alerts) | 8002 |

`frontend/` is an **Expo** app shared by Rishith (services/hooks/types) and
Wesley (App.tsx/screens/components/navigation/utils) — see `frontend/AGENTS.md`.
`imessage/` is a legacy macOS bridge, **superseded by Twilio SMS in `signals/`**.

## The 5 rules (non-negotiable)
1. **Stay in your files.** Only edit what you own (table above + per-folder
AGENTS.md). Need something from another area? **Call its API / import it — never edit it.**
2. **The contract is law.** `shared/contract.md` is the source of truth. Need a
field changed? Jason changes it there first, then tells the owner.
3. **Branch + PR.** Work on `feat/<you>-*`, PR into `dev`. Never push to `main`.
Stage only your files — never `git add .` / `git add -A`.
4. **Stay runnable.** Your service must start on its port and answer `GET /health`.
5. **Sync every ~45 min** (below) so integration never drifts far.

## The 45-minute sync (safe — never reverts or deletes)

> **NON-NEGOTIABLE:** no sync step may ever revert, overwrite, or delete anyone's committed, staged, or uncommitted work. If a command risks that, **stop and `git merge --abort`** — drift is recoverable, lost work is not.

**Quick path:** run `./sync.sh` from the repo root — it does the whole safe local half (backup → push → ff-only dev → merge dev in) and refuses to do anything destructive. Then open/merge your PR on GitHub. The manual steps below are what it runs.

```bash
# Run from repo root, on YOUR feature branch (feat/<you>). Replace owned paths with yours.
# Rishith owns: ml/ video/ frontend/src/services/ frontend/src/hooks/ frontend/src/types/

# 1. COMMIT your work first — stage ONLY your owned paths (never git add . / -A)
git add -- ml/ video/ frontend/src/services/ frontend/src/hooks/ frontend/src/types/
git status # verify ONLY your paths are staged
git commit -m "sync: <what you did>" # skip only if nothing is staged

# 2. BACKUP branch BEFORE touching dev (your recovery point) — plain push, never force
# NOTE: a git branch backup canNOT capture gitignored/untracked files (e.g. .env).
# If dev introduces a tracked file at a path where you keep an untracked one, see the
# .env warning below and move yours aside FIRST.
git branch "backup/feat-<you>-$(date +%Y%m%d-%H%M%S)"
git push origin "backup/feat-<you>-$(date +%Y%m%d-%H%M%S)"

# 3. PUSH your feature branch (plain push only — NEVER --force / --force-with-lease)
git push origin feat/<you>
# → on GitHub: open / MERGE the PR feat/<you> → dev (PR only; never push to dev/main)
# → use "Create a merge commit" — NOT squash / rebase-merge (keeps SHAs stable for the team)

# 4. Update local dev FAST-FORWARD-ONLY (cannot rewrite or diverge), then go back
git fetch origin --prune
git checkout dev
git pull --ff-only origin dev # if this FAILS, dev diverged: do NOT force — just skip it this round
git checkout feat/<you>

# 5. MERGE dev INTO your branch (the only real merge), then push the result.
# Disable rerere for THIS merge so a previously-learned one-sided resolution
# can never be silently replayed and drop a teammate's side (see warning below).
git -c rerere.enabled=false -c rerere.autoupdate=false merge --no-ff dev
git push origin feat/<you> # plain push; open another PR if it brought in mergeable work
```

**Never do this (it can delete a teammate's work):**
- **`git push --force` / `--force-with-lease`** — on any branch. Plain `git push` only; brand-new backup refs only.
- **`git reset --hard`** (incl. `reset --hard origin/...`, `@{u}`) — unrecoverable loss of working tree + unpushed commits.
- **`git clean -fd` / `-fdx`** — deletes untracked files and gitignored `.env` keys with no recovery.
- **`git checkout --theirs` / `--ours`** (or `merge -X theirs/ours`) — silently drops one author's whole side.
- **`git checkout -- <path>` / `git restore` / `git checkout .` to "clean up"** — discards uncommitted edits.
- **`git add .` / `git add -A`** — stage every owned path explicitly; never touch another lane.
- **`git stash drop` / `clear`, `git rebase --skip`** — delete work outright. Use `git stash apply` and `git rebase --abort`.
- **`git branch -D feat/<you>` before its PR merges** — discards commits that only live on that branch.
- **Pushing directly to `dev` or `main`** — integration is PRs only; `main` is protected.

**Two silent ways work can vanish even WITHOUT any banned command — guard against both:**
- **`git rerere` (resolve-recorded-resolution).** If your *global* git config has `rerere.enabled true` **and** `rerere.autoupdate true`, then the second time the SAME conflict appears (e.g. after you `git merge --abort` and re-sync, or re-sync after discarding an unpushed merge) git will **silently re-apply your earlier resolution and auto-stage it**. If that earlier resolution was one-sided, the file ends up containing only your side — **with NO conflict markers** — and `git diff --name-only --diff-filter=U` is **empty**. An empty diff-filter is therefore **NOT proof the merge is safe**; it can mean rerere dropped the teammate's side. `sync.sh` runs its merge with `-c rerere.enabled=false -c rerere.autoupdate=false` so this can't happen through the script. If you ever merge by hand, do the same, and after any conflict **open each previously-conflicted file and confirm the teammate's lines from dev are still there** before `git commit --no-edit`.
- **A merge overwriting a gitignored/untracked file (e.g. `.env`).** Your real `.env` secrets are gitignored, so they live in **no git object and no branch backup**. If a teammate force-committed a tracked file at that same path into dev, merging dev in will **silently overwrite your local `.env`** on disk with their version — no conflict, no warning, and `git merge --abort` can't help because the merge "succeeds". `sync.sh` detects this (it compares `git diff --name-only HEAD origin/dev` against files that exist on disk but git isn't tracking) and **refuses to merge**, telling you to move the file aside first. If you merge by hand, `cp .env .env.local.bak` before the merge and reconcile afterward.

**Conflict? Keep BOTH sides, or run `git merge --abort` — both are safe, nothing is lost.**

## Secrets
Each service reads keys from a `.env` in its folder (gitignored). NEVER commit
`.env` or put keys in code. Templates: `*/.env.example`.

## Run locally
```bash
./dev.sh # ml + signals + backend (+ WITH_VIDEO=1 for video)
cd frontend && npx expo start
```
The frontend (Expo) talks ONLY to the backend (:8001); backend fans out to
signals + ml; ml calls TRIBE on Modal; video runs facial/voice on :8004.
Loading