Files
hope-voice-api/ios/docs/PROJECT_STATUS.md
T

29 KiB
Raw Blame History

Project status and handoff

Last updated: 2026-10-04 (verified monorepo import; no new live chat/device qualification).

Repository migration

Native source and full original Git ancestry were imported into ios/ of snowspeeder/hope-voice-api master from iOS feature tip 7bd893a. This is now the active development checkout; old sibling paths and separate push guidance below are historical. See migration record for ancestry, build paths, compatibility scope and identified functional follow-ups. Root theme generation now checks the native artifact; obsolete native backend deploy tooling fails closed. Migration merge d735ee7 was pushed and remote ancestry verified; the old repository was then archived using tea api, with a separate GET confirming archived: true. Native relocated tests passed 72 cases with one live-chat skip; backend offline selection passed 147 cases.

Reusable implementation, testing, device and deployment lessons are in ENGINEERING_LEARNINGS.md. September 12 test/install results below are historical evidence, not a September 19 health check.

Unified native Hope chat — 2026-10-03

The explicit unified-parity request supersedes historical no-model-picker notes. Native Chats is now the default authenticated shell, alongside unchanged Talk and Activity access. HTTP text chat uses the same verified account/bearer and pinned, ephemeral, redirect-denying networking; it requires neither LiveKit nor microphone capture. Durable server conversations, search, paging, workspaces, pins, archive, move, rename and confirmed delete are supported. Branch/edit/ regenerate preserve original history. SSE full-message replacements deduplicate monotonic replay IDs, expose progress/approval/stop and explicit resume after interruption. Foreground metadata sync is account-scoped, five-second, cancels on background/logout, and preserves drafts/read position. Text selection, fenced-code copy, headings/lists/links use native rendering (no remote WebView).

The backend-generated semantic Opal/Graphite/Ocean/Sage/Lavender artifact is consumed by native chat; appearance and palette persist through authenticated preferences. Model/thinking controls use only /api/settings/model, never a direct upstream connection. Chat → Talk binds /token to the selected durable conversation after existing room teardown; nil legacy bootstrap stays supported. Keychain origin scope, auth verification/logout sequencing, exact TLS pins and existing voice lifecycle invariants were not weakened.

Advanced UI follows the new CHAT_FEATURES_CONTRACT: capability-gated private Files/Photos uploads (8 MiB client cap; up to eight selected IDs), in-memory text/image/PDF preview, artifacts with revision edit/history/text share, scheduled task create/edit/pause/resume/run/delete and revision-aware memory controls. Uploads do not automatically send. Task execution is server-owned; there is no APNs/push or task output history claim. Images/research show only after explicit server qualification. Memory changes may require a later Hermes session. Connectors and fabricated integration switches are not shown. Advanced data, previews/drafts, replay tasks and exports clear during account teardown.

Offline verification: initial 66 unit + 2 chat UI passed; expanded 67 unit + 10 UI (7 existing voice + 3 chat) passed at /tmp/hope-chat-final-tests.xcresult. Extra HTTP transport coverage then passed 69 unit + 3 chat UI at /tmp/hope-chat-qualified-tests.xcresult. An earlier artifact UI run failed an exact row label assertion (native label includes revision/kind); predicate fixed, failed evidence retained at /tmp/hope-chat-advanced-tests.xcresult. Final current-source run passed 70 unit + 4 chat UI tests, zero failures, /tmp/hope-chat-final-qualified.xcresult and /tmp/hope-chat-final-qualified.log. Retained Chat, Workspaces, Appearance (accessibility XXXL), and Artifact screenshots were exported to /tmp/hope-chat-screenshots/ and visually inspected. Screens use native navigation/forms and readable Opal tint; appearance title truncates at XXXL as standard large-title behavior, controls remain reachable/scrollable. Signed generic iPhone build passed at /tmp/hope-chat-phone-build.log, artifact /tmp/hope-chat-phone-build/Build/Products/Debug-iphoneos/Voice.app. Final current source signed build passed again at /tmp/hope-chat-phone-final-build.log. No installation, production requests, backend deployment or physical speech qualification occurred. Opt-in live TLS cases return without probes in offline suite and are not TLS proof.

Native follow-up: named .md/.json conversation and .md/.txt artifact Transferable file exports preserve bytes and keep no persistent private cache; exports clear on conversation switch/logout. Basic markdown tables render selectable native rows. Polling uses weak account lifetime, teardown/deinit cancellation, and background stream cancellation with foreground replay. Explicit standard-size UI fixtures now distinguish default screenshots from XXXL accessibility fixtures. Follow-up 70 unit + 5 chat UI passed at /tmp/hope-chat-polish-final.xcresult; signed current build passed /tmp/hope-chat-phone-export-final.log. Standard light/dark chat and dark list screenshots exported to /tmp/hope-chat-standard-screenshots/ and inspected. Conversation captures retain the active keyboard; dark list capture shows full shell. Initial new dark UI test failed the combined native picker label and was corrected; failed bundle retained /tmp/hope-chat-polish-tests.xcresult. No hardware installation or live request.

Physical follow-up — 2026-10-03: parent reported installation and ordinary launch of the signed app succeeded around 12:04–12:05. Added ChatLiveTests, deliberately opt-in with protected environment credentials; offline simulator compile/test explicitly skipped one live test, zero failures, at /tmp/hope-chat-live-optin-compile.log. One authorized physical HTTP attempt used native pinned login and a disposable chat (no memory/preferences mutations). It failed, not passed: /tmp/hope-chat-physical-qualified.xcresult reports one failed test, zero passed, "test runner exited with code 0 before finishing"; outer xcodebuild exited 65. Evidence /tmp/hope-chat-physical-qualified.log and /tmp/hope-chat-physical-summary.json. App-host logs also show interactive voice connections and microphone enable while the HTTP probe was running; the probe itself calls no voice API. Concurrent user interaction is possible, not proven causal. Do not claim no audio captured or successful physical chat qualification. Cleanup after host exit is unconfirmed; parent should inspect only admin-owned chat titled Native qualification · disposable, never unrelated user chats. Further native live attempts stopped at parent's request while user uses voice. No test-host UI guard was retained and no subsequent device action performed.

English Dragon HD Omni voice picker — 2026-10-03

User reported missing new voices on iOS. Root cause: native picker used a fixed 12-voice list, and SettingsStore rejected persisted IDs outside that list. Picker now loads authenticated same-origin GET /voices through existing pinned, redirect-rejecting networking, supports name/locale/gender/model search, and shows retry/failure state. Ava Omni remains default; fallback personas now use Omni. Dynamic catalog IDs persist across relaunch; legacy Dragon HD IDs migrate to Omni. Offline UI fixtures do not call the NAS. No login, TLS, microphone or account storage behavior changed. Requires backend /voices route from voice-api commit 6b8d55f; real regional synthesis remains unqualified.

Final simulator unit run passed (60 tests including opt-in TLS cases returning without live probes), /tmp/hope-omni-ios-final-test.log. Signed iPhone build passed, /tmp/hope-omni-phone-final.log. Initial installation failed with CoreDevice connection reset; fresh device check showed paired/available, but the single retry was denied because the iPhone was locked (MobileImageMounterDeviceLocked). After user unlocked it, installation and launch succeeded on the paired iPhone 14 Pro Max at 08:00 local (dev.shane.voice). No live catalog/audio verification implied. No credentials/preferences reset. No live voice/audio qualification claim.

Approved Opal pink redesign — 2026-10-02

User approved the Opal HTML direction with the existing square reactive persona. Native implementation now uses luminous pink-white surfaces, readable plum text, rose actions (#A64270), an account-derived personalized greeting, 184pt welcome / 112pt active Canvas presence, pastel starter prompts, and an inset floating dock. The approved Opal overlapping-speech icon replaces the old glowing capsule. Light/dark semantic roles, Dynamic Type and Reduce Motion remain supported. Sign out moved into the Account menu; authentication, TLS, session lifetime, account-scoped history, draft retention, transcript follow-latest behavior and voice/text readiness gates are unchanged. Starter prompts prepare editable text only: they never send automatically or enable microphone capture.

Verification: simulator and signed generic-iPhone builds passed. Full offline run passed 59 unit + 7 UI tests, /tmp/hope-opal-offline.xcresult; three explicitly opt-in TLS unit cases returned without live probes, not runtime TLS evidence. Independent review identified editor disappearance on clearing a prompt; corrected by keeping the Type draft editor mounted even when empty. Final focused draft-edit / offline recovery run passed 2 UI tests, /tmp/hope-opal-draft-final.xcresult. Final pink idle-persona tint change rebuilt and 59 unit tests passed again, /tmp/hope-opal-final-unit.log. Native light/dark simulator screenshots inspected; light screenshot /Users/shane/dev/hope-voice-api/design-options/opal-native-light.png.

Initial physical install reset its transport, then retry was explicitly denied because the iPhone was locked. After the user unlocked it, final signed build, installation and launch succeeded on the paired iPhone 14 Pro Max at 13:27 local. Artifact /tmp/hope-opal-phone-build/Build/Products/Debug-iphoneos/Voice.app, final build log /tmp/hope-opal-phone-install.log. App data/credentials were preserved. No backend, account data, saved credentials or production preferences were reset. Real microphone capture, audible replies and interruptions were not requalified by this UI work; successful installation is not a live voice-session claim.

Authorized Hermes migration — 2026-10-01

The direct user request supersedes the historical Qwen/client-model direction below. This repository now implements server-account login (/api/auth/login), bearer verification (/api/auth/me), logout (/api/auth/logout), and authorized /token bootstrap. Hermes is server-managed: model settings, pickers and control messages were removed. No backend source, deployment, live endpoint or secrets were touched in this iOS implementation.

  • Bearers live in device-only, unlocked Keychain items scoped to canonical HTTPS origin; passwords are never persisted. Login accepts numeric Unix expiry and ISO8601 expiry, and ready/provisioning accounts.
  • Logout and origin replacement invalidate auth work, cancel bootstrap and disconnect transport before removing the account UI. Each account/root gets fresh drafts/tasks and history scoped to origin + immutable user id; the global legacy voice_history is never imported.
  • Signaling is same-origin WSS /livekit for all hosts, including IPv6/private addresses. Userinfo/query/fragment origins are rejected. There is no plaintext fallback and no trust-all delegate. HTTP retains the existing exact LAN pin; the LiveKit SDK's separate WSS trust may require an installed trusted CA. Self-signed WSS, actual login, multi-user server isolation, microphone capture, audible speech and interruptions require subsequent user hardware qualification.
  • assistant-progress working/completed/failed events display request activity without polluting eventual normal transcripts. Payload logging is removed; SDK log level is error rather than release debug.
  • Offline UI uses fake auth and memory token storage plus isolated preferences; bootstrap fixtures cannot contact a remote server. Live suite is excluded from this validation. project.yml generates sources and an explicit test scheme.

Verification: simulator build passed. Initial offline unit run: 50 passed, 0 failed (5 new focused authentication tests), /tmp/hermes-ios-unit2.xcresult. A scheme-less first test attempt failed before running tests; adding the generated scheme fixed it. Combined offline run: 51 unit + 5 UI passed, 0 failures at /tmp/hermes-ios-offline-final.xcresult. Expanded run added the logout/account-switch privacy UI test, which passed; one existing URL normalization assertion raced the asynchronous save render and failed. It now waits for the saved confirmation before asserting the normalized value. A subsequent accessibility-size Activity empty-state assertion timed out after 5 seconds; its wait now allows 15 seconds. Both failed-run bundles are retained (/tmp/hermes-ios-offline-qualified.xcresult and /tmp/hermes-ios-offline-release.xcresult). Final focused re-verification: 51 unit + 3 UI passed, 0 failures, including accessibility-size navigation, settings persistence/normalization and logout/account-switch transcript/task privacy, /tmp/hermes-ios-focused-final.xcresult. Remaining three offline UI cases passed in both expanded runs. Parent security review identified transient /me outages clearing Keychain prematurely; fixed to clear only 401/403 and added a regression test. Final source: 52 unit passed, 0 failures, /tmp/hermes-ios-unit-final.xcresult. All six offline UI cases have passed across the runs described above; the final three focused cases passed together. No claim of one fully green expanded six-UI run is made. These are client-only simulator claims, not live/deployment/hardware qualification.

Physical iPhone deployment — 2026-10-01

At the user's request, compiled the current Hermes account app with Xcode 26.4 using automatic development signing (Voice_iOS, Debug, generic iOS destination). Build succeeded; artifact /tmp/hope-voice-phone-build/Build/Products/Debug-iphoneos/Voice.app, build log /tmp/hope-voice-phone-build.log. Installed bundle dev.shane.voice on the connected, paired iPhone 14 Pro Max (iOS 26.7.1, Developer Mode enabled). Installation reported success. The first automatic launch was denied because the iPhone was locked (FBSOpenApplicationErrorDomain: Locked). After the user unlocked it, devicectl device process launch succeeded and launched dev.shane.voice. No credentials/preferences were reset, no backend changes, and no physical speech or authenticated WSS qualification is implied by successful installation.

Square presence and certificate onboarding — 2026-10-01

User reported invalid server certificate at sign-in and requested the webapp square presence as the primary native avatar. Replaced Lightwell rendering with an efficient 21×21 Canvas LED matrix, centered 220pt on welcome and 136pt above active transcript; smaller accessibility sizes preserve controls/readability. Mic/listening and agent speech levels drive separate patterns; Reduce Motion freezes motion/audio geometry. Existing semantic theme and single accessible status are preserved.

Actual deployed leaf and SPKI pins match the app. Explicit live native HTTPS probe passed on both simulator and physical iPhone against /api/auth/me (expected401, no credentials). Physical native password POST reached server and returned401 for John Doe; user confirmed deleting this test account, so no account was recreated. No TLS bypass, plaintext fallback, or pin change was made. The reported original login failure was not reproduced by this test; installed-version or transient cause is not conclusively established. Admin full-auth test was interrupted by lock/lost testmanager connection, not claimed successful.

LiveKit's SDK uses an independent system-trust WebSocket session. User approved a public certificate-only installation profile, exact leaf SHA256 07907abde3bb9c685265909927627d9ea4705d7e7b27056eefe06e10b192af46. Profile is served at https://192.168.86.2:9178/hope-server.mobileconfig, requires manual installation and full trust in iPhone Settings. App login links it only for the exact configured LAN host/port, and certificate errors include safe recovery. An initial container-copy download disappeared after another backend deployment; fixed by persistent cert mount and exact nginx alias; verified200/apple profile MIME, private-key path404. Nothing installs trust automatically or weakens TLS checks.

Validation: 19 conversation tests passed (3 new LED tests), simulator screenshots /tmp/hope-led-welcome-ready.png and /tmp/hope-led-history-dark.png. Complete unit suite:58 passed, including two explicit-opt-in live TLS cases skipped in offline run. Actual opted-in physical HTTPS test passed separately. New live tests use environment-only credentials, never fixtures/source/argv. Updated signed phone build passed (/tmp/hope-square-phone-build.log) and app installed/launch succeeded on the iPhone14ProMax (dev.shane.voice, 18:00 local). Physical speech remains user qualification.

Follow-up: user installed profile/full trust but SDK still reported validation cert invalid. Reproduced physically: exact pinned HTTPS passed; system URLSession HTTPS failed -1202/-9807. Controlled A/B removed redundant NSPinnedCAIdentities in XcodeGen (normal iOS trust, HTTPS/WSS and exact HTTP leaf pin remain unchanged). System trust then passed on same phone, establishing ATS CA-only rule conflict, not proven certificate validity-length rejection. Final physical native live test 3 passed: pinned HTTPS, system-trusted HTTPS, and admin login/me/bootstrap → actual LiveKit SDK authenticated WSS connect/disconnect → logout; no microphone enabled. Evidence /tmp/hope-phone-wss-qualified.log. John Doe remains deleted. Signed corrected build passed /tmp/hope-square-tls-fixed-build.log.

Current direction

Keep Qwen 2.5. The user explicitly canceled the earlier request to switch to a ChatGPT subscription/device-token login. Do not revive that migration because an old saved goal mentions it. Current work is the native iOS UI/UX redesign and end-to-end qualification.

2026-09-12: the embedded music player was removed by user decision — the feature is gone from the app (Media tab, Sources/Media/, MediaPanelView, MediaAPI, MediaPlayerController, PinnedMediaResourceLoader, media tests, and the media_state control publish). The backend media API still exists and is untouched. The backend AAC compatibility patch (below) is now irrelevant to the iOS app but remains deployed.

The Nearlight native redesign is implemented across Talk, Activity, Settings, pickers, task details, and connection diagnostics. Current simulator qualification is green, and GPT-6 Astra's final blocker re-verification returned PASS. The final source also produced a signed generic-iOS build, and once the phone reconnected the final build was installed and launched on the paired iPhone. Hardware audio and interruption qualification remains open.

Completed work

Native interface and usability

  • The app now has native Talk and Activity tabs sharing one session; changing tabs never ends a conversation.
  • Implemented Astra's Nearlight direction: an adaptive plum/rosewood semantic palette, restrained rounded typography, shared spacing/radius/motion tokens, and the compact Lightwell aperture as the conversation state signature.
  • Rebuilt Talk around a clear pre-connection welcome, Voice/Type choice, explicit start/end controls, compact active status, microphone meter, persistent Type draft, and a dock that keeps primary actions reachable.
  • Reworked the transcript as a readable shared record with speaker/time metadata, live partials, copy/selection, explicit destructive confirmation, and measured follow-latest behavior. Restored history stays at the top; manual reading, selection, and VoiceOver row focus suspend following until Latest.
  • Made connection and ready-session microphone failures visible without discarding the real error. Permission denial exposes an iPhone Settings recovery action; Type mode remains available.
  • Reworked Settings around voice, mode, model, and connection. Server edits are validated and explicitly saved; normalization confirmation survives session replacement and clears on the next user edit.
  • Improved Activity hierarchy, active-count badge, empty state, state-specific rows, and task detail presentation.
  • Removed forced dark appearance; all redesigned surfaces support light/dark mode, Dynamic Type, Reduce Motion, increased contrast branches, and 44-point-or-larger interaction targets where applicable.

Functional fixes

  • Observed nested transcript, conversation-state, and task objects so asynchronous updates render.
  • Preserved latest-wins server replacement while keeping an open Settings navigation stack and its success feedback intact.
  • Preserved Type-mode microphone privacy, pending-message send gating, clean session teardown, transcript persistence/deduplication, and interruption ordering.
  • All media-related client behavior was removed with the music player on 2026-09-12.

Tests and tooling

  • Follow-up commits added the Lightwell app icon (7ac8a1c) and regenerated the project to include its asset-catalog resource phase (85169d6). The qualification results below predate those follow-ups; do not describe them as a fresh full run of the current HEAD.
  • Installed XcodeBuildMCP and its skills; used it for simulator build/run/test work.
  • The current combined simulator run passed 50 tests, 0 failures, with the opt-in live suite skipped once. Five offline UI scenarios cover recovery, settings validation/persistence and save feedback, transcript/task flows, restored-history reading position, and accessibility-size navigation.
  • A post-focus-change targeted restored-history UI regression passed independently.
  • DEBUG UI fixtures use isolated preferences; ordinary application preferences and the NAS are not modified by offline UI runs.
  • GPT-6 Astra completed the design specification and four verification rounds. Its final scoped review found B1–B3 resolved, N1/N2 preserved, and no concrete remaining release blocker. Accepted visual/adaptive/accessibility polish remains documented as debt, not as exhaustive WCAG or VoiceOver qualification.

Backend deployment and cross-repository dependency

SSH host: [email protected] (Unraid). App URL: https://192.168.86.2:9178.

  • voice image: hope-voice-api-voice:68f4c46-ios-audio, host networking, restart policy unless-stopped. Includes LiveKit, voice agent, token service, media API, and nginx.
  • LLM: qwen2.5-vl-3b through existing OpenWebUI at http://192.168.86.2:3005/api. Existing Azure speech configuration retained.
  • voice, open-webui, and local-vl-models were present and healthy when the user suspected deletion. Do not recreate healthy services unnecessarily.
  • Original library, certificates, and skills were retained under /mnt/user/dev/hope-voice-api; the dirty remote checkout was not overwritten.
  • Separate deployment source: /mnt/user/dev/hope-voice-deploy-68f4c46.
  • Stopped rollback container: voice-before-ios-audio.
  • Backend AAC conversion preserves original WebM files and serves cached AAC with byte-range support. Browser/raw requests retain their original behavior.

Important: the backend patch and its test are in a different local repository: /Users/shane/dev/hope-voice-api/web/media_server.py and /Users/shane/dev/hope-voice-api/tests/test_ios_audio.py. They were still uncommitted at the earlier deployment handoff; their current sibling-repository status has not been rechecked in this documentation update. Committing/pushing the iOS repository does not publish those files. The deployed image contains the patch, but a future backend CI rebuild can lose it unless it is committed and deployed through that repository's workflow. Inspect that workflow before pushing: it may replace the live container.

Launch configuration and rollback details: DEPLOYMENT.md. Do not copy credentials or .env contents into documentation or commits.

Verification performed

Project Voice.xcodeproj, scheme Voice_iOS, iPhone 17 Pro simulator AFDA137E-1657-428E-BF01-E5CD7989772C.

  • Current combined run: 50 passed, 0 failed, 1 live skip in 104.9 seconds. Result bundle: test_sim_2026-09-12T14-03-09-058Z_pid95846_2ca9da7e.xcresult.
  • Post-focus-change restored-history regression: 1 passed, 0 failed. Result bundle: test_sim_2026-09-12T14-05-21-070Z_pid98187_bd3e531a.xcresult.
  • A fresh isolated simulator launch showed restored history top-anchored with Latest visible; the UI regression then tapped Latest and verified the final event became visible.
  • Astra's targeted isolated runtime check verified a padded changed-address save stays in Settings, normalizes the field, shows its checkmark and confirmation, and clears both only after the next user edit. Final verdict: PASS.
  • iPhone 14 Pro Max BF93D35E-0853-5470-A125-6EB0DB3F17AB was paired, signed, installed, trusted, and launched successfully. The final Nearlight build (/tmp/voice-ios-device-derived/Build/Products/Debug-iphoneos/Voice.app, dev.shane.voice) was reinstalled and launched via devicectl after the phone reconnected. Free provisioning expires after seven days; reinstall to re-sign.
  • Historical live NAS runs verified Qwen replies, background task completion, and reconnect. Backend integration remains 16 passed, 7 skipped, 2 deselected.

Exact historical qualifications: VERIFICATION.md. Artifacts live on this Mac under the XcodeBuildMCP workspace, not in Git.

UX reliability refinement — 2026-10-03

Implemented the approved native UX refinement without changing backend contracts: Chats now shows accurate search/workspace/archive empty states and scope, clearer author hierarchy, named selected attachments, and a workspace editor that preserves its draft and reports an in-place failure. Private file preview clears stale data before a new request, identifies the file, and renders loading/error/retry states; unsupported Library accounts no longer appear to load indefinitely. Voice selection now reports a failed live application without overwriting the previous selection. Login preserves an in-memory password only until successful authentication, makes server save feedback explicit, and settings requires a confirmed account-switch warning before changing server. Connection diagnostics now identify token/session setup scope rather than claiming voice qualification.

Voice preference migration normalizes every legacy fallback persona to Dragon HD Omni while preserving safe dynamic English catalog IDs across relaunch. Added offline regressions for workspace result semantics, stale preview cleanup, static and dynamic voice migration. Sol code review initially found confirmation wiring, async-test compilation, fallback migration, and dynamic catalog persistence blockers; all were fixed before final test/build evidence below. Native iPhone build is signed but not installed in this refinement.

Verification scoped to current source: 60 unit tests passed with /tmp/hope-ios-redesign-derived/Logs/Test/Test-Voice_iOS-2026.10.03_18-46-50--0400.xcresult; final confirmation-flow UI regression passed at /tmp/hope-ios-redesign-ui-derived/Logs/Test/Test-Voice_iOS-2026.10.03_18-54-51--0400.xcresult. An earlier complete offline run executed all existing UI tests but predated the final confirmation-test stabilization. A signed generic iPhone Debug build passed at /tmp/hope-ios-redesign-phone/Build/Products/Debug-iphoneos/Voice.app, log /tmp/hope-ios-redesign-phone-build.log. No production request, live voice test, device installation, or physical audio claim is made by this refinement.

What remains, in order

  1. Qualify real audio: microphone capture, TTS audibility, speaker/headphones, barge-in, Siri/phone interruptions, lock/background behavior, microphone-permission-denied fallback, and network-loss recovery. These require the user's speech/listening or a real interruption; simulator state tests do not prove them. Follow DEVICE_TESTING.md.
  2. Broader live regression: exercise slow-network cancellation, network-loss recovery, repeated server replacement, and voice/model selection against the live agent.
  3. Manual accessibility/visual qualification: inspect populated/error screens in light and dark mode, accessibility text sizes, narrow/landscape layouts, and a real VoiceOver focus session. Astra accepted the remaining presentation debt for this personal-app release; that is not proof of exhaustive conformance.
  4. Publish the backend compatibility source safely in the sibling repository and ensure its normal deployment preserves mounts, certificates, and Qwen settings. This is lower priority because the iOS app no longer consumes the media API, but the deployed container still serves it for the web UI.

Repository handoff

The iOS work and this report belong to branch feat/ios-app, remote origin (https://git.srmr.xyz/snowspeeder/hope-voice-ios.git). Keep the Nearlight implementation, tests, physical-device notes, and this handoff together when committing. Check Git history and remote status for the final publish result.