# Next Session: Implement 4-Command Nav VLM on Panda

## What

Build the "perception on Panda, planning in code, execution on Pi" navigation system. Session 72 proved the 2B VLM can detect objects but can't steer (25/25 cycles were "forward"). This session implements the adversarial-reviewed plan: a panda-nav FastAPI sidecar that asks the VLM "where is it?" instead of "what should I do?", then maps Position×Size to 4 structured commands. The Pi gets a new `/drive/turn` endpoint for closed-loop IMU-assisted turns.

## Plan

**Read the plan first:** `~/.claude/plans/snuggly-rolling-cat.md`

It contains the full implementation, all 18 adversarial review findings (all addressed), state machine, pre-mortem analysis, exact code for all components, and verification steps.

## Key Design Decisions (from adversarial review)

1. **IMU-assisted turns run on the Pi, not Annie** — new `POST /drive/turn` endpoint does closed-loop rotation at 100Hz locally. Counts as 1 rate-limited command (vs 6 WiFi bursts that blew the 30/60s rate limit — CRITICAL fix).
2. **Sonar is REQUIRED, not optional** — sonar_cm is the primary stop mechanism (< 25cm hard stop, < 40cm + MEDIUM size = stop). VLM LARGE is secondary.
3. **Exploration bypasses panda-nav** — keyword containment check (`any(kw in goal.lower() for kw in ("explore", "wander", "look around", "roam"))`) routes to Titan 26B with existing `_ask_nav_combined()`.
4. **Parser Strategy 2 uses word boundaries** — no loose `'NO '` substring (false-positive: "I see NO obstacle BUT target is LEFT" was misclassified as NONE).
5. **Strategy 3 size priority is SMALL > MEDIUM > LARGE** (conservative: ambiguous responses default to "far away" → drive forward, sonar catches the stop).
6. **Search alternates direction** — even=left, odd=right. Avoids 330° worst-case for right-side misses.
7. **Separate HTTP client for panda-nav** — `connect=2.0s, read=5.0s` (not the VLM client's 0.5s connect which caused false fallbacks on startup).
8. **panda-nav `/health` returns 503 unless llama-server verified** — prevents "panda-nav up but VLM dead" silent failure.
9. **`search_rotations` initialized to 0 before loop** — was NameError (CRITICAL fix).
10. **Terminal check (`action in ("goal_reached", "give_up", "stop")`) preserved before ACT** — prevents sonar_override from silently continuing.

## Files to Modify

*In implementation order (dependencies → dependents):*

| # | File | Change |
|---|------|--------|
| 1 | `services/panda-nav/server.py` | **NEW** — FastAPI sidecar: VLM prompt, parser, command mapper, auth, health |
| 2 | `services/panda-nav/requirements.txt` | **NEW** — fastapi, uvicorn, httpx |
| 3 | `services/panda-nav/tests/test_server.py` | **NEW** — 15 tests: parser, mapper, overrides, auth, health |
| 4 | `services/panda-nav/__init__.py` | **NEW** — empty |
| 5 | `services/panda-nav/tests/__init__.py` | **NEW** — empty |
| 6 | `services/turbopi-server/main.py` | **ADD** `POST /drive/turn` — TurnRequest model, closed-loop IMU turn, open-loop fallback |
| 7 | `services/turbopi-server/tests/` | **ADD** 5 tests for /drive/turn |
| 8 | `services/annie-voice/robot_tools.py` | **MODIFY** — `_ask_nav_decide()`, `_get_nav_decide_client()`, THINK+ACT in `handle_navigate_robot()` |
| 9 | `start.sh` | **ADD** `start_panda_nav()`, `stop_panda_nav()`, env vars |
| 10 | `docs/RESOURCE-REGISTRY.md` | **ADD** panda-nav row, changelog |

## Start Command

```
cat ~/.claude/plans/snuggly-rolling-cat.md
```

Then implement the plan using 3 parallel agents + 1 sequential:

```
Agent A: services/panda-nav/ (worktree)
Agent B: services/turbopi-server/main.py — /drive/turn (worktree)  
Agent C: services/annie-voice/robot_tools.py (worktree)
Agent D: start.sh + RESOURCE-REGISTRY + deploy + E2E verify (sequential after A+B+C)
```

All adversarial findings are already addressed in the plan. Do not revert the fixes.

## Verification

1. `cd services/panda-nav && python -m pytest tests/ -v` — 15 tests pass
2. `cd services/turbopi-server && python -m pytest` — /drive/turn tests pass
3. `cd services/annie-voice && python -m pytest tests/ -v` — existing + new tests pass
4. Deploy Pi: `ssh pi "cd ~/workplace/her/her-os && git pull"` + restart turbopi-server
5. Deploy Panda: `ssh panda "cd ~/workplace/her/her-os && git pull"` + install deps + start panda-nav
6. Pi /drive/turn: `curl -X POST Pi:8080/drive/turn -d '{"direction":"left","angle_deg":90,"speed":40}'` → achieves ~90°
7. Panda health: `curl http://192.168.68.57:11436/health` → `{"status": "ok"}`
8. Restart Annie with `NAV_DECIDE_URL` and `NAV_TOKEN`
9. **Goal-seeking**: Red ball 2m away, 30° off-axis → robot corrects with turns, approaches, stops
10. **Search**: No ball → alternating left/right search → give_up after 12 rotations
11. **Explore**: "explore this room" → Titan 26B fallback, works as before
12. **Fallback**: Stop panda-nav → falls back to Titan 26B
13. **Sonar stop**: Ball at 20cm → immediate stop
