# Iteration 01 Request - Task: 20260831-105824-plan-code-ui-audit - Requested by: build - Owner: coder - Status: changes-requested ## Confirmed Defects To Fix 1. `MediaAPI.post` decodes every successful JSON object response as `Data.self`; JSONDecoder only accepts a JSON-encoded Data representation, so `/play`, `/control`, `/report`, and `/streams` mutate the server and then throw `decodeFailed`. Make no-response POSTs validate HTTP success without decoding a body. 2. `MediaPlayerController.reconcile` never assigns `desired`, so the UI always says Stopped, the transport icon is wrong, and interruption resume cannot work. Keep published desired state synchronized with server state. 3. Play/pause/stop/seek do not fully satisfy the documented reporting protocol. Ensure local transport changes promptly report actual playback and publish `media_state` where required, while avoiding obviously stale room/session callbacks. 4. `endConversation` stops media polling but retains a non-nil room, so `MediaPanelView.startDefaultPolling()` cannot restart it. Preserve coherent post-conversation polling/resume behavior. 5. Persisted text mode is not synchronized on join: `agentReadyReceived` always enables the microphone and never sends `set_mode:text`. Join must honor persisted mode, keep the mic disabled in text mode, and synchronize agent mode. 6. The Settings text-mode toggle writes only UserDefaults. During a ready session it changes the visible composer mode without muting/unmuting or notifying the agent. Route it through the same live mode application behavior as ConversationView. 7. A `.failed` session renders a Start button, but `connect()` accepts only `.idle`, making retry a no-op. Allow a user retry from failed state while preserving the one-time agent-timeout retry guard. 8. Persisted model preference can be overwritten by an early server `models.selected` message because `selectedModel` starts nil and sync prefers it over `settings.modelID`. Make persisted preference deterministic and do not let event timing silently replace it before sync. 9. Transcript primary and secondary finals are not deduplicated. `receiveDataTranscript` marks partial rows final and appends another final, and identical primary/data finals can both persist to history. Implement role/text-aware cross-source final dedupe and replace the appropriate open partial rather than creating duplicates. 10. Add focused automated tests for pure/testable regressions above. Create the smallest viable unit-test target in `project.yml` if none exists, regenerate the Xcode project, and cover at least MediaAPI successful object responses, transcript dedupe/replacement, failed-state retry behavior where dependency seams permit, and media desired-state reconciliation where feasible. Do not contort production architecture solely for tests; document anything requiring integration coverage. ## Constraints - Product/source/config/test changes must be made only by coder. - Keep fixes minimal and avoid the broader UI redesign in this iteration. - Do not modify hope-voice-api or hope_companion. - Preserve unrelated worktree changes and do not commit. - Use XcodeBuildMCP per repository instructions for build/test verification; load its skill and show defaults first. - Write the durable report to `implementation.md`, naming actual fixed model, files changed, exact commands/tools and outcomes, and residual risks. ## Acceptance - Simulator build succeeds with zero new warnings. - New tests pass. - Each defect above is fixed or explicitly reported blocked with evidence. - Existing server/device limitations are not misrepresented as client fixes.