132 lines
8.3 KiB
Markdown
132 lines
8.3 KiB
Markdown
# 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.
|
|
- 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.
|