# Engineering learnings Updated 2026-09-19 from repository source, recorded test evidence, and prior debugging. This is guidance, not a claim that hardware or server checks were rerun today. Read [PROJECT_STATUS.md](PROJECT_STATUS.md) for the current product scope. ## Preserve behavior across UI changes - Observe nested observable objects directly where their data is rendered. Watching only the parent session does not guarantee transcript/state/task updates redraw. - Keep one conversation session across tabs. Server replacement must remain latest-wins and reject late callbacks from a torn-down session. - Keep Settings presentation outside the replaced session's identity. Otherwise saving a changed server can destroy the navigation stack and its success feedback. - Treat normalization as a programmatic edit: trimming a saved URL must not clear its own confirmation. Clear confirmation on the next genuine user edit instead. - A restored transcript is reading history, not a request to jump to the bottom. User scrolling, text selection, and VoiceOver row focus suspend auto-follow; explicit **Latest** restores it. Test the reading position, not just row existence. - Gate Send on transport readiness, reconnect state and pending sends. Retain an unsent draft on failure. Type mode must not enable microphone capture. - A ready room can still have a microphone failure. Show the actual error and its recovery action without disabling the usable text path. These are regression invariants of the current Nearlight implementation, not a reason to restore the removed music UI or the canceled ChatGPT migration. ## Match evidence to the claim - A token/TLS check is not an agent-join test; Docker health is not a Qwen-response test; app installation is not proof of microphone capture or audible output. - Offline UI fixtures must use isolated preferences and deterministic local failure endpoints. Never reset production preferences to make a UI test pass. - Keep live tests opt-in. Record which model/server was used, what was actually asserted, skips, result bundles, and any cleanup of test-created state. - Test repeated lifecycle sequences, not just one successful launch. Repeat runs exposed an intermittent playback timeout that a single green run missed. - After an interrupted tool call, inspect the existing process/result before restarting. A lost tool handle does not imply the underlying test stopped. - When UI snapshots expose no targets, cross-check screenshots and XCTest results; this occurred despite a rendered app and was not sufficient evidence of a crash. - Keep failed-run evidence. A later pass does not explain the earlier failure. Source review and automated large-text checks also do not replace manual VoiceOver or real-device speech/interruption testing. ## Device connection and signing Diagnose the layers separately: USB detection → pairing/trust → developer/signing readiness → install/launch → live voice behavior. An unlocked phone may still be unpaired. Once USB detects it, investigate pairing rather than repeatedly blaming the cable. Query fresh device details; do not reuse an old availability report. The phone was eventually paired and the final Nearlight build installed/launched on September 12. That supersedes the earlier Finder/pairing blocker. Recheck its current availability and provisioning before the next run; the recorded personal provisioning was short-lived. Do not reset privacy settings or erase a device as a routine diagnostic step. ## Project generation and assets `project.yml` is the source for `Voice.xcodeproj`; the scheme is `Voice_iOS`, bundle ID `dev.shane.voice`. Adding an icon file alone is insufficient: the asset catalog must also be included in the resource build phase. The September 12 follow-up commits added the Lightwell icon and regenerated the project to include it. Regenerate deliberately and review generated diffs alongside source changes. ## NAS deployment and historical audio findings - Inspect actual containers, images, mounts and health before recreating anything. A user concern about deletion is a reason to check, not proof of deletion. - Preserve the persistent certificate. A container restart need not rotate it; update the app pin only after verifying an actual certificate change. - Stage clean deployment source separately from a dirty remote checkout. Retain library/configuration mounts and a recoverable previous container. - Record the deployed image and source patch. Cross-repository changes must be published separately, with any push-triggered deployment reviewed first. - Do not print environment files, API keys, or full credential-bearing configuration. Historical only—the embedded player no longer exists in this app: - WebM/Opus library downloads needed cached AAC for the iOS playback path tested. Preserve originals and verify byte ranges/cache behavior separately from conversion. - AVFoundation's resource-loader content type required a UTI, not an HTTP MIME type. - Buffering an entire 6 MB response before returning audio reproduced an AVPlayer timeout. Bounded 256 KB range delivery passed three subsequent live repetitions. These findings do not establish support for every radio format or indefinite stream. See [DEPLOYMENT.md](DEPLOYMENT.md) and [VERIFICATION.md](VERIFICATION.md) for recorded deployment and test history. Do not reintroduce the client player to preserve history.