First of two phase-D parity plans. Grounded in the existing V2 command
dispatcher (Server/ws/command.go) and the dormant
plugin.Registry.DispatchCommand + host_commands.go — this is not a
green-field design, it's a wiring plan for code that already exists.
Covers:
- Wire format: command_invoke, command_autocomplete, command_reply,
command_autocomplete_result
- Manifest extension: commands[] with option types, default_member_permissions,
contexts, autocomplete flag
- Schema: migrations/016_plugin_commands.sql with a unique name index
so two plugins can't both own /ban
- Code surface: enumerated file-by-file touch list
- Permission model: server-enforces default_member_permissions BEFORE
the plugin is invoked, plugins never get to gate their own commands
- Built-in commands: /me + /shrug ship in-tree as reference handlers
- Concurrency: 3s deadline via context.WithTimeout passed to DispatchCommand
- Failure modes & UX: 6-row table from "unknown command" through panic
auto-disable
- Testing: unit + integration + contract round trip
- Telemetry: 3 new counters + OTel span
- 4-stage rollout, each step independently shippable
- Open questions: bot identity for broadcasts, component v2 reservation,
cross-plugin imports, DM-context handling
Plan #8 (E2EE DMs + DAVE voice) and PHASE_D_PARITY_TODO.md items 2-7
to follow in a subsequent commit.
https://claude.ai/code/session_01UsBsQW2YiA2usk9pnJjAWk
Resolves the modify/delete conflict with dev (which removed
phase-a-foundation.md in a1e8970). The Implementation Status and
Actionable TODOs sections are preserved under docs/ alongside the
other project docs, matching the existing docs/*.md convention.
The original phase-a-foundation.md design brief is gone per dev's
intent; only the post-implementation status and follow-up checklist
survive.
- deployment.md: new Docker section with quick-start, config.yaml notes,
data persistence, upgrade, and LiveKit reference
- livekit-setup.md: new Docker section with .env / livekit.yaml setup,
node_ip explanation, and firewall table; companion process section
retitled for clarity
Soundboard was never implemented — remove USE_SOUNDBOARD permission bit,
rate limiter, protocol entry, admin mockup reference, TODOS entry, and
all related test assertions.
teardownForReconnect only cleaned up audio pipeline and token timer,
leaving manual camera/screenshare MediaStreamTracks capturing
indefinitely after unexpected disconnect. Added stopManualCameraTrack
and stopManualScreenTracks calls before room is nulled, plus store
flag resets so the UI reflects the actual state.
Add 7 new tasks for killing surviving Stryker mutants across
livekitSession, media-visibility, streamPreview, screenShare,
safe-render, roomEventHandlers, and livekitDiagnostics.
Mark Coraza WAF middleware (T-448) as completed.
CRITICAL (5):
- Hub panic recovery now calls h.Stop() after 3 panics (ws/hub.go)
- Ring buffer EventsSince returns non-nil empty slice for current seq (ws/ringbuffer.go)
- PTT event listener stores unsubscribe handle to prevent leak (ptt.ts)
- verifyTotp respects config.allowSelfSigned instead of hardcoding (api.ts)
- ptt_listen_for_key uses spawn_blocking to avoid thread pool starvation (ptt.rs)
HIGH - Server (13):
- TOTP rate-limit checked after body decode; counters reset on success
- TOTP enable returns 409 if already enabled (must disable first)
- Global search pre-computes accessible channel IDs for FTS WHERE clause
- DeleteAccount queries roles by name instead of hard-coded IDs
- BackupToSafe uses absClean in VACUUM INTO
- Voice camera slot uses atomic EnableCameraIfUnderLimit DB method
- readPump snapshots voiceChID before unregister for TOCTOU safety
- Voice join sets state after token send; rollback takes broadcast flag
- Updater download uses probe pattern instead of overflow write
- Webhook checks Authorization header before reading body
- Storage.Save adds fsync and fixes double-close
- Default WS origin denies cross-origin (was: accept all)
HIGH - Client (6):
- WS reconnect uses generation counter to discard stale events
- AudioPipeline uses generation counter against stale worklet callbacks
- Screenshare mute state preserved across reconnect (not full leave)
- handleVoiceToken uses iterative loop instead of unbounded recursion
- store.ts re-entrancy guard with pending update queue
- Notification AudioContext cleaned up on logout
Reviewed by 4 parallel agents across Server Core, Server Realtime,
Client & Tauri, and Security. 55 total findings; 24 CRITICAL+HIGH
fixed here, 31 MEDIUM+LOW tracked in vault backlog (T-265–T-295).
Fix multiple bugs across settings tabs (AppearanceTab theme restoration,
AdvancedTab testability, LogsTab refresh, VoiceAudioTab device listing),
harden embeds/media/attachments with cache validation, add logPersistence
rotation logic, and add 8 new test files with expanded test cases for
existing tests. Brings client test coverage from ~68% to ~72%.
- Add auto-login feature description (lightning bolt toggle, startup flow)
- Add server health with online_users and 15s periodic health checks
- Update sidebar layout: DMs above channels (3-item preview, bubble to
top, unread badge), collapsible member list with persisted state
- Add voice call duration timer to Key Features
- Note accent color is restored on startup (not just settings)
- Add DM authorization critical rule (IsDMParticipant checks)
- Document full 1-on-1 DM implementation with REST endpoints, WebSocket events
- Add unified sidebar architecture replacing 4-column Discord layout
- Document theme system (neon-glow default, custom theme support)
- Add quick-switch server overlay feature (door button)
- Update PROTOCOL.md with dm_create, dm_channel_open/close events
- Add ready payload dm_channels field for initial DM state
- Expand CLAUDE.md key features section with all new implementations
Changes reflect completed session work on sidebar redesign and DM system.
Two specs for the video/screenshare experience:
1. SCREENSHARE-AUDIO.md — system audio capture + per-tile mute
2. VIDEO-FOCUS-MODE.md — Discord-style opt-in viewing with focus layout
The refactored LiveKit session dropped track.attach() for remote audio,
so no <audio> element was created and remote participants were silent.
Also refactors noise suppression to use LiveKit TrackProcessor API,
adds input volume gain node bypass at 100%, and exposes __lkDebug()
on window for DevTools diagnostics.
- BUG-039: switchOutputDevice continues loop on partial failure instead of early return
- BUG-040: clearOnError() prevents stale callback after MainPage destroy
- BUG-041: voice store tests cover localCamera, localScreenshare, setLocalSpeaking
- BUG-042: auth store updateUser tests and UserBar mute/deafen callback tests
- BUG-043: switchInputDevice guards against no active WebRTC session
- BUG-044: replace synchronous confirm() with double-click-to-delete via toast
- BUG-045: isSafeUrl() blocks javascript: URLs in image attachment src
Server:
- Add message search and pinned messages support
- Add admin hub integration and live connection stats
- Update admin test mocks for hub interface
Client:
- Add data-testid attributes to components for E2E testing
- Add window management capabilities (position, size, maximize)
- Add prod E2E test config and script
- Fix CSS imports (use vite bundling instead of HTML link tags)
- Add inline styles to InviteManager overlay for reliability
- Update CHATSERVER.md references from WPF to Tauri
Docs:
- Update quick-start guide