--- brief: 3 convoy: workspace-chat-foundation role: role-implementer title: Chat workspace UI and left-rail navigation status: blocked priority: P1 slice_dependencies: - brief-1-chat-persistence - brief-2-chat-realtime recommended_model: auto/coding model_tier: standard --- # Brief 3 — `Task-chat-ui-and-navigation.md` ## Goal Expose workspace chat through the existing application shell with accessible channel navigation, durable history/read state, and realtime updates. ## Files Expected scope; confirm conventions against nearby workspace routes and UI components. - `apps/web/app/(app)/[workspaceSlug]/chat/page.tsx` — channel selection/redirect or empty state. - `apps/web/app/(app)/[workspaceSlug]/chat/[channelSlug]/page.tsx` — channel conversation route. - `apps/web/components/layout/icon-rail.tsx` — Chat destination and accessible unread/mention badge. - `apps/web/components/chat/` — channel list, paginated message feed, composer, thread affordance, connection state, and retry UI. - `apps/web/components/providers/` or focused hooks — realtime event reconciliation with tRPC/React Query caches. - Adjacent component/unit tests. ## Required design - Server state is authoritative; realtime events invalidate or reconcile durable query data. Foundation UI supports workspace-context channels only; project/object entry points wait for the context authorization task. Never expose unauthorized titles/previews, mention candidates or counts; purge inaccessible cached conversations on denial/revocation. Mention UI must not promise agent addition/execution before its reviewed membership flow exists. - Ordinary unread activity uses a dot. In this convoy, the numeric badge is computed from persisted message-mention rows above each channel read cursor plus unread direct-channel activity. The later notifications epic adds unified inbox projections without changing the rail contract. - Unread means a channel's highest message sequence is greater than the member's last-read sequence. Opening a channel does not mark unseen content read until the client intentionally advances the visible boundary. - Direct-channel unread activity contributes to the numeric Chat badge; ordinary public/private channel activity contributes only the unread dot unless the user is mentioned. - Optimistic messages and thread replies have deterministic pending, committed, failed, and retry states keyed by the idempotency key. - History and thread history use per-channel sequence cursor pagination and preserve scroll position while loading older messages. - Realtime read-state invalidations reconcile rail/channel badges across the user's devices. - Keyboard and screen-reader behavior cover navigation, unread indicators, composer, threads, and failures. - Use existing Radix/Tailwind primitives and the current right-panel pattern for a minimally complete thread list/reply surface. ## Acceptance - A user can enter Chat from the left rail, switch channels, send/retry messages and thread replies, intentionally advance the read boundary, and reload without losing state. - Realtime messages and replies appear once and per-channel reconnect catch-up converges to server truth. - Rail/channel unread, direct-message, and mention indicators update across devices after durable mark-read operations. - Loading, empty, unauthorized, archived, offline, and failed-send states are handled. - Responsive layout and keyboard navigation work without inaccessible color-only signals. - `pnpm lint && pnpm type-check && pnpm test` pass. ## Prompt Read the workspace layout, icon rail, right panel, UI primitives, tRPC provider, and the APIs/events produced by briefs 1–2. Do not add a separate design system or raw internal fetch calls. Keep notification inbox and agent participation out of this slice.