fix: Chats doesn't sync for multi client users

This commit is contained in:
2026-06-11 00:04:49 +02:00
parent d0aff6319d
commit d174536272
16 changed files with 662 additions and 16 deletions

View File

@@ -0,0 +1,90 @@
# Obsidian Bug Tracker — Agent Contract
User-maintained bug reports live outside the repo. Read this file when asked to triage, investigate, or work from the bug backlog.
**Overrides** `agents-docs/AGENT_WORKFLOW.md` §8 (Autonomous Bug Fixing) unless the user explicitly asks you to fix a bug in code.
---
## Location
| Item | Path |
|------|------|
| Bug inbox | `/home/ludde/Nextcloud/Obsidian Vault/Log/Bugs/` |
| Attachments | `…/Bugs/attachments/<Bug title>/` |
| Dashboard | `/home/ludde/Nextcloud/Obsidian Vault/Log/Create bug.md` |
| Template | `/home/ludde/Nextcloud/Obsidian Vault/Log/Templates/Bug Report.md` |
---
## Allowed actions on vault files
Unless the user explicitly asks for more:
1. **Change `status`** in a bug note's YAML frontmatter (`Open``Resolved` or `Closed`).
2. **Move files** (e.g. reorganize notes or attachments when instructed).
Do **not** edit other vault fields or sections (`Investigation`, `Resolution`, description, etc.) unless the user asks.
---
## Allowed reads (unrestricted)
To understand and solve bugs you may read freely:
- All bug notes and attachments under `Log/Bugs/`
- The full MetoYou repo (code, tests, logs, docs)
- Runtime output, test results, and debug artifacts
Investigation findings belong in chat or in repo changes — not in the vault — unless the user asks you to update the note.
---
## Bug note format
Each note is Markdown with YAML frontmatter:
```yaml
---
title: Bug - …
type: bug
status: Open # Open | Resolved | Closed
priority: Low | Medium | High | Critical
severity: Low | Medium | High | Critical
environment:
created: YYYY-MM-DD HH:mm
tags: [bug]
---
```
Body sections: **Description**, **Steps to Reproduce**, **Expected Result**, **Actual Result**, **Logs / Screenshots**, **Investigation**, **Resolution**.
The dashboard (`Create bug.md`) uses Dataview; keep `type: bug` and `status` accurate so counts stay correct.
---
## Workflow
1. List open bugs: `Glob` or `ls` on `…/Log/Bugs/*.md`, filter `status: Open`.
2. Read the note and any linked attachments.
3. Investigate in the repo (read-only toward the vault).
4. Report findings to the user.
5. Only when told to fix: implement in repo (TDD, lint, build per `AGENTS.md`).
6. When a bug is done: update vault `status` to `Resolved` or `Closed` (and move files if the user specifies a convention).
---
## Open bugs (snapshot 2026-06-10)
| Title | Priority | Environment |
|-------|----------|-------------|
| Attachments gets syncronized corrupt | Critical | All major clients |
| Chats doesn't sync for multi client users | High | All |
| No android app icon | High | Android |
| No login screen mobile phone on startup | High | Android, Android Browser |
| Fresh users have the server list in dashboard completely empty until anything searched | High | — |
| Video attachment on android gets sent in the message bubble above with no preview image | High | Android |
| Local files should be remembered by client | High | — |
| Emojis should be user bound not client bound | Medium | All |
Re-scan the folder at session start; this table is not auto-updated.

View File

@@ -25,6 +25,13 @@ Durable rules for AI agents working on this project. Read this file at session s
## Lessons
### Don't bump E2E timeouts for sync flakes - gate on presence and read server logs [testing] [realtime]
- **Trigger:** a multi-client chat-sync E2E flaked on "message not visible" and the first instinct was to raise `toBeVisible` timeouts or add waits; the user correctly rejected this ("it's not a timeout issue").
- **Rule:** when a cross-user E2E assertion flakes, first gate the assertion on an observable precondition (peer visible in the members panel), then diff the signaling-server logs of a passing vs failing run (`joined server`, `user_joined`, `user_left`, `Removing dead connection`) before touching any timeout.
- **Why:** the flake was a server race — `identify` + `join_server` arriving in one TCP segment were processed concurrently, the join was dropped as unauthenticated, and room membership silently vanished; no timeout can fix a message that is never broadcast. Fixed by serializing per-connection message handling in `server/src/websocket/handler.ts`.
- **Example:** failing run showed one `joined server` for Ludde then `user_left` on sibling-client close; passing run showed two. `expectServerPeerVisible(page, displayName)` in `e2e/helpers/multi-device-session.ts` is the presence gate.
### When renaming an Angular route, sweep every navigate/url-match/doc reference [routing]
- **Trigger:** the find-servers route was renamed `/search``/servers` in `app.routes.ts`, but `servers-rail.component.ts` still called `router.navigate(['/search'])` (leave-server) and matched `startsWith('/search')` for the user-bar visibility signal, throwing `NG04002: 'search'` on leave and never showing the user-bar on the discovery page.

View File

@@ -54,6 +54,7 @@ Require `Authorization: Bearer`:
- `oderId` must match the token's user id when provided.
- `clientInstanceId` is a stable per-tab UUID generated by the product client (`metoyou.clientInstanceId` in `sessionStorage`). The signaling server uses it to distinguish multiple WebSocket connections for the same user and to route voice ownership.
- Server responds with `auth_error` or `auth_required` when authentication fails.
- **Per-connection message ordering (invariant):** the server processes WebSocket messages for one connection strictly in arrival order (`handleWebSocketMessage` chains them per connection id). `identify` awaits a DB token lookup, and clients send `identify` + `join_server` back-to-back (often one TCP segment); concurrent handling let the join run mid-identify, get rejected as unauthenticated, and silently drop room membership — that connection then missed all `user_joined` / `chat_message` broadcasts (root cause of "chats don't sync for multi-client users").
## Multi-device sessions
@@ -68,7 +69,8 @@ When the same account is logged in on multiple devices, account-owned data is ke
| Data | Mechanism |
|---|---|
| Server chat messages | Existing `chat_message` relay (connection-scoped broadcast) |
| Server chat messages (live) | `chat_message` signaling relay (connection-scoped broadcast) **plus** `account_sync` `chat-message` / `message-revision` to sibling devices |
| Server chat messages (catch-up) | `account_sync` `chat-sync-batch` pushed when a sibling device comes online (`account_sync_peer_online`) |
| Voice / typing | Existing `voice_state` / `user_typing` relays |
| Saved servers (join/leave) | `account_sync` payload `saved-room-sync` / `saved-room-remove` |
| Profile avatar + card text | `account_sync` `user-avatar-full` + `user-avatar-chunk` |
@@ -81,7 +83,7 @@ Client rules:
- `broadcastMessage()` still fans out over peer data channels; relayable events are **also** wrapped in `account_sync` and sent on the WebSocket.
- The server forwards `account_sync` to every other open connection for the same `oderId` via `notifyOtherConnectionsForOderId`.
- Receivers ignore payloads whose `clientInstanceId` matches the local tab id.
- When a new device identifies, the server notifies existing connections with `account_sync_peer_online`; those devices push a full snapshot (saved rooms, friends, profile, emoji library).
- When a new device identifies, the server notifies existing connections with `account_sync_peer_online`; those devices push a full snapshot (saved rooms, **room message history**, friends, profile, emoji library).
WebSocket envelope: