# Routes & how to test (dev)

The front end picks what to render from the URL (see `src/main.tsx`). Vite dev
server runs on `http://localhost:5173`.

| URL | Renders | For |
|---|---|---|
| `/` | Splash → campaign (walks the story graph: cutscene → dialogue → hands off to the board) | Narrative / player flow |
| `/node/<nodeId>` | **Dev/admin teleport** to a story node; a *level* node lands you on that level's board clone | Jumping around the tree while testing |
| `/level/<levelId>` | A level board directly (add `?edit=1` to author) | Board work / editing |
| `/admin` | Admin panel (mysteries, NPCs, assets, graph editor) | Authoring |
| `/?phone=1` | 3D clamshell phone dialer spike | Phone prototype |

## Game state (demo)

A player's whole state is two things on their **playthrough**:
- `playthroughs.current_node_id` — *where* they are (which node; a level node also has `current_level_id`, the board clone).
- `achievements` — *what* they've earned (`(playthrough_id, flag_key, awarded_by_node_id)`).

Normal play resolves position via `GET /api/playthroughs/current`. `/node/:id`
is just the dev override that *writes* `current_node_id` (the `goto` teleport).

## Quick test recipes

**Play the campaign:** open `/` → New Game → click through the intro/briefing → you land on the board.

**Jump straight to a node:** `/node/<id>`. List the current node ids:
```bash
docker exec osint-board-db psql postgres://osint:osint_secret@localhost:5432/osint_dev -At -F' | ' \
  -c "SELECT n.id,n.node_type,n.label FROM osint.story_nodes n JOIN osint.mysteries m ON m.id=n.mystery_id WHERE m.slug='glass-harbor' ORDER BY n.ypos;"
```
(The `level` row is the board.)

**Phone seam (achievements):** `/?phone=1` → dial `55501` (Elias) → voicemail → click **grant elias_number_callable** → the handset glows → dial `55501` again → connects. Voss `55502` stays voicemail; other numbers = unobtainable.

**Reset to a clean slate:** `docker exec osint-board-db psql postgres://osint:osint_secret@localhost:5432/osint_dev -c "TRUNCATE osint.playthroughs CASCADE;"` then reload (playthroughs + achievements are wiped; `/` shows the splash again).

## Dev-only endpoints

Both are gated to `NODE_ENV !== 'production'` (swap for the admin JWT when we want them in a deployed build):
- `POST /api/playthroughs/:id/goto` `{ nodeId }` — teleport (powers `/node/:id`).
- `POST /api/playthroughs/:id/achievements` `{ flagKey }` — grant an achievement (stand-in until the server-side rule engine fires them from play). `GET` the same path lists earned achievements.

Backend changes need a container rebuild: `docker compose -f docker-compose.dev.yml up -d --build app`. The Vite front end is live on 5173.
