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,110 @@
|
||||
# 08 — Messaging visibility (“messages not seen”)
|
||||
|
||||
> **User theme:** “I sent a message but they don’t see it / history missing / only some devices have chat.”
|
||||
> **Severity:** P0.
|
||||
|
||||
---
|
||||
|
||||
## How messaging is supposed to work (user view)
|
||||
|
||||
### Server text channels
|
||||
|
||||
- Send in a text channel → everyone currently in that community sees it quickly.
|
||||
- If someone was offline or just joined, they still get recent history after connecting to peers (or from their other logged-in device).
|
||||
- Edits, deletes, reactions converge.
|
||||
- Typing indicators are ephemeral.
|
||||
- The **signal server does not keep a chat log** — history lives on clients.
|
||||
|
||||
### Direct messages
|
||||
|
||||
- 1:1 and group DMs deliver even without a shared community when signaling can reach the peer.
|
||||
- Delivery ticks: queued → sent → delivered → acknowledged (monotonic).
|
||||
- Offline: queue until peer/network returns.
|
||||
|
||||
### Multi-device
|
||||
|
||||
- Second device receives live + catch-up via `account_sync` when siblings are online.
|
||||
|
||||
---
|
||||
|
||||
## How messaging is supposed to work (system)
|
||||
|
||||
| Path | Role |
|
||||
|------|------|
|
||||
| P2P `chat-message` / revisions | Primary live + sync plane |
|
||||
| WS `chat_message` | Narrow **live** fallback to room members |
|
||||
| Inventory / sync-batch | Catch-up on DC (limit 20_000, chunk 200) |
|
||||
| `account_sync` chat batches | Sibling devices |
|
||||
| DM PeerDelivery | DC → signaling → offline queue |
|
||||
|
||||
Feature contract: `agents-docs/features/messaging.md`.
|
||||
Domain: `domains/chat/`, `domains/direct-message/`, `store/messages/`.
|
||||
|
||||
---
|
||||
|
||||
## Failure modes
|
||||
|
||||
### 1 — Invisible membership → no live fallback
|
||||
|
||||
If identify/join failed (`04`/`05`), user is not in server membership → server never broadcasts `chat_message` to them; peers may not offer DC. **Classic “chats don’t sync for multi-client users”** root cause (serialized identify).
|
||||
|
||||
### 2 — Live works, history doesn’t
|
||||
|
||||
DC down: live WS fallback OK; inventory never runs → “they only see new messages after refresh if a peer happens to sync later” / empty history for late joiners.
|
||||
|
||||
### 3 — Sync poll too slow after “clean” cycle
|
||||
|
||||
Fast poll 10s while catching up; **15 min** when clean. A false “clean” leaves long windows without repair.
|
||||
|
||||
### 4 — Cross-signal identity forks DMs
|
||||
|
||||
Incoming DM `conversationId` carries foreign actor id → second empty thread; replies invisible on the thread the user is watching (`09`). Lessons claim canonicalize/merge — **symbols not in tree**.
|
||||
|
||||
### 5 — Recipient alias miss
|
||||
|
||||
DM/call ignored if local admission only checks home id. Inbound DM aliases largely fixed; verify call + any new surfaces.
|
||||
|
||||
### 6 — Attachment-only emptiness
|
||||
|
||||
Message text arrives; image stuck “Waiting…” — announce/bind race (lesson). Looks like “message incomplete / not really received.”
|
||||
|
||||
### 7 — NgRx prune confusion
|
||||
|
||||
Inactive rooms pruned to 100 messages in memory; DB still has more. User switching rooms may think history vanished until reload/sync — document vs bug.
|
||||
|
||||
### 8 — Doc lie on inventory cap
|
||||
|
||||
Chat domain README still says **1000**; code `INVENTORY_LIMIT = 20_000`.
|
||||
|
||||
---
|
||||
|
||||
## Investigation checklist (agents)
|
||||
|
||||
1. Did both users `identify` + `join_server` on the **same** signal URL as the room?
|
||||
2. Is there an open DC between them? (`connectedPeers`, debug metrics)
|
||||
3. Does live send emit both DC and `chat_message`?
|
||||
4. On receive, is `roomId` in saved/current rooms?
|
||||
5. For DM: conversation id aliases; delivery state machine stuck at QUEUED?
|
||||
6. Multi-device: `account_sync_peer_online` fired; batches received?
|
||||
|
||||
---
|
||||
|
||||
## Proof of done
|
||||
|
||||
1. Two clients: send N messages with DC disabled (force) → live still appears via WS.
|
||||
2. Re-enable DC → inventory brings missing history.
|
||||
3. Third client late join → receives recent history from a peer with DB.
|
||||
4. Cross-home users in foreign room: messages visible both ways without authorize prompt.
|
||||
5. Cross-signal DM: single conversation thread; replies visible to both.
|
||||
6. Multi-device: second device gets `chat-sync-batch` after identify.
|
||||
7. Regression covering identify-before-join (presence + chat broadcast).
|
||||
|
||||
---
|
||||
|
||||
## Fix order
|
||||
|
||||
1. Auth + identify (`04`/`05`) — without presence, messaging “fixes” are theater.
|
||||
2. Ensure fallback + DC resync on repair (`07`).
|
||||
3. Identity canonicalize for DMs (`09`).
|
||||
4. Attachment re-queue invariants.
|
||||
5. Correct stale chat README inventory number.
|
||||
Reference in New Issue
Block a user