feat: consolidate iOS with API and web preserving history
Rebuild & Deploy / rebuild (push) Failing after 23s
Rebuild & Deploy / rebuild (push) Failing after 23s
This commit is contained in:
commit
d735ee72fa
102 files changed
+13077
-258
No files matched your search
@@ -0,0 +1,135 @@
|
||||
# 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.
|
||||
|
||||
## Authorized accounts and transport — 2026-10-01
|
||||
|
||||
- The latest direct request makes Hermes server-managed; do not restore model
|
||||
selectors or `set_model` from old handoffs. Server account auth is distinct from
|
||||
the previously canceled ChatGPT subscription/device login.
|
||||
- Scope credentials to canonical HTTPS origin, and transcripts to origin + immutable
|
||||
user id. Never import the global legacy transcript into an authenticated account.
|
||||
- Disable bootstrap immediately on logout, invalidate/cancel pending authentication,
|
||||
then disconnect before clearing account UI. New account identity resets drafts and
|
||||
task stores; reject stale room transcription/control callbacks during teardown.
|
||||
- Check generation and cancellation immediately after token fetch and before
|
||||
`room.connect`. Serialize normal mic actions with interruption actions and recheck
|
||||
Type mode after awaits; pending Voice requests cannot enable Type-mode capture.
|
||||
- Private IP does not justify plaintext signaling. Use same-origin URLComponents WSS
|
||||
(IPv6 safe). Exact HTTP certificate pinning does not establish the SDK's separate
|
||||
WSS trust. Installed trusted CA and real hardware qualification may be required;
|
||||
never introduce trust-all or plaintext fallback to bypass it.
|
||||
- A transient `/me` outage is not revocation: retain its Keychain bearer but show
|
||||
no unverified account UI. Only 401/403 clears saved authentication. Authentication
|
||||
URLSession is ephemeral with cookies/cache/credential persistence disabled and
|
||||
redirects rejected to protect password/bearer origin confinement.
|
||||
- Never log data-channel payloads or bootstrap response bodies. Keep release SDK
|
||||
logging at error. Offline auth fixtures use memory tokens, isolated preferences
|
||||
and deterministic no-network bootstrap errors.
|
||||
|
||||
## Native certificate diagnosis and square presence — 2026-10-01
|
||||
|
||||
- Match actual leaf and SPKI before changing a pin. Matching source pins do not
|
||||
prove every SDK session uses the delegate; LiveKit owns a private system-trust
|
||||
URLSession for WSS. App HTTPS probe passed physically; do not conflate a401 from
|
||||
a deleted account with TLS failure or recreate unwanted test accounts.
|
||||
- Gate live TLS tests with explicit opt-in and print started/skipped scope; a test
|
||||
returning early is not runtime proof. Regenerate XcodeGen for added test membership.
|
||||
Physical test bundles require a development team override, not just app signing.
|
||||
- Proven device ATS trap: `NSPinnedCAIdentities` can still reject a manually
|
||||
full-trusted self-signed server with -1202/-9807. Same-phone A/B removal of this
|
||||
redundant rule made system HTTPS and actual LiveKit WSS pass. Retain exact app
|
||||
HTTP pin + normal SDK system trust; do not infer validity-length cause from dates.
|
||||
- Certificate profiles require public-only verified data, user installation/full
|
||||
trust, and persistent hosting. Copying a file into an active container is erased
|
||||
at redeploy. Exact nginx alias may expose a public profile from cert mount; never
|
||||
expose the directory containing the private key. Use apple profile MIME/no-store.
|
||||
- Canvas dot matrix is one accessible avatar, not441 focusable LEDs. Pause animation
|
||||
when inactive and freeze audio geometry with Reduce Motion. Primary square remains
|
||||
centered while active size/dynamic type leave space for transcript and controls.
|
||||
|
||||
## 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.
|
||||
Reference in new issue
Block a user