Files
Toju/emergency-fix/02-user-journeys.md
T
myxelium e49b3ec112 chore: dev-stack switches, shared e2e harness, and desktop shell rules
- `LIVE_RELOAD=false npm run dev` keeps the renderer alive across a machine
  suspend; the reload client otherwise destroys the session under test.
- `dev-peer.sh` plus a separate userdata dir runs a second local peer.
- `tools/voice-probe.js` samples peer state and RTP counters from a live
  window, persisting to localStorage so a renderer reload cannot erase it.
- e2e helpers for voice pairs, peer-role election, and a TURN relay.
- Electron single-instance and dev-client-load decisions move into rules
  files with colocated specs.
2026-08-14 03:19:29 +02:00

7.8 KiB
Raw Blame History

02 — User journeys (how it is supposed to feel)

Each journey: steps the user takes, what should happen, failure symptoms, where to dig.


J1 — First-time home login

User steps

  1. Open app → login/register.
  2. Pick (or accept default) signal server.
  3. Register or log in with username/password.
  4. Land on dashboard / saved servers.

Supposed to happen

  • Session token stored for that URL (metoyou.authTokens + credential store).
  • Local user profile scoped in DB; NgRx currentUser set with homeSignalServerUrl.
  • Provision secret created and persisted (Electron safeStorage / web sessionStorage).
  • Signing public key registered on home server when possible.
  • WebSocket connects, identify with home token + clientInstanceId, ready for joins.

Broken looks like

  • Stuck on login; bounce back after “success”.
  • Dashboard with no token → later SESSION_EXPIRED.
  • Later foreign joins always open authorize (missing secret).

Code

  • domains/authentication/ (AuthenticationService, login/register UI)
  • store/users/users.effects.ts (authenticateUser, prepareAuthenticatedUserStorage, ensureHomeProvisionSecret)
  • Feature: agents-docs/features/authentication.md

J2 — Restart app still logged in

User steps

  1. Quit Electron fully; reopen.
  2. Expect same user without typing password.

Supposed to happen

  • Load user from local DB + valid home token (credential store or legacy token fallback).
  • Identify on home (and later foreign) sockets.
  • Opportunistic ensureProvisioned for active endpoints without login UI.

Broken looks like

  • Flash of dashboard then /login.
  • Profile restored but chat/presence dead (“alone”).
  • Foreign rooms immediately open /login?mode=authorize.

Code

  • loadCurrentUser$, hasValidPersistedSession, migrateHomeCredential
  • Lessons: identify legacy token fallback; persisted user still needs token

J3 — Join a community on the same signal server

User steps

  1. Discover or invite → Join.
  2. Open a text channel; send “hello”.
  3. Join a voice channel; talk.

Supposed to happen

  • REST join with bearer; then WS join_server.
  • Receive server_users / user_joined; peer mesh forms.
  • Text: local add + DC chat-message + WS chat_message fallback.
  • Voice: voice_state broadcast; WebRTC offer/answer; same-channel audio routing.

Broken looks like

  • Joined in UI but not in others member lists (identify/join race).
  • Messages only on sender device.
  • Voice tile appears, no audio.

J4 — Join a community on a different signal server (critical)

User steps

  1. Already logged into home Signal A.
  2. Open invite / browser card whose sourceUrl is Signal B.
  3. Join and chat/voice.

Supposed to happen

  1. ensureCredentialForServerUrl(B) → silent provision (or reuse credential).
  2. REST + WS use actor id on B, not home id.
  3. No authorize login page.
  4. Peers on B see actor display name (may show #prefix disambiguation).
  5. Voice/chat use Bs WebSocket for that rooms affinity.

Broken looks like

  • Redirect to /login?mode=authorize&serverId=… while user bar still shows logged in.
  • Join “succeeds” locally but invisible on B.
  • Can see members but WebRTC never connects (initiator/identity mismatch).
  • DMs/calls to that person later miss rings or fork conversations.

Code

  • SignalServerAuthorizeService, SignalServerProvisionerService, room-signaling-connection.ts
  • Story: agents-docs/user-stories/silent-cross-signal-server-auth.md
  • Pack: 04-auth-login-bugs.md, 09-identity-cross-signal.md

J5 — Send a text message (server channel)

User steps

  1. In text channel, type and send.
  2. Peer in same room should see it live; late joiner should catch up after connecting.

Supposed to happen

  • Optimistic local message with stable id (attachments bind to that id).
  • Broadcast on data channel; also relay chat_message on signaling for peers without DC.
  • Edits/deletes primarily P2P (+ account_sync to sibling devices).
  • On peer connect: inventory ↔ sync-batch (up to 20k recent msgs, chunks of 200).

Broken looks like

  • Sender sees it; others dont (no presence / no join / DC+fallback both fail).
  • Others see live but not history (DC inventory never ran).
  • Multi-device: one device has history, another empty (account_sync / identify).
  • Attachments “Waiting for image…” forever (announce vs message ordering).

Pack: 08-messaging-visibility.md


J6 — Join voice in a channel

User steps

  1. Click a voice channel.
  2. Grant mic if prompted.
  3. Hear others; they hear you; speaking indicators; optional camera/screen.

Supposed to happen

  • Leave any previous voice/call first (exclusive).
  • Publish voice_state with channel/server ids + clientInstanceId.
  • Only one device owns mic (others passive; Join = takeover).
  • Peer connections already for chat mesh; mic tracks attached only to same-channel peers.
  • Playback only for peers in same voice channel.

Broken looks like

  • “Connecting” forever.
  • One-way audio.
  • UI shows peers in channel but silent.
  • After network blip: forever dead until full app restart.
  • Works same-home, fails cross-home (initiator uses home id vs peer actor id).

Pack: 06-voice-webrtc-bugs.md


J7 — Call someone from DM / people card

User steps

  1. Open DM or people card → Call.
  2. Callee hears ring / sees modal (unless DND).
  3. Answer → private call UI with optional chat panel.

Supposed to happen

  • direct-call event delivered via PeerDelivery (DC then signaling).
  • targetUserId = callees currently connected signal identity.
  • Callee admission checks all local aliases (home + every provisioned actor id).
  • Caller never sits “In Voice” if ring could not be delivered.

Broken looks like

  • Caller In Voice; callee silent (outbound route null / wrong id).
  • Callee never notified (inbound alias filter — partially fixed).
  • Cross-signal: two DM threads; replies land in the “wrong” empty one.

Pack: 09-identity-cross-signal.md


J8 — Network blip / signal server restart

User steps

  1. In voice + chat; WiFi blips or signal process restarts.
  2. Continue without manual reconnect.

Supposed to happen

  • WS reconnect with backoff; health probe forces fresh socket on instance change.
  • reIdentifyAndRejoin then room resync.
  • Peer disconnect grace 10s; then reconnect loop (~12 × 5s).
  • DC close triggers repair; chat fallback covers live text meanwhile.
  • Voice presence clears on dead voice-active disconnect server-side.

Broken looks like

  • Zombie “online” with no events.
  • Give-up after ~60s with no error UI.
  • Docs promise soft DC replace; code tears down full peer (audio drop).
  • Identify skipped → alone forever until manual leave/rejoin.

Pack: 05-signaling-multi-server.md, 07-data-channel-drops.md


J9 — Two devices, same account

User steps

  1. Desktop in voice; phone/browser also logged in.
  2. Second device shows “in voice on another device”; can Takeover.
  3. Chat/history appears on both.

Supposed to happen

  • Distinct clientInstanceId per tab (sessionStorage).
  • Broadcasts reach sibling connections; account_sync for owned state.
  • Voice exclusive; takeover yields mic on old owner.

Broken looks like

  • Tabs evict each other (shared clientInstanceId in localStorage — lesson says use sessionStorage).
  • Second device never gets chat batches.
  • Both think they own voice / neither transmits.

J10 — Logout

User steps

  1. Title-bar Logout (desktop) or Settings → Logout (mobile).

Supposed to happen

  • Disconnect sockets; clear current user id; reset rooms/users/messages; /login.

Broken looks like

  • Stale credentials for foreign URLs linger and confuse next account (verify credential clear scope when fixing auth).