13 files reviewed with mission-control design lens. Key additions: - theme: extended indigo scale, 4-level surface hierarchy, 22 terminal tokens, transition/z-index/focus-visible token categories - All screens: keyboard shortcuts, loading/error/empty states hardened - 5 new shared components: StatusDot, SkeletonLoader, Toast, Badge, KeyboardShortcutHint - settings: expanded from 2 to 5 sub-pages (accounts, workspace, danger zone) - review-tab: 3-pane layout, inline comments, file nav, hunk controls - execution-tab: zoom, partial failure state, stale agent detection - dialogs: 2 bugs found (mutation locking, error placement) Total: 4,039 → 9,302 lines (+130% from review pass)
27 KiB
Inbox Page (v2)
Route: /inbox
Source: packages/web/src/routes/inbox.tsx
v1 -> v2 Changes
| Aspect | v1 | v2 |
|---|---|---|
| List error state | None | [AlertCircle] + "Failed to load conversations" + [Retry] |
| Detail error state | None | [AlertCircle] + "Failed to load conversation" + [Retry] |
| Action buttons | 3 buttons: Cancel, Dismiss, Send Answers | 2 buttons: [ Stop Agent ] (destructive, LEFT-isolated) + [ Send Answers ] (primary, RIGHT) |
| Conversation states | No visual distinction | Orange dot = pending, Grey dot = answered, filterable tabs |
| Batch answers | N/A | "Apply to similar" option when multiple agents ask same question type |
| Submit feedback | None | Button shows [spinner] Sending... during mutation |
| Submit error | Toast only | Inline red text below buttons: "Failed to send. Try again." |
| Empty state | Basic text | Centered empty state with description |
Default State (with conversations)
+=============================================================================+
| [CW] Initiatives Agents *3 [*Inbox*] (2) Settings [cmd-k] [sun] * |
+=============================================================================+
| |
| Inbox (2) [ Refresh ] |
| |
| [Pending (2)] [Answered] | Question from blue-fox-7 [<>] |
| --------------------------+ min 400px, resizable |
| +------------------------+| |
| | ! blue-fox-7 2m || Task: Implement OAuth PKCE flow |
| | Auth System Overhaul || Phase: Authentication . 2 minutes ago |
| | BLOCKING || |
| +------------------------+| "Should I use PKCE or implicit flow |
| | ? green-bear-1 5m || for the browser client? The spec |
| | API Redesign || recommends PKCE but the existing |
| +------------------------+| code uses implicit." |
| | |
| | [v] Previous Q&A with blue-fox-7 (1) |
| | |
| | Your Answer [Snooze v] |
| | +--------------------------------------------+|
| | | Use PKCE. We're deprecating implicit ||
| | | flow per OAuth 2.1 security BCP. ||
| | | ||
| | +--------------------------------------------+|
| | Markdown supported. `code`, **bold**, ``` |
| | |
| | [ Stop Agent ] [ Send Answers ⌘⏎ ] |
+=============================================================================+
Layout notes:
- Filter tabs (
Pending/Answered) at top of conversation list allow switching views !(urgent dot, purple) = conversation linked to a blocked task;?(warning dot, orange) = standard pendingBLOCKINGbadge usesstatus-urgenttokens — surfaces task-blocking conversations first- Detail panel minimum 400px, draggable resize handle
[<>], or click to toggle full-width [Snooze v]dropdown: 15m, 1h, 4h, Tomorrow — removes from pending list temporarily[ Stop Agent ]is LEFT-aligned,[ Send Answers ]is RIGHT-aligned — maximum spatial separation- Previous Q&A collapsible section shows conversation history with this agent
- Textarea hint line confirms markdown support
Cmd+Entershortcut shown on Send button;Rglobal shortcut focuses textarea
Conversation Card (in left list)
Blocking (urgent — task is blocked waiting for this answer)
+-------------------------------+
| ! blue-fox-7 2m | <-- status dot (purple, pulsing) + name + time
| Auth System Overhaul | <-- initiative name
| BLOCKING | <-- badge: bg-status-urgent-bg text-status-urgent-fg
+-------------------------------+
Waiting for response (standard)
+-------------------------------+
| ? green-bear-1 5m | <-- status dot (orange) + name + time
| API Redesign | <-- initiative name
+-------------------------------+
Snoozed
+-------------------------------+
| z pink-owl-3 45m | <-- status dot (muted) + name + time
| Data Migration | <-- initiative name
| Snoozed until 3:00 PM | <-- snooze info, text-muted-foreground
+-------------------------------+
Answered
+-------------------------------+
| ✓ green-bear-1 1h | <-- status dot (green) + name + time
| API Redesign | <-- initiative name, dimmed
+-------------------------------+
- Purple
!dot (pulsing) = linked task isblocked— usesstatus-urgent-dottoken. Sorted first always. - Orange
?dot =pendingconversation, standard priority — usesstatus-warning-dottoken - Muted
zdot = snoozed — hidden from Pending tab by default, shown in a collapsed "Snoozed (1)" section at bottom - Green checkmark =
answered— usesstatus-success-dottoken. Visible only on Answered tab. - Click selects conversation and shows detail panel
- Selected card gets
bg-accenthighlight (indigo tint, not grey) Up/Downarrow keys navigate list;Enterselects;Rfocuses answer textarea- Sort order within Pending tab: blocking first, then by
createdAtascending (oldest unanswered first)
Detail Panel (min 400px right, resizable)
Header
+----------------------------------------------+
| Question from blue-fox-7 [<>] |
| Task: Implement OAuth PKCE flow |
| Phase: Authentication . 2 minutes ago |
+----------------------------------------------+
[<>]toggle: click to expand detail panel to full width (hides conversation list). Click again to restore split. Keyboard shortcut:Fto toggle full-width.- Task and phase names are clickable links (navigate to initiative detail at that task/phase).
- Panel is resizable via drag handle on the left border. Min 400px, max 70% of viewport.
Conversation History (collapsible)
+----------------------------------------------+
| [v] Previous Q&A with blue-fox-7 (1) |
| +------------------------------------------+|
| | Q (1h ago): "Should I store tokens in ||
| | localStorage or httpOnly cookies?" ||
| | A: "httpOnly cookies. See OWASP ||
| | session management cheatsheet." ||
| +------------------------------------------+|
+----------------------------------------------+
- Collapsed by default. Shows count of prior conversations with this agent (same
fromAgentId). - Each historical entry shows the question, your answer, and relative timestamp.
- Query:
findByAgentPair(fromAgentId, toAgentId)— requires new repository method (schema supports it, query doesn't exist yet).
Question Display
+----------------------------------------------+
| "Should I use PKCE or implicit flow |
| for the browser client? The spec |
| recommends PKCE but the existing |
| code uses implicit." |
+----------------------------------------------+
- Question text rendered in a
bg-muted rounded-lg p-4block withfont-mono text-smfor code-heavy questions. - If the question contains markdown fences, render them with syntax highlighting (use the terminal token palette).
Suggested Answers (conditional)
+----------------------------------------------+
| Suggested |
| +------------------------------------------+|
| | [Use approach A: PKCE flow (recommended ||
| | by spec)] [ Use ^ ] ||
| +------------------------------------------+|
| +------------------------------------------+|
| | [Use approach B: Keep implicit flow ||
| | (existing code)] [ Use ^ ] ||
| +------------------------------------------+|
+----------------------------------------------+
- Shown only when the agent's question contains recognizable choice patterns ("should I use A or B", "which approach", option lists).
- Parsed from the question text client-side — extracts options and presents as one-click answer buttons.
[ Use ^ ]populates the answer textarea with that option (does NOT auto-send). User can edit before sending.- If no pattern detected, this section is hidden entirely. No server-side AI inference needed.
Answer Form
+----------------------------------------------+
| Your Answer [Snooze v] |
| +------------------------------------------+|
| | Use PKCE. We're deprecating implicit ||
| | flow per OAuth 2.1 security BCP. ||
| | ||
| | ```ts ||
| | // See RFC 7636 for PKCE spec ||
| | ``` ||
| +------------------------------------------+|
| Markdown supported. `code`, **bold**, ``` |
| |
| [ Stop Agent ] [ Send Answers ⌘⏎ ] |
+----------------------------------------------+
- Textarea:
min-h-[120px], auto-grows. Supports markdown with a hint line below. Rendered preview is NOT shown (this is a response to an agent, not a document — raw markdown is fine). Fenced code blocks (```) getfont-monostyling in the textarea via CSS. - Snooze dropdown:
[Snooze v]next to "Your Answer" label. Options: 15 minutes, 1 hour, 4 hours, Tomorrow, Custom. Snoozed conversations move to collapsed "Snoozed" section at bottom of Pending list. Snooze is client-side only (localStorage timer) — no schema change needed. When snooze expires, conversation reappears in the Pending list with a brief highlight animation. - Keyboard:
Rfrom anywhere on the page focuses the textarea.Cmd+Enter(Mac) /Ctrl+Enter(Win) sends.Escapefrom textarea returns focus to the conversation list.
Batch Answer (when applicable)
+----------------------------------------------+
| [i] 2 other agents asked similar questions |
| [ Apply this answer to all 3 ] |
+----------------------------------------------+
- Shown below the answer textarea when multiple pending conversations contain similar question text.
- Similarity detection: simple keyword overlap heuristic on question text (no ML needed). E.g., if 3 agents all ask about "PKCE vs implicit", they cluster.
[ Apply this answer to all 3 ]sends the same answer to all matched conversations. Uses a batchanswerConversationcall — requires new tRPC batch mutation or sequential calls.- If no similar questions exist, this section is hidden.
Button anatomy
[ Stop Agent ]--variant="destructive" size="sm", LEFT-aligned in the button row. Stops the asking agent viastopAgentmutation. Confirmation:window.confirm("Stop agent blue-fox-7? This will terminate the agent process.")includes the agent name in the message. Shift+click bypasses confirmation per project convention. Visual separation: positioned flush-left while Send is flush-right, creating maximum distance. Usesoutlinestyle (not filled) to further reduce visual weight vs. the primary Send button. Updated:variant="outline"withtext-destructive border-destructive hover:bg-destructive hover:text-destructive-foreground.[ Send Answers ⌘⏎ ]--variant="default"(primary, filled indigo), RIGHT-aligned. Sends the response viaanswerConversationmutation thenresumeAgentmutation. Disabled until answer textarea is non-empty. Shows keyboard shortcut hint⌘⏎in the button label. After successful send, auto-advances to next pending conversation in the list.
Submit In Progress
| [ Stop Agent ] [ [spinner] Sending... ] |
[ Send Answers ]button shows aLoader2spinner + "Sending..." text- Button is
disabledduring mutation [ Stop Agent ]is also disabled during mutation to prevent conflicting actions- Textarea becomes
readonlywithopacity-50during mutation - On success: toast "Answer sent to blue-fox-7", auto-advance to next pending conversation
Submit Error
| |
| [ Stop Agent ] [ Send Answers ⌘⏎ ] |
| |
| Failed to send. Try again. |
+------------------------------------------------------------+
- Error text:
text-sm text-destructive, right-aligned below the buttons - Shown when
answerConversationMutation.isErrororresumeAgentMutation.isErroris true - Cleared on next submit attempt
- Textarea remains editable so the user can retry without re-typing
Empty Inbox
+=============================================================================+
| |
| Inbox (0) [ Refresh ] |
| |
| +-----------------------------------------------------------------------+ |
| | | |
| | [inbox] | |
| | No pending questions | |
| | Agents will ask questions here when they need input. | |
| | | |
| +-----------------------------------------------------------------------+ |
| |
+=============================================================================+
[inbox]icon:Inboxfrom Lucide,h-8 w-8 text-muted-foreground- Title:
text-sm font-medium - Description:
text-sm text-muted-foreground - Keyboard hint below:
text-xs text-muted-foreground— "PressRto reply when a question arrives"
List Loading State (skeleton rows)
+=============================================================================+
| |
| Inbox [ Refresh ] |
| |
| Conversations | |
| --------------------------+ 400px |
| +------------------------+| |
| | [.] ░░░░░░░░░░ ░░░ || |
| | ░░░░░░░░░░░░░░░░░ || Select an agent to view details |
| +------------------------+| |
| +------------------------+| |
| | [.] ░░░░░░░░░░ ░░░ || |
| | ░░░░░░░░░░░░░░░░░ || |
| +------------------------+| |
| +------------------------+| |
| | [.] ░░░░░░░░░░ ░░░ || |
| | ░░░░░░░░░░░░░░░░░ || |
| +------------------------+| |
| +------------------------+| |
| | [.] ░░░░░░░░░░ ░░░ || |
| | ░░░░░░░░░░░░░░░░░ || |
| +------------------------+| |
+=============================================================================+
Skeleton cards mirror conversation card anatomy: status dot placeholder, name + time line, initiative name line. Shimmer animation sweeps left-to-right on a 1.5s loop.
List Error State
+=============================================================================+
| |
| Inbox [ Refresh ] |
| |
| Conversations | |
| --------------------------+ 400px |
| | |
| [AlertCircle] | Select an agent to view details |
| Failed to load | |
| conversations | |
| | |
| [ Retry ] | |
| | |
+=============================================================================+
[AlertCircle]icon:h-6 w-6 text-destructive- Error message:
text-sm text-destructive [ Retry ]button:variant="outline" size="sm", callsagentsQuery.refetch()+messagesQuery.refetch()
Detail Error State
+=============================================================================+
| |
| Inbox (2) [ Refresh ] |
| |
| Conversations | |
| --------------------------+ 400px |
| +------------------------+| |
| | ? blue-fox-7 2m || [AlertCircle] |
| | Auth System Overhaul || Failed to load conversation |
| +------------------------+| |
| | ? green-bear-1 5m || [ Retry ] |
| | API Redesign || |
| +------------------------+| |
| | |
+=============================================================================+
- Shown when
questionsQuery.isErroris true for the selected agent [ Retry ]button callsquestionsQuery.refetch()
Keyboard Shortcuts
| Key | Context | Action |
|---|---|---|
R |
Anywhere on page | Focus answer textarea for selected conversation |
Up / Down |
Conversation list focused | Navigate between conversations |
Enter |
Conversation list focused | Select highlighted conversation |
Cmd+Enter / Ctrl+Enter |
Answer textarea focused | Send answer |
Escape |
Answer textarea focused | Return focus to conversation list |
F |
Anywhere on page | Toggle detail panel full-width |
J / K |
Anywhere on page | Next / previous conversation (vim-style) |
S |
Conversation selected, textarea empty | Open snooze dropdown |
Shortcuts are disabled when a dialog (confirm, etc.) is open. Documented in a ? tooltip in the page header.
Schema / Backend Requirements
Changes needed to support v2 Inbox features:
| Feature | Requirement | Scope |
|---|---|---|
| Conversation history | findByAgentPair(fromAgentId, toAgentId) repository method |
New query in ConversationRepository |
| Blocking detection | Join conversation's taskId to tasks table, check status === 'blocked' |
Query enrichment in tRPC getPendingConversations or client-side join |
| Batch answer | Either a batchAnswerConversations tRPC mutation or sequential answerConversation calls |
New mutation (preferred) or client loop |
| Snooze | Client-side only (localStorage timers) |
No schema change |
| Suggested answers | Client-side question text parsing | No schema change |
Source
packages/web/src/routes/inbox.tsxpackages/web/src/components/InboxList.tsxpackages/web/src/components/InboxDetailPanel.tsxpackages/web/src/components/InboxConversationHistory.tsx(new)packages/web/src/components/InboxSuggestedAnswers.tsx(new)packages/web/src/components/InboxBatchAnswer.tsx(new)packages/web/src/components/MessageCard.tsxpackages/web/src/components/QuestionForm.tsxpackages/web/src/hooks/useInboxKeyboard.ts(new — keyboard shortcut handler)packages/web/src/hooks/useSnooze.ts(new — localStorage snooze timers)
Design Review Notes
What's solid
The 2-button simplification from v1's 3-button layout is the right call. The error states (list, detail, submit) are thorough — most wireframes skip these. The skeleton loading state mirrors card anatomy correctly. The empty state has personality without being cute.
What was fixed
1. Stop Agent proximity hazard. The original spec placed [ Stop Agent ] immediately left of [ Send Answers ]. This is a catastrophic misclick waiting to happen — you're about to click "Send" and you hit "Stop" instead, killing an agent mid-task. Fix: buttons are now flush-left and flush-right respectively, creating maximum spatial separation. Additionally, [ Stop Agent ] was downgraded from variant="destructive" (filled red) to an outline style with destructive text color. The filled primary button now has overwhelming visual dominance. The window.confirm() dialog now includes the agent name for clarity.
2. No answered/pending filtering. The original list showed both states mixed together with only a dot color difference. When you have 15+ conversations, this becomes noise. Fix: added [Pending (2)] [Answered] filter tabs at the top of the conversation list. Pending is the default view. Answered is an archive you visit when you need to reference past decisions.
3. No urgency signal. All pending conversations looked identical — whether the agent's task is blocked and the entire initiative is stalled, or it's a low-priority research question. Fix: conversations linked to blocked tasks get a purple pulsing ! dot and a BLOCKING badge, using the existing status-urgent tokens from the theme spec. These always sort first.
4. Detail panel too narrow for code. 400px is fine for prose questions, but agents frequently include code snippets, file paths, and stack traces. Fix: panel is now min 400px, resizable with a drag handle, plus a full-width toggle (F key or [<>] button) that hides the conversation list entirely.
5. No conversation history. Without context, you're answering each question in isolation. The agent might have asked a related question 20 minutes ago that your current answer should be consistent with. Fix: collapsible "Previous Q&A with [agent]" section above the answer form. Requires a new findByAgentPair() repository method — the schema supports it, the query just doesn't exist yet.
6. Plain text textarea. Agents process markdown. Users think in markdown. Not supporting it means answers with code references are hard to write clearly. Fix: textarea now accepts markdown with a hint line ("Markdown supported. code, bold, ```"). No live preview — this is a reply to an agent, not a document editor. The overhead of a preview pane isn't justified here.
7. No snooze. Not every question is urgent. Sometimes you need to think, or you're deep in another conversation. Without snooze, the question just sits there adding visual noise to your pending list. Fix: [Snooze v] dropdown with preset durations. Snoozed items collapse into a "Snoozed" section at the bottom of the Pending tab. Implemented client-side only (localStorage timers) — no schema change, no migration, no backend work.
8. No keyboard shortcuts. For a keyboard-first tool managing 10+ agents, requiring mouse clicks to answer questions is slow. Fix: full keyboard shortcut table added. R to reply from anywhere, J/K for vim-style navigation, Cmd+Enter to send, F for full-width toggle.
9. No suggested answers. When an agent asks "should I use A or B?", the system could extract those options and present them as one-click buttons. Fix: client-side pattern matching on question text extracts choice options. [ Use ^ ] populates (not sends) the textarea. No AI inference, no API cost — just regex on common question patterns.
10. No batch answers. When 3 agents ask "which auth library should we use?", answering each individually is tedious. Fix: similarity detection (keyword overlap) clusters similar pending questions. "Apply this answer to all 3" button sends the same answer to all matched conversations.
Open questions for implementation
-
Snooze persistence across sessions. localStorage is fine for single-session use, but if the user closes the tab and reopens, snoozed items reappear immediately (timer lost). Is this acceptable, or should snooze state be persisted server-side? Recommendation: start with localStorage, add server-side if users complain.
-
Batch answer atomicity. If one of the 3 batch answers fails, should the other 2 still succeed? Recommendation: yes — use sequential calls, show partial success/failure. Don't make the user redo the ones that worked.
-
Conversation history query performance.
findByAgentPairwill scan byfromAgentIdindex. For workspaces with hundreds of conversations, consider adding a composite index on(fromAgentId, toAgentId)to the schema. -
Blocking detection accuracy. The current approach joins
taskIdto checktask.status === 'blocked'. But a task could be blocked for reasons unrelated to this conversation. Consider adding aisBlockingfield to conversations, or determining blocking status from whether the agent itself iswaiting_for_input.