# Phase 2: UX Audit — View-by-View Code Review **Date:** 2026-03-20 **Auditor:** Production Readiness Automation (Phase 2) **Scope:** All 7 views, sidebar, onboarding, Direction D compliance, emotional assessment **Method:** Source code reading only (no runtime verification) --- ## 2A. View-by-View Scorecard Each view scored 1-5 on 8 quality dimensions. | View | Layout | Empty State | Loading State | Error State | Direction D | Typography | Spacing | Accessibility | **Avg** | |------|--------|-------------|---------------|-------------|-------------|------------|---------|---------------|---------| | Chat | 5 | 5 | 2 | 3 | 4 | 4 | 5 | 4 | **4.0** | | Memory | 4 | 5 | 4 | 2 | 4 | 4 | 4 | 3 | **3.8** | | Events | 4 | 5 | 4 | 2 | 4 | 4 | 4 | 2 | **3.6** | | Capabilities | 5 | 5 | 4 | 4 | 3 | 4 | 5 | 4 | **4.3** | | Cockpit | 5 | 4 | 5 | 5 | 4 | 4 | 5 | 2 | **4.3** | | MissionControl | 4 | 5 | 4 | 4 | 4 | 4 | 4 | 2 | **3.9** | | Settings | 4 | 3 | 3 | 2 | 4 | 4 | 4 | 3 | **3.4** | **Overall View Average: 3.9 / 5.0** --- ### Chat View (avg 4.0) **Files:** `app/src/views/ChatView.tsx`, `packages/ui/src/components/chat/ChatArea.tsx`, `ChatInput.tsx`, `ChatMessage.tsx`, `ToolCard.tsx`, `ApprovalGate.tsx`, `SubAgentProgress.tsx`, `FileDropZone.tsx`, `FeedbackButtons.tsx`, `WorkflowSuggestionCard.tsx` **Layout (5/5):** Clean flex column with tabs, chat area, and input. FileDropZone wraps content. WorkflowSuggestionCard positioned above input. Persona indicator in header. All Tailwind. **Empty State (5/5):** Two-tier empty state is excellent. With workspace context: shows "Workspace Now" block with summary, recent decisions, blockers, open items, threads, memories, suggested prompts, and onboarding hints for new workspaces. Without context: shows branded SVG icon, workspace name, contextual suggestion chips derived from workspace name (research, writing, planning, code, etc.). This is one of the strongest UX patterns in the entire app. **Loading State (2/5):** The loading indicator uses BEM CSS classes (`chat-area__loading-dot`) with NO corresponding CSS definition anywhere in the codebase. These dots will render as invisible/unstyled `` elements. The streaming indicator is effectively broken at the CSS level. **Error State (3/5):** Server command fetch silently falls back to client commands (good graceful degradation). No explicit error UI for chat API failures -- the `isLoading` prop is the only signal. Tool errors are well-handled via ToolCard status states. ApprovalGate has clear error recovery paths. **Direction D (4/5):** Mostly compliant. Uses `bg-card`, `border-border`, `text-foreground`, `text-muted-foreground`, `bg-primary`, `text-primary` consistently. One concern: `FileDropZone` uses `bg-indigo-500/[0.12]` and `border-indigo-500/60` and `text-indigo-500` -- indigo is NOT part of Direction D palette (should use `primary`/amber). ToolCard uses inline style for `justCompleted` transition. ChatMessage uses `text-[#3fb950]` for completed tool group dots (hardcoded green). **Typography (4/5):** `font-mono` on ChatInput textarea and command palette. Prose classes for markdown rendering. Body text at 14px, code at 13px. Good hierarchy. Missing explicit `font-[Inter]` on some elements. **Spacing (5/5):** Consistent use of Tailwind spacing: `px-6 py-4`, `gap-2`, `space-y-4`. Well-structured padding on workspace home block. **Accessibility (4/5):** Chat area has `role="log"`, `aria-label`, `aria-live="polite"`. Messages have `role="article"` with user/agent labels. Trail toggle has `aria-expanded` and descriptive `aria-label`. Copy button has `aria-label`. Command palette has `focus-visible` styles. Missing: keyboard shortcut for file attachment, no `aria-label` on Send button. --- ### Memory View (avg 3.8) **Files:** `app/src/views/MemoryView.tsx`, `packages/ui/src/components/memory/MemoryBrowser.tsx`, `MemorySearch.tsx`, `FrameTimeline.tsx`, `FrameDetail.tsx` **Layout (4/5):** Two-panel split (50/50 timeline + detail). Search bar at top, filters below, stats footer at bottom. Clean structure. The 50/50 split may be suboptimal on narrow windows -- could benefit from responsive breakpoint. **Empty State (5/5):** Shows brain emoji, "No memories yet" heading, and helpful description: "As you chat, important context is automatically saved here." Detail panel shows "Select a frame to view details" when no frame selected. **Loading State (4/5):** Shows "Loading memories..." with `animate-pulse` class. Functional but not a skeleton loader. **Error State (2/5):** No error state handling. If the API call fails, the component will show the empty state (no memories) which is misleading -- user won't know if data failed to load vs. genuinely empty. **Direction D (4/5):** Uses theme tokens throughout. Filter chips use `bg-primary`/`text-primary-foreground`. Stats footer uses `text-muted-foreground`. No hardcoded colors in MemoryBrowser itself. **Typography (4/5):** Good hierarchy with filter chips at `text-xs`, stats at `text-xs`, search area clean. **Spacing (4/5):** Consistent padding `p-2`, `p-3`, `py-1.5`. Minor inconsistency: timeline panel has `p-2` while detail panel has `p-3`. **Accessibility (3/5):** Search input likely has built-in label from MemorySearch component. Filter chips lack `role="group"` and `aria-label`. No keyboard navigation for frame selection. --- ### Events View (avg 3.6) **Files:** `app/src/views/EventsView.tsx`, `packages/ui/src/components/events/EventStream.tsx`, `StepCard.tsx`, `SessionTimeline.tsx` **Layout (4/5):** Two-tab layout (Live Events / Session Replay). Live tab wraps EventStream. Replay tab has session picker dropdown and SessionTimeline. Clean flex column structure. **Empty State (5/5):** EventStream shows clipboard emoji, "No events recorded" heading, descriptive text about what appears there. SessionTimeline shows "No events" for empty sessions. Session picker shows "No sessions found" or "Select a workspace first" as appropriate. **Loading State (4/5):** Shows "Loading sessions..." and "Loading timeline..." text indicators. EventStream does not have its own loading state indicator. **Error State (2/5):** Network errors in session/timeline fetch silently result in empty arrays. User has no way to know if data failed to load. The `try/catch` blocks swallow errors with `// Network error` comments. **Direction D (4/5):** Tab bar uses `bg-secondary`, `text-primary`, `bg-primary/15`. StepCard uses inline `style={{ borderLeftColor: typeColor }}` with dynamically computed colors. Session picker select uses `bg-black/30` -- a hardcoded value that won't adapt to light theme. **Typography (4/5):** `font-mono` on timestamps. Step names are bold. Good hierarchy. **Spacing (4/5):** Consistent `px-3 py-2` for sections. `gap-0.5` for tab buttons is tight but acceptable. **Accessibility (2/5):** Tab buttons lack `role="tab"`, `aria-selected`, `aria-controls` attributes. Live/Replay tabs are plain `