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.
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
# 09 — Cross-signal identity (calls, DMs, voice routing)
|
||||
|
||||
> **User theme:** “Calls don’t ring / I’m In Voice alone / DMs fork / voice fails only with people on other homes.”
|
||||
> **Severity:** P0.
|
||||
> **Coupling:** Depends on silent provision (`04`) creating foreign actor ids at all.
|
||||
|
||||
---
|
||||
|
||||
## How identity is supposed to work (user view)
|
||||
|
||||
- You have one profile on this device.
|
||||
- People may see slightly different usernames on other signal hosts (suffix / `#prefix` tag) — still you.
|
||||
- Calling or DMing someone you met in any community should reach **them**, not a ghost.
|
||||
- You should never end up with two chat threads that are secretly the same person.
|
||||
- Voice in a shared room should work even if you registered on different home signal servers.
|
||||
|
||||
---
|
||||
|
||||
## How identity is supposed to work (system)
|
||||
|
||||
| Id | Meaning |
|
||||
|----|---------|
|
||||
| Home user id | Local profile / NgRx `User.id` |
|
||||
| Foreign actor id | Credential `userId` for that signal URL |
|
||||
| Peer map key | Usually the `oderId` seen on that signal’s presence |
|
||||
| `targetUserId` on WS relay | Must equal callee’s **connected** `oderId` on that server |
|
||||
| DM `conversationId` | Should canonicalize to one thread per human pair on this device |
|
||||
|
||||
Self-admission for inbound events must accept **all** aliases: home id, entity id, peer id, every valid provisioned credential user id.
|
||||
|
||||
Outbound delivery must pick a **routable** id (one the signaling server can map to an open connection), not merely the home id stored on a people card.
|
||||
|
||||
---
|
||||
|
||||
## What is implemented vs claimed
|
||||
|
||||
| Capability | Status (2026-08-12 tree) |
|
||||
|------------|--------------------------|
|
||||
| Inbound direct-call alias admission + normalize | **Present** — `direct-call-participant-identity.rules.ts` |
|
||||
| DM self-id alias sets | **Present** — `direct-message-identity.rules.ts` (narrower than lesson’s conversation merge) |
|
||||
| Outbound `collectRecipientDeliveryCandidateIds` / `pickRoutableRecipientId` | **Absent** — lesson describes as if shipped |
|
||||
| `DirectCallService.resolveRoutableRecipientId` / `recipientUnreachable` UX | **Absent** |
|
||||
| `resolveDirectConversationId` / `mergeAliasDirectConversations` | **Absent** |
|
||||
| Room join uses `resolveActorUserIdForServer` | **Present** — `room-signaling-connection.ts` |
|
||||
| Initiator election uses per-signal actor id | **Weak / home-biased** — see `06` |
|
||||
|
||||
**Treat LESSONS entries for outbound call routing and DM canonicalize as specifications of unfinished work**, not as completed history.
|
||||
|
||||
---
|
||||
|
||||
## Failure modes
|
||||
|
||||
### 1 — Outbound call: silent “In Voice”
|
||||
|
||||
Caller joins call session; `PeerDeliveryService` cannot resolve signaling peer id (home id ≠ connected actor id); ring never sent or wrong `targetUserId`; delivery result ignored.
|
||||
|
||||
### 2 — Inbound call: dropped ring (historical)
|
||||
|
||||
Fixed for admission aliases; still verify e2e `dm-header-call-ring` across secondary signal registration. Do not assume outbound is fixed because inbound is.
|
||||
|
||||
### 3 — DM thread fork
|
||||
|
||||
Incoming messages keyed by foreign actor conversation id → UI shows empty home-id thread; replies land elsewhere.
|
||||
|
||||
### 4 — Voice peer election / routing alias miss
|
||||
|
||||
Home vs actor mismatch → no PC or one-way audio (`06`).
|
||||
|
||||
### 5 — People search / friends store home ids only
|
||||
|
||||
Cards display home identity; without alias expansion at send time, every cross-signal action is fragile.
|
||||
|
||||
---
|
||||
|
||||
## Key files
|
||||
|
||||
- `domains/direct-call/` (+ `direct-call-participant-identity.rules.ts`)
|
||||
- `domains/direct-message/` (+ `direct-message-identity.rules.ts`, `PeerDeliveryService`)
|
||||
- `domains/authentication/` credential store + `resolveActorUserIdForServer`
|
||||
- `infrastructure/realtime/signaling-transport-handler.ts` (home vs per-URL credentials)
|
||||
- Lessons (aspirational): outbound routing + DM canonicalize sections in `LESSONS.md`
|
||||
- E2E intent: `e2e/tests/voice/dm-header-call-ring.spec.ts` (scope expand if needed)
|
||||
|
||||
---
|
||||
|
||||
## Recommended implementation sequence
|
||||
|
||||
1. **Instrument** one cross-home repro: log home id, actor ids, peer map keys, `targetUserId`, conversation ids.
|
||||
2. **Outbound delivery** — implement lesson APIs for real: collect aliases, pick routable, always attempt send, surface unreachable error (stop fake In Voice).
|
||||
3. **DM canonicalize + merge** — single thread; remap inbound; merge duplicates on load.
|
||||
4. **Voice initiator / routing** — per-signal local actor id (`06`).
|
||||
5. Rewrite lesson examples if names differ so future agents don’t hunt phantom files.
|
||||
|
||||
---
|
||||
|
||||
## Proof of done
|
||||
|
||||
1. User1 home A, User2 home B, meet in room on A: DM-header call rings B’s modal; B answers; both hear audio.
|
||||
2. Same pair: DM replies appear in **one** thread on both clients.
|
||||
3. People-card call when only shared presence is under actor id: still rings or shows **unreachable** (never silent In Voice).
|
||||
4. Unit tests for alias collect/pick and conversation merge.
|
||||
5. E2E cross-signal call + DM (expand scope with user approval for `e2e/`).
|
||||
|
||||
---
|
||||
|
||||
## Interview choices
|
||||
|
||||
| Option | Focus first |
|
||||
|--------|-------------|
|
||||
| **A (recommended)** | Outbound routable id + unreachable UX |
|
||||
| **B** | DM canonicalize/merge first |
|
||||
| **C** | Voice initiator per-signal id first |
|
||||
| **D** | Full identity service refactor (large — avoid in emergency) |
|
||||
Reference in New Issue
Block a user