Description
## Summary
When a body is redeployed (or its Sunshine session drops for any reason), the iPad reconnects Moonlight but the AppState machine re-enters `.selfService` instead of `.streaming`. Because `micRelay.start(bodyHost:)` is only called on the `.streaming` transition in `startStream()`, the mic relay never starts for the new session. Voice is silently absent — no error, no log warning.
## Root cause
`AppState.tick()` only calls `startStream()` from `.idle` or `.selfService` when a server assignment is active. After a Sunshine session drop, the iPad may re-enter `.selfService` (e.g. if it was in a user-initiated self-service stream when the session dropped). The tick loop at line 165 of `AppState.swift` explicitly does NOT interfere with a self-service stream: `"Tick: streaming (self-service) — not interfering"`. So if the app transitions from streaming → selfService (due to the disconnect) and the server assignment is no longer active (because the body cleared it), the mic relay is never restarted.
Confirmed on chunky-turnip-23 (node-b961f1c8) with ipad-head-3, 2026-05-24. Body was redeployed the day before; iPad showed the stream working but Mercator heard no voice.
## Verification
Check head heartbeat: `GET /api/v1/heads/{id}` — `status: self-service` with the stream appearing connected is the tell.
## Workaround (operational)
Restart the stream on the iPad (exit experience + reconnect). When the app goes through `startStream()` again, `micRelay.start()` is called correctly. Runbook and testbook updated with this check as Step 2.
## Fix direction
When the app re-enters `.selfService` after having been in `.streaming` with `enableMicrophone: true`, it should stop the mic relay (already done via `stopStream()` → `micRelay.stop()`) but the fix needs to ensure the next `startStream()` call always restarts the relay — OR the tick loop should detect a live Moonlight session with a body that has `stream_status: streaming` and call `micRelay.start()` if it is not already running.
Alternatively: add a `micRelay.isRunning` check to the tick loop so it self-heals without requiring user intervention.