Redesign Hope iOS UI, harden playback, and document project handoff

This commit is contained in:
snowspeeder committed 2026-09-09 20:23:44 -04:00
1 parent 40aaf4fea7
commit 24f95639dc
24 files changed
+1470 -594

No files matched your search

+38
View File
@@ -0,0 +1,38 @@
# NAS deployment — 2026-09-08
The `voice` container on `192.168.86.2` hosts LiveKit, the voice agent,
token service, music API and nginx. The app connects to
`https://192.168.86.2:9178`. Docker uses host networking and an
`unless-stopped` restart policy.
- Image: `hope-voice-api-voice:68f4c46-ios-audio`.
- LLM: **qwen2.5-vl-3b**, through OpenWebUI at `http://192.168.86.2:3005/api`.
- Speech: existing Azure configuration, unchanged.
- Persistent certs, skills and music: `/mnt/user/dev/hope-voice-api` mounts.
- Build source: `/mnt/user/dev/hope-voice-deploy-68f4c46`, a separate clean
archive plus the iOS audio compatibility patch. The pre-existing dirty
backend checkout was not overwritten.
- Local compatibility source: sibling `hope-voice-api/web/media_server.py`;
regression test: `hope-voice-api/tests/test_ios_audio.py`.
The music service retains original WebM files. An explicit `?format=m4a`
request generates and caches AAC audio under `media/files/ios`, serving
byte ranges for iOS playback and seeking. Browser requests remain unchanged.
## Health and rollback
```sh
ssh [email protected] 'docker inspect --format "{{.State.Status}} {{.State.Health.Status}}" voice'
```
The stopped container `voice-before-ios-audio` retains the baseline deployment.
If rollback is needed, first inspect both containers, then stop the current
`voice`, rename it to a unique recovery name, rename `voice-before-ios-audio`
to `voice`, and start it. This restores the baseline without deleting images,
containers or the music library; it does not include iOS AAC conversion.
`scripts/deploy-backend.sh` records the launch configuration and refuses to
replace an existing `voice` container. Run it on the NAS only after building
the named image and explicitly handling the existing container. It reads
credentials on the NAS and never prints them. No source changes were pushed;
future CI deployments must include the compatibility patch to preserve it.
+2 -2
View File
@@ -33,8 +33,8 @@ ensure the server cert pin is current (see README — re-export after any server
## 4. Audible media playback
- [ ] Requires a track in an iOS-decodable format (m4a/aac/mp3) — current library tracks
are webm and will fail with `-11828` until the server serves a supported format.
- [ ] The deployed server now serves cached AAC for library tracks requested with
`?format=m4a`; verify actual speaker/headphone output on hardware.
- [ ] Play a library track: audio is audible, now-playing card shows art/title/status.
- [ ] Pause / ±10 s / stop / seek all work; agent mics off while playing
(`media_state` published), mics back on when stopped.
+139
View File
@@ -0,0 +1,139 @@
# Project status and handoff
Last updated: 2026-09-09.
## 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,
restoring the music/AI server, and end-to-end qualification.
The UI and playback changes are implemented, and the NAS deployment was verified
on September 8. Full app qualification is **not complete**: physical-device tests
and some broader live scenarios remain. This report summarizes prior evidence;
server health and device availability must be rechecked when resuming.
## Completed work
### Native interface and usability
- Reorganized the app into native **Talk, Music, Activity** tabs, sharing one session.
- Replaced the large animated orb with a compact waveform/status presentation;
added a clearer welcome screen, Voice/Type selection before connecting, explicit
start/end controls, microphone meter, and recoverable connection errors.
- Improved transcript readability, timestamps, live updates, copy/selection,
follow-latest scrolling, and explicit Cancel/Clear confirmation.
- Reworked Settings around voice, input mode, model, and connection; server changes
require an explicit validated save. Voice selection persists.
- Improved Music library/search navigation, now-playing controls, seek/volume,
empty/loading/error states, and validated station-entry sheet.
- Improved Activity hierarchy and task detail presentation with an active-task badge.
- Removed forced dark appearance; adopted adaptive system styling, Dynamic Type,
and larger interaction targets, informed by the Impeccable native-iOS guidance.
### Functional fixes
- Observed nested transcript/state/task objects so asynchronous updates render.
- Preserved latest-wins server replacement; clear old media when switching servers.
- Sent standalone media controls and reports to the default server room.
- Refreshed track metadata as downloads become ready; exposed request/decoder errors.
- Loaded artwork through pinned TLS for the NAS self-signed certificate.
- Requested cached AAC (`?format=m4a`) for YouTube library tracks on iOS.
- Corrected AVFoundation's content type to a UTI rather than an HTTP MIME string.
- Changed the pinned media loader to deliver 256 KB byte ranges instead of buffering
an entire song before delivering usable audio, addressing a reproduced timeout.
### Tests and tooling
- Installed XcodeBuildMCP and its skills; used it for simulator build/run/test work.
- Added the `VoiceUITests` target in `project.yml` and regenerated the Xcode project.
- Added isolated DEBUG UI-test preferences and optional fixtures; normal application
preferences are not reset by those UI-test launch arguments.
- Added media-control/error/metadata tests and opt-in real-NAS UI tests.
- Live tests retain failure screenshots/UI hierarchy and attempt to stop music
they initiated even when an assertion fails.
## 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 this handoff. Committing/pushing `voice-ios` 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](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.
- 68 unit tests passed; five offline UI tests passed (navigation/error recovery,
settings persistence/validation, music validation, transcript/task UI, large text).
- Real NAS tests verified Qwen replies, background task completion, reconnect,
library loading, advancing playback time, pause, forward-seek control and stop.
- Qwen/task/reconnect passed three consecutive iterations.
- Music initially failed intermittently. Logs captured an AVPlayer timeout while
buffering a whole 6 MB response. After bounded-range delivery, three consecutive
live playback iterations passed. The earlier isolated agent-join failure remains
unexplained; do not claim all intermittent connection issues are resolved.
- Backend integration: 16 passed, 7 skipped, 2 deselected.
- Backend AAC regression: one passed (conversion, cache reuse, original preservation,
and byte ranges); deployed range request returned HTTP 206.
- The final bounded-range change was verified by focused playback repetitions,
not a new full combined regression run.
Exact result-bundle names and qualifications: [VERIFICATION.md](VERIFICATION.md).
Artifacts live on this Mac under the XcodeBuildMCP workspace, not in Git.
## What remains, in order
1. **Pair the physical iPhone.** Latest check detected it on USB and reported iOS
26.6.1, device ID `BF93D35E-0853-5470-A125-6EB0DB3F17AB`, state `unpaired`.
The user had unlocked it but could not see it in Finder. Last guidance was to
enable iOS devices in Finder's Sidebar settings and accept any accessory/Trust
prompts. Recheck current state rather than repeating old cable assumptions.
2. **Build, sign, install and run on that iPhone.** No successful device install is
recorded. Check Developer Mode, trust, signing team/profile, and LAN access.
3. **Qualify real audio:** microphone capture, TTS audibility, speaker/headphones,
barge-in, media/voice handoff, Siri/phone interruptions, lock/background behavior,
microphone-permission-denied fallback, and network-loss recovery. Some checks
require the user's speech/listening or a real interruption; simulator state tests
do not prove them. Follow [DEVICE_TESTING.md](DEVICE_TESTING.md).
4. **Broader regression:** rerun the full suite after the range-loader change; test
longer playback, seek near EOF, slow network/cancellation and server replacement.
Exercise new downloads, test-owned station add/play/remove, and voice/model
selection against the live agent. Do not delete the user's existing library song.
5. **Final visual/accessibility pass:** inspect all populated/error screens in light
and dark mode, large text and small-device layouts; test VoiceOver and focus.
The existing large-text navigation test is not a full accessibility audit.
6. **Publish the backend compatibility source safely** in the sibling repository
and ensure its normal deployment preserves mounts, certificates and Qwen settings.
7. Update this report with results and remaining limitations. Do not mark the whole
app qualified until the outstanding end-to-end gates have evidence.
## Repository handoff
The iOS work and this report belong to branch `feat/ios-app`, remote `origin`
(`https://git.srmr.xyz/snowspeeder/voice-ios.git`). The requested handoff commit
includes the implementation, tests, deployment notes and startup instructions.
Check Git history/remote status for the final commit and push result.
+77
View File
@@ -0,0 +1,77 @@
# Verification — 2026-09-08
## Scope and implementation
The requested model migration was canceled: Qwen 2.5 remains configured.
The native redesign separates Talk, Music and Activity; simplifies the welcome
and connection controls; supports light/dark appearance and Dynamic Type;
adds recoverable error states; and gives transcript clearing explicit confirmation.
The Impeccable native-iOS guidance informed system controls, hierarchy, readable
spacing and touch targets. Settings, transcripts and task updates are observed
directly so asynchronous changes render promptly.
Music fixes include standalone control requests, current download metadata,
visible failures, pinned artwork, server-change cleanup, cached AAC delivery,
and correct AVFoundation content-type identifiers in the pinned resource loader.
## Evidence
Project: `Voice.xcodeproj`; scheme: `Voice_iOS`; simulator: iPhone 17 Pro.
- 68 unit tests passed, including standalone media controls, retry error clearing,
download metadata, transcript persistence/deduplication, session replacement,
and interruption state handling.
- Five offline UI tests passed: navigation/offline recovery, voice persistence and
URL validation, music search failure/station validation, transcript confirmation
and task details, and accessibility-size navigation.
- Two live UI tests passed together against the NAS at 17:56 UTC: real Qwen
response, background task completion, disconnect/reconnect, library playback
with advancing media time, pause, forward seek and stop.
- Backend integration run: 16 passed, 7 skipped, 2 deselected. This is not a claim
that the skipped or slow integration cases were verified.
- AAC conversion regression: one passed, checking original-file preservation,
AAC output, cache reuse and HTTP byte ranges. Deployed range request returned 206.
XcodeBuildMCP result bundles are under
`~/Library/Developer/XcodeBuildMCP/workspaces/voice-ios-9eb036fdea81/result-bundles/`:
- Unit/offline UI: `test_sim_2026-09-08T11-48-52-164Z_pid87175_79b1a610.xcresult`.
This combined run's two live cases failed after long stalls; its unit/offline
results above passed.
- Latest live pair: `test_sim_2026-09-08T17-56-51-984Z_pid30949_ec504db2.xcresult`.
- Earlier live rerun: `test_sim_2026-09-08T17-46-07-841Z_pid30949_bd804a04.xcresult`
failed agent join and advancing playback. Their intermittent cause has not
been established. The subsequent passing pair did not require a server restart.
## Not yet qualified
Passing simulator tests do not prove audible speaker/headphone output, microphone
capture, barge-in, phone/Siri interruption recovery, background audio, or network
loss recovery on hardware. Follow `DEVICE_TESTING.md` with a physical iPhone on
the LAN. Navigation at large text sizes is tested, not a complete VoiceOver audit.
Live tests use an existing library track; they do not prove new YouTube downloads
or every radio stream format.
## Repeatability follow-up — 18:04 UTC
Three consecutive Qwen/task/reconnect iterations passed. Music passed twice and
failed on the third iteration; runtime logs captured AVPlayer's request timeout
while the TLS loader buffered a whole 6 MB response. The loader now delivers
bounded 256 KB ranges to AVPlayer, retaining cancellation and EOF handling.
After that change, **all three consecutive live playback iterations passed**
in about 13 seconds each. This addresses the observed music buffering failure;
the earlier isolated agent-join failure has not been reproduced or explained.
- Reproduction bundle: `test_sim_2026-09-08T18-00-03-993Z_pid30949_49da9d2c.xcresult`.
- Post-fix playback bundle: `test_sim_2026-09-08T18-04-30-014Z_pid30949_df323119.xcresult`.
- Live tests now attach failure screenshots and UI hierarchy, and attempt to stop
test-initiated music during teardown even after an assertion failure.
The initial hardware check found an unavailable iPhone. A later USB check detected
the phone running iOS 26.6.1, explicitly marked **unpaired** by XcodeBuildMCP.
No successful device install or hardware qualification is recorded. See
`PROJECT_STATUS.md` for the current pairing blocker and next steps.
Deployment and rollback are recorded in `DEPLOYMENT.md`. The NAS image includes
the compatibility patch; the sibling backend source still requires its own commit
and safe deployment workflow. See `PROJECT_STATUS.md` for repository handoff scope.