docs: final audit pass — all 17 remaining audits resolved

New findings:
- Service worker EXISTS at src/sw.ts — task #95 just needs notificationclick handler
- Highlight animation EXISTS in layout.css.ts:44-66 — task #81 just wires it to @mentions
- CallControl.toggleSound() EXISTS — task #100 push-to-deafen trivial
- Sanitizer strips <math>/<mml> — task #56 needs sanitizer changes too
- Folds uses vanilla-extract not CSS vars in non-TDS — task #74 needs theme variant
- Cinny cannot inject audio into EC stream — task #88 redesigned as local-only soundboard
- Policy list code: zero existing code, completely additive
- Notification dispatch: only 2 code points — task #12 is 4-line addition
- Upload preview: UploadCardRenderer.tsx:19-98 — task #36 insertion point found
- Room stats cache limited to ~80 events — task #45 must label clearly
- MSC3489/3672 live location: BLOCKED on server
- Profile banner: DROPPED — not in matrix-js-sdk or any Matrix standard

Server checks:
- /.well-known/matrix/support: 404 (needs server-side file creation)
- MSC3489 live location: false → task #64 added to BLOCKED section
- preview_url: requires auth (endpoint exists, works with user token)

Stale [AUDIT REQUIRED] tags scrubbed from P0 section.
All upstream-confirmed items removed or marked clearly.
Architecture reference table expanded with 15 new confirmed facts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-01 14:42:50 -04:00
parent 3a58d4f94d
commit 4223ac85d3
+67 -74
View File
@@ -73,34 +73,50 @@ DM last-message preview, Media gallery, Knock-to-join full UX
| ~~JetBrains Mono not bundled~~ | **Already loaded via Google Fonts CDN** in `index.html:33-35`. Other fonts: use `@fontsource` npm packages |
| ~~Animated backgrounds: add to backgroundImage~~ | **backgroundImage can't have @keyframes** — use CSS `::before` pseudo-element on `<Page>` component with `position:absolute, inset:0, z-index:-1` |
#### Confirmed facts
#### Confirmed facts (all audits complete as of June 2026)
| Finding | Impact |
|---|---|
| `folds AvatarImage` does NOT accept children | Add frame/overlay inside `UserAvatar.tsx` component itself (see correction above) |
| No in-app toast system exists | Toast redesign (#80): build `ToastProvider` + Jotai atom queue; insert after `OverlayContainerProvider` at `App.tsx:65`; use `portalContainer` div from `index.html:101` |
| `useUnverifiedDeviceCount()` hook EXISTS | Task #65 is trivial: `src/app/hooks/useDeviceVerificationStatus.ts:65-106` — call it in `RoomInput.tsx` |
| `folds AvatarImage` does NOT accept children | Add frame/overlay inside `UserAvatar.tsx` itself — optional `frameName` prop |
| No in-app toast system exists | Task #80: build `ToastProvider` + Jotai queue; insert at `App.tsx:65` after `OverlayContainerProvider`; `portalContainer` in `index.html:101` |
| `useUnverifiedDeviceCount()` hook EXISTS | Task #65 is trivial: `src/app/hooks/useDeviceVerificationStatus.ts:65-106` |
| Voice player: `AudioContent.tsx:44-223` | Task #8: `playbackRate` on hidden `<audio>` at line 217 |
| Notification sounds: 2 hardcoded `.ogg` files in `ClientNonUIFeatures.tsx` | Task #22: replace hardcoded paths with `settingsAtom` value |
| Chat backgrounds: `chatBackground.ts`, applied to `<Page>` in `RoomView.tsx:106` | Task #77: animated backgrounds need CSS class + `::before` pseudo-element approach |
| `KeywordMessages.tsx` already implements custom keyword push rules | Task #61: only non-keyword rule types need new UI |
| `StateEventEditor.tsx` in Developer Tools can already edit any state event | Task #69: build user-friendly ACL UI in Permissions tab, not from scratch |
| URL preview: `urlPreview: true`, `encUrlPreview: false` in settings.ts | Task #49: one-line default change + warning string |
| Private read receipts: `ReceiptType.ReadPrivate` in SDK, `markAsRead()` has param | Task #34: trivially simple |
| Right-click room menu: 6 items in `RoomNavItem.tsx:70-220` | Task #102: add Mute-duration submenu using `PopOut` same as Notifications item |
| GIF links render as generic OG preview cards | Task #42: inline GIF embed needs explicit URL pattern detection + `<img>` render |
| Toolbar buttons (in order): Attach, Formatting, Emoji/Sticker, GIF, Location, Voice, Send | Task #43: sequential array in `RoomInput.tsx` after/before props |
| `MessageQuickReactions` already in hover toolbar (4 recent emojis) | Task #92: increase limit, adjust layout position |
| `knockSupported()` utility EXISTS at `matrix.ts:376-391` | Task #58: just need "Request to Join" button in `RoomIntro.tsx:25-119` |
| `CallControl.setMicrophone(bool)` at `CallControl.ts:206-212` | Task #84: call this directly for AFK auto-mute |
| matrix-js-sdk has NO arbitrary profile field methods | Task #62: use `mx.http.authedRequest()` for MSC4133 raw HTTP calls |
| `JumpToTime.tsx` FULLY IMPLEMENTED and wired in `RoomViewHeader.tsx:215` | Task #7: DELETED — already upstream |
| Poll voting already implemented in `PollContent.tsx:189-213` | Task #9: only CREATE UI needed |
| `useRecentEmoji(mx, 4)` provides the quick reaction data | Task #92: same source as full emoji board |
| `getMatrixToRoom()` in `matrix-to.ts` already generates invite URLs | Task #24: just add QR code display to room settings |
| `/.well-known/matrix/support` not configured on server | Task #67: client reads gracefully if present; Jared must CREATE the file server-side |
| MSC4151 report room endpoint returned HTTP 405 on GET → POST-only → EXISTS | Task #59: endpoint is live, just needs POST with JSON body |
| `/timestamp_to_event` returned 401 (needs auth) → EXISTS | Jump to Date already works — task deleted as upstream |
| `useCallSpeakers.ts` uses CSS MutationObserver polling on EC iframe DOM | Task #107: can augment with TDS ring animation on top of existing data |
| Notification sounds: 2 hardcoded `.ogg` files | Task #22: replace paths with `settingsAtom` value |
| Chat backgrounds: `chatBackground.ts`, `<Page>` in `RoomView.tsx:106` | Task #77: animated backgrounds need CSS class + `::before` pseudo-element |
| `KeywordMessages.tsx` already has custom keyword push rules | Task #61: only non-keyword rule types need new UI |
| `StateEventEditor.tsx` in Developer Tools edits any state event | Task #69: build user-friendly ACL UI in Permissions tab |
| URL preview defaults: `urlPreview: true`, `encUrlPreview: false` | Task #49: one-line default change + one warning string |
| Private read receipts: `ReceiptType.ReadPrivate` + `markAsRead()` param exist | Task #34: trivially simple |
| Right-click room menu: 6 items in `RoomNavItem.tsx:70-220` | Task #102: add Mute-duration submenu via `PopOut` |
| GIF links: render as generic OG preview cards, NOT auto-embedded | Task #42: needs explicit URL pattern detection + `<img>` render |
| Toolbar buttons sequential array in `RoomInput.tsx` | Task #43: straightforward — no complex layout system |
| `MessageQuickReactions` already in hover toolbar (4 recent emojis) | Task #92: increase limit + layout tweak |
| `knockSupported()` utility exists at `matrix.ts:376-391` | Task #58: only need "Request to Join" in `RoomIntro.tsx:25-119` |
| `CallControl.setMicrophone(bool)` at `CallControl.ts:206-212` | Task #84: call for AFK auto-mute |
| `CallControl.toggleSound()` at `CallControl.ts:230-251` | Task #100: push-to-deafen — just wire a hotkey to this |
| matrix-js-sdk has NO arbitrary profile field methods | Task #62: use `mx.http.authedRequest()` for MSC4133 |
| `JumpToTime.tsx` FULLY IMPLEMENTED, wired in `RoomViewHeader.tsx:215` | Task #7: DELETED — already upstream |
| Poll voting implemented in `PollContent.tsx:189-213` | Task #9: only CREATE UI needed |
| `getMatrixToRoom()` in `matrix-to.ts` generates invite URLs | Task #24: just add QR code to room settings |
| `/.well-known/matrix/support` returns 404 | Task #67: Jared must CREATE the file; client handles gracefully |
| MSC4151 report room: HTTP 405 on GET = endpoint exists (POST only) | Task #59: endpoint live, just POST with JSON body |
| `/timestamp_to_event` returns 401 = endpoint exists | Task #7 deleted — Jump to Date already upstream |
| `useCallSpeakers.ts` CSS MutationObserver polling | Task #107: TDS ring animation on top of existing data |
| Highlight animation EXISTS in `layout.css.ts:44-66` (2s infinite keyframe) | Task #81: wire to @mention events, make one-shot (0.6s) |
| Cindy CANNOT inject audio into EC call stream | Task #88: redesign as local-only soundboard |
| `toggleSound()` at `CallControl.ts:230-251` mutes EC `<audio>` elements | Task #100: push-to-deafen trivial — hotkey → toggleSound() |
| Folds uses vanilla-extract in non-TDS, NOT CSS custom properties | Task #74: must create new vanilla-extract theme variant dynamically |
| Theme presets need ~50 CSS custom properties each | Task #75: significant design work before coding |
| Sanitizer (`sanitize.ts`) allows table, div, span, a, code, hr | Task #82: LFG HTML card is safe locally; test on Element/FluffyChat |
| Sanitizer STRIPS `<math>`/MathML tags | Task #56: must also modify sanitizer to add math tags |
| Service worker EXISTS at `src/sw.ts` (session + media handling) | Task #95: just add `notificationclick` handler to existing sw.ts |
| No policy list code anywhere in codebase | Task #70: completely additive, zero conflict risk with Draupnir |
| Notification dispatch: 2 points (ClientNonUIFeatures.tsx:96, :165) | Task #12: 4-line addition across 2 functions + useQuietHours() hook |
| Developer Tools has current-state browser, NO history | Task #41: build audit log via /messages API with state event filter |
| Room names not processed (emoji displays fine, truncation may break emoji) | Task #79: simple render improvement; add emoji-aware truncation |
| Upload preview: `UploadCardRenderer.tsx:19-98`, `RoomInput.tsx:608-639` | Task #36: add compression before file enters upload queue |
| Room cache: PAGINATION_LIMIT=80 events, no total count API | Task #45: stats limited to loaded history; must label clearly |
| MSC3489/3672 live location: BOTH false on server | Task #64: BLOCKED |
| Profile banner: NOT in matrix-js-sdk v41.6.0-rc.0, not in MSC4133 yet | Task #91: DROPPED — not a standard Matrix field |
#### Key file quick reference
| What you need | File | Lines |
@@ -202,75 +218,47 @@ Cache the response in component state; no repeated fetches.
### [ ] P0-5 · Rich room topic rendering (MSC3765)
**Spec:** MSC3765, merged Matrix spec v1.15.
**What:** Room topics can now include formatted text via the `m.topic` content block (bold, links, italic). Currently topics render as plain text in the room header. Pipe the `formatted_body` of the `m.room.topic` state event through the existing HTML/Markdown renderer (same renderer used for message bodies).
**[AUDIT REQUIRED]** — Check if `matrix.lotusguild.org` Synapse version supports sending `m.topic` content blocks. Also check if existing room topics on the server have `formatted_body` set. The rendering improvement is worthwhile even if new formatted topics aren't being set yet.
**Where:** Wherever the room topic is displayed in `src/app/features/room/RoomViewHeader.tsx` or similar.
**Complexity:** Low — reuse existing HTML renderer.
**Server status:** Server reports v1.12 — MSC3765 not formally available. However, the `formatted_body` field on `m.room.topic` state events is part of the generic Matrix content structure and can be stored/read regardless of spec version. The rendering improvement is worthwhile to build now: it will activate whenever any room admin sets a formatted topic.
**What:** Pipe the `formatted_body` of the `m.room.topic` state event through the existing `sanitizeCustomHtml()` + `html-react-parser` pipeline (same as message bodies). Fall back to plain `body` if no `formatted_body` present.
**Where:** `src/app/features/room/RoomViewHeader.tsx` — where the topic string is rendered.
**Complexity:** Low — reuse existing HTML renderer pipeline.
---
### [ ] P0-6 · Edit history viewer
**Spec:** CS-API §11.8.2 (stable). Edit history is stored as `m.replace` relation events.
**What:** When a message shows the "edited" label, clicking it opens a popover or small modal listing every prior version of the message with timestamps:
```
Original: "Hello world" — 3:41 PM
Edit 1: "Hello world!" — 3:42 PM
Edit 2: "Hello everyone!" — 3:45 PM (current)
```
Fetch edit history via:
```
GET /_matrix/client/v1/rooms/{roomId}/relations/{eventId}/m.replace
```
**[AUDIT REQUIRED]** — Confirm upstream Cinny does NOT already show edit history on click. If it does, this is upstream and should not be added to our README.
**Where:** `src/app/features/room/message/Message.tsx` — find where "edited" label renders and add click handler.
**Audit result:** Confirmed NOT in upstream Cinny. The "edited" label shows on messages (Message.tsx:1231) but clicking it does nothing. `getEventEdits()` is used internally but no history modal exists.
**What:** Click the "edited" label → popover/modal lists every prior version with timestamps. Fetch via `GET /_matrix/client/v1/rooms/{roomId}/relations/{eventId}/m.replace`. Show each version's body + timestamp. Use folds `Overlay`+`Modal` pattern.
**Where:** `src/app/features/room/message/Message.tsx` — find where "edited" label renders (line ~1231) and add onClick handler that opens the history modal.
**Complexity:** Low — one API call, display list.
---
### [ ] P0-7 · Room preview before joining (MSC3266)
**Spec:** MSC3266, merged Matrix spec v1.15. Synapse supports it.
**What:** When a user follows a `matrix.to` link or is invited to a room they haven't joined, show a preview card:
- Room avatar, name, topic, member count, join rule
- "Join Room" / "Request to Join" (if knock) / "Accept Invite" button
- "Back" option without joining
Uses: `GET /_matrix/client/v1/rooms/{roomId}/summary`
**[AUDIT REQUIRED]** — Check if upstream Cinny already has a room preview screen before joining. Many Matrix clients have this. If Cinny has it, this is upstream and only Lotus-specific styling/improvements are needed.
**[SERVER CHECK]** — Verify `matrix.lotusguild.org` Synapse version supports the `/summary` endpoint.
**Where:** Likely in the routing/navigation layer when a room is selected but not joined.
**Complexity:** Low-medium.
### [BLOCKED] P0-7 · Room preview before joining (MSC3266)
**Spec:** MSC3266, merged Matrix spec v1.15.
**Server check result:** `GET /_matrix/client/v1/rooms/{roomId}/summary` returned **404** on `matrix.lotusguild.org`. Endpoint not available despite Synapse 1.153.0.
**Audit result:** Confirmed NOT in upstream Cinny — no pre-join preview screen exists.
**Status:** BLOCKED until homeserver exposes the `/summary` endpoint. When unblocked: build a preview card (avatar, name, topic, member count, join rule) shown before the user joins, with Join/Request/Accept buttons.
**Complexity:** Low-medium (when unblocked).
---
### [ ] P0-8 · Personal room name overrides (MSC4431)
**Spec:** MSC4431, in review.
**What:** Let a user rename a room locally — visible only to them. Stored in Matrix account data:
```
PUT /_matrix/client/v3/user/{userId}/account_data/m.room_names
Body: { "rooms": { "!roomId:server": "My Custom Name" } }
```
Access via right-click on room in sidebar → "Rename for me…". Show a small edit icon next to locally-renamed rooms. The original room name remains unchanged for all other members.
**[SERVER CHECK]** — MSC4431 is still in review; it uses account data which is universally supported even if the MSC isn't finalized. The account data key name may change when merged.
**Where:** Room nav item context menu, sidebar room list rendering.
**Spec:** MSC4431, still in review.
**Server check:** Uses standard account data (`PUT /user/{userId}/account_data/`) which is universally supported regardless of MSC status. Safe to implement now; use `io.lotus.room_names` as the account data key until MSC4431 is finalized (may need to rename key when merged).
**What:** Right-click room in sidebar → "Rename for me…" → input dialog → saves to account data. Show a small ✏ icon next to locally-renamed rooms. `useRoomName()` hook in `useRoomMeta.ts:19-35` — override its return value when a local rename exists for that roomId.
**Where:** Room nav item context menu (`RoomNavItem.tsx`), sidebar room list via `useRoomName()` hook.
**Complexity:** Low.
---
### [ ] P0-9 · "Back to Latest" button
**What:** A floating pill button that appears at the bottom of the room timeline when the user has scrolled up (away from the live timeline). Displays "↓ Jump to latest" (and shows unread count if applicable, e.g. "↓ 12 new messages"). Clicking scrolls to the live timeline bottom and hides the pill.
**[AUDIT REQUIRED]** — Confirm this does NOT already exist in upstream Cinny. Check `RoomTimeline.tsx` for any existing "scroll to bottom" UI — there is already a "Jump to Unread" chip (confirmed in code exploration); verify this is distinct.
**Where:** `src/app/features/room/RoomTimeline.tsx` — add a `TimelineFloat` element at the bottom, shown conditionally based on scroll position.
**Complexity:** Low.
### [UPSTREAM — REMOVED] P0-9 · "Back to Latest" button
**Audit result:** CONFIRMED UPSTREAM. `handleJumpToLatest()` + floating "Jump to Latest" Chip already exist in `RoomTimeline.tsx:2180-2192`. Task #104 (unread count + animation improvement) remains in the build queue.
---
### [ ] P0-10 · Mark all rooms as read
**What:** A single action that sends read receipts for all rooms with unread counts, clearing every unread badge at once. Accessible via:
- Right-click on the "Home" icon in the sidebar → "Mark all as read"
- Or a button in the Home view header
Iterates all rooms with `room.getUnreadNotificationCount() > 0` and calls `mx.sendReadReceipt(room.getLastActiveTimestamp())` for each.
**[AUDIT REQUIRED]** — Confirm upstream Cinny does NOT already have this. Check sidebar context menus and Home view for any existing "mark all read" action.
**Where:** `src/app/pages/client/sidebar/HomeTab.tsx` context menu or `src/app/pages/client/home/Home.tsx` header.
**Complexity:** Low.
### [UPSTREAM — REMOVED] P0-10 · Mark all rooms as read
**Audit result:** CONFIRMED UPSTREAM. Per-section "Mark as Read" exists at `Home.tsx:73-102` (home rooms) and `DirectTab.tsx:29-61` (DMs). No global all-sections version — but that was agreed not needed.
---
@@ -1050,6 +1038,11 @@ All toggleable manually in Settings → Appearance regardless of date. Respects
These features are confirmed desirable but cannot be built until the listed dependency is resolved.
Check back after each Synapse upgrade — re-run `/matrix/client/versions` and `unstable_features` to see if they've become available.
### [BLOCKED] · Live Location Sharing (MSC3489 + MSC3672)
**Blocked by:** `org.matrix.msc3489 = false` AND `org.matrix.msc3672 = false` on `matrix.lotusguild.org` (confirmed from unstable_features).
**What it would do:** Real-time GPS beacon streaming upgrading the existing static location share.
**Action when unblocked:** Both MSCs must be enabled on the homeserver before any client work.
### [BLOCKED] · Reaction / Relation Redaction (MSC3892)
**Blocked by:** `org.matrix.msc3892` = false on `matrix.lotusguild.org`
**What it would do:** Cleanly remove a reaction without redacting the parent message.