diff --git a/LOTUS_TODO.md b/LOTUS_TODO.md
index 81373970a..eb50cb435 100644
--- a/LOTUS_TODO.md
+++ b/LOTUS_TODO.md
@@ -63,6 +63,129 @@ Built and gate-green; verify per [LOTUS_TESTING.md](./LOTUS_TESTING.md), then gr
## 🔴 Open — Actionable
+### 🔍 Discovery pass (2026-07) — agent-surveyed, TPVR-verified
+
+Three discovery agents swept the fork's own code (`src/`) for correctness / a11y /
+tech-debt items beyond the tracked backlog; every item was then independently
+re-verified (TPVR, 2026-07) against the code + installed SDK. **All 18 are real** —
+`✅` = confirmed as claimed, `◐` = real but the claim was imprecise (correction inline).
+`Sev` = user/maintenance impact, `Eff` = S/M/L. IDs `DP1–DP18`.
+
+**Correctness (user-facing):**
+
+- [ ] **DP1 — Silent slash-command failures (Sev High · Eff S–M) · ✅.** `RoomInput.tsx:542`
+ calls `commandContent.exe(plainText)` (all `exe` are async, several reject) with no
+ `await`/`.catch`, then unconditionally resets the editor as if it succeeded; no global
+ `unhandledrejection` handler exists. `/kick` `/ban` `/invite` `/join` `/leave` `/ignore`
+ `/myroomnick` `/acl` `/delete` etc. fail with **zero** feedback. **TPVR context:** `/kick`
+ & `/ban` also route through `rateLimitedActions` (`utils/matrix.ts:452`) whose `to()`
+ swallows all non-429 errors — so a real fix must surface errors **per-command**, not just
+ await line 542.
+- [ ] **DP2 — Pending invites re-notify on every reload (Sev Med · Eff S) · ✅.**
+ `ClientNonUIFeatures.tsx:202-217` + `usePreviousValue(invites.length, 0)` (ref inits `0`).
+ **TPVR context:** on warm reload the invite atom is populated synchronously from cached
+ `mx.getRooms()` while the client is already `SYNCING`, so the sync-state gate (`:203`)
+ doesn't protect → `notify(N)` + sound fire for pre-existing invites. Fix: seed previous to
+ `invites.length` and/or a first-render ref.
+- [ ] **DP3 — Status cleared on another device is resurrected (Sev Med · Eff S) · ✅.**
+ `Profile.tsx:370` gates on `presence?.status` truthy, so a remote **clear** (falsy) skips
+ both the input reset and the `localStorage` write; `usePresenceUpdater.readStatus()`
+ re-sends the stale `status_msg` on the next heartbeat. Fix: treat `status !== undefined`
+ as an explicit clear + `localStorage.removeItem(STATUS_MSG_KEY)`.
+- [ ] **DP4 — Favourite / Low-priority toggles fail silently (Sev Low–Med · Eff S) · ✅.**
+ `RoomNavItem.tsx:337-351` — `setRoomTag`/`deleteRoomTag` fire-and-forget with no optimistic
+ UI (state derives from the account-data echo), so a rejected write leaves the menu closed,
+ no change, no toast. UX-only, bites on failure.
+- [ ] **DP5 — Soundboard pack hooks go stale on room/space change (Sev Low · Eff S) · ✅.**
+ `useSoundboardPacks.ts:80,103,125` — `useState(() => …)` inits once. **TPVR context:** both
+ consumers usually remount when the room changes so the stale window is narrow, but it's a
+ real footgun. Fix: derive via `useMemo`/resync effect keyed on the room list.
+- [ ] **DP6 — Call-decline event fire-and-forget (Sev Low · Eff S) · ✅.**
+ `CallEmbedProvider.tsx:547` — uncaught `mx.sendEvent(RTCDecline)`; UI dismisses
+ unconditionally (`:553`), so a failed decline looks handled locally while the caller keeps
+ ringing. Content shape verified spec-correct. Fix: best-effort `.catch` + still dismiss.
+
+**a11y / UX / mobile:**
+
+- [ ] **DP7 — Deafen button announces the OPPOSITE action to screen readers (Sev High · Eff S) · ✅.**
+ `call-status/CallControl.tsx:70` — `aria-label={enabled ? 'Undeafen' : 'Deafen'}` is
+ inverted; the tooltip (`:58`) is the **correct** one. When sound is ON, pressing deafens →
+ label should say "Deafen". aria-only fix, no visual risk.
+- [ ] **DP8 — Sound/Video/ScreenShare toggles missing `aria-pressed` (Sev Med · Eff S) · ✅.**
+ Only `MicrophoneButton` (`CallControl.tsx:38`) exposes it; `:52,90,131` omit. **TPVR note:**
+ also pick a consistent pressed-semantics across all four (mic's `aria-pressed={!enabled}`
+ = "pressed means muted" is debatable).
+- [ ] **DP9 — GifPicker drops focus + fixed width overflow (Sev Med · Eff S/M) · ✅ focus / ◐ width.**
+ `GifPicker.tsx:106` `returnFocusOnDeactivate:false` (+ `initialFocus:false`) drops focus to
+ `
` on dismiss — confirmed. **Correction:** `PICKER_WIDTH=312` is ~314px total and only
+ overflows *below* ~314 CSS-px or when PopOut positions near a screen edge (not a flat
+ "≤320px"). Fix: return focus + `min(312px, calc(100vw - 16px))`.
+- [ ] **DP10 — Search-filter "clear" icons: redundant mouse-only affordance (Sev Low–Med · Eff S) · ◐.**
+ `message-search/SearchFilters.tsx:800,826,850` (+ `DateRangeButton:648`) — ``
+ inside a `Chip`(`