Skip to content
Internals — roadmap: describes planned work that may not match the current code.
Verified: Accurate against commit b64377d40 View Commit

Chat Surface Design Critique and Information Matrix (2026-09-28)

Chat Surface Design Critique and Information Matrix

Section titled “Chat Surface Design Critique and Information Matrix”

Evidence: review-bundle captures (chat--{default,empty,error,composer-filled}--wide, chat-trust-receipts.png, chat-trust-claims.png, research-debugger-stepper.png), axe results from e2e/review/capture.spec.ts, and a code inventory of crates/vox-gui/ui/src (file:line citations below are relative to that directory). Builds on GUI Intuitiveness Implementation Plan (the attention inbox and intent panel it shipped are kept). The implementation plan, written to be driven task-by-task by Claude Code through the agy CLI, is docs/superpowers/plans/2026-09-28-chat-surface-trace-and-latest-models.md.

The chat surface looks calm and branded, but it answers “what is the whole system doing?” in four places and “what did this turn do?” in none. The same numbers (agents, queue, spend, model, mesh) appear in the status bar, the Execution rail, the composer, and the Dashboard. Meanwhile the per-turn facts a user needs to trust or debug a reply are either missing or scattered as chips: which tools ran, whether they were receipted, why a model was chosen, what grounding concluded, and what was delegated. The biggest opportunity is a collapsed per-turn trace under each assistant message, with the status bar and rail each owning one scope.

ScopeHomeWhat lives thereWhat leaves
Global engine (whole orchestrator, all sessions)Status bar cards, each with a popover for detailEngine (agents · queue), Spend (budget burn, OpenRouter), Mesh, Routing (auto + resolved model family), Needs you (approvals + questions), Freshness (LIVE/POLL/OFFLINE)The rail’s “Resources” block, the composer’s global “session $x/$y” text
This sessionExecution railTasks (with lock and phase chips), Routing decision for the last turn, Context window, Session spendThe Agents roster (moves to the Agents surface; the status-bar card links there), global Resources
This turnTranscript: the message plus one collapsed trace rowTools called and their receipt state, model resolution and reason, grounding verdict, delegations, research milestones, locks waited on, errors and timeoutsFree-floating chips that are not actionable
Needs a human nowInline interrupt chip in the transcript, plus the status-bar Needs-you countApproval required, fabricated/unverified claims, injection detected, budget exceeded, scope violation—
Deep inspectionInspector drawer that reuses the research debugger’s stage stepper and payload panesFull per-step input/output payloads, timings, raw event JSON—

Design matrix: every chat-adjacent element

Section titled “Design matrix: every chat-adjacent element”

Verdicts: Keep, Move (to another home), Merge, Rename, Collapse (behind disclosure), Add, Delete (orphaned code).

#Element (where)Shows todayDuplicatesVerdictTarget
1Status bar AGENTS (BottomStatusBar.tsx:141)active agentsrail Resources, rail roster, DashboardMerge”Engine” card: 9 agents · 44 queued; popover lists busy agents and links to Agents
2Status bar QUEUEqueue depthrail Resources, composer “N queued”, DashboardMergeinto Engine card; the composer pill stays only when this session has queued work
3Status bar BUDGETglobal budgetBurnDriveConsole meter, composer “session $x/$y”, DashboardMerge”Spend” card $12.34 / $50; popover splits OpenRouter vs local and by session
4Status bar OR SPENDuseLlmSpendrail OpenRouterMergeinto Spend card popover
5Status bar MESHX/Y online via useMeshNodesrail “N peers” (different source)Keep, single sourceone hook, one phrasing: Mesh 2/2 online
6Status bar MODELactiveModel ?? 'auto-route'rail Model, rail “Intents”, composer Run on, badgeRename + Merge”Routing” card: Auto → claude-opus (latest), showing a version only when read from the OpenRouter catalog
7Status bar APPROVALSapprovals countApprovals dock, Needs You, ReviewRename”Needs you” card (approvals + questions), matching the attention inbox
8Status bar RESEARCHlane + Tavily quota; popover mostly hardcoded (StatusBarCluster.tsx:80-139)—Keep, make honestpopover from real engine status only; no hardcoded “Online” or ”✓ Wikipedia (Live)“
9Status bar LIVE / POLL / OFFLINEfreshness—Keeprename POLL to “Polling” in the tooltip
10Status bar Configure ▾tile toggles, “Budget burn” label—Keeplabels match the card names
11Rail “Execution” task listsession tasks + lifecycle—Keep + Addlock chips (Phase 5), phase chip (revive PhaseChip)
12Rail “Intents”routing selection · bandit state, “Alt:” rowsstatus bar MODELRename + Collapse”Routing”: Routed to claude-opus (latest: claude-opus-5-5) — exploit; alternatives behind a disclosure; the word “exploit” gets a tooltip (“using the best-known model; explore = trying an alternative”)
13Rail “Agents” rosterall agents + tasksstatus bar, Agents surfaceMoveout of the chat rail; the Engine card links to Agents
14Rail “Resources”agents, queue, mesh, model, OpenRouter, sessionstatus bar ×5Delete blockkeep only “Session spend” in the rail (the one session-scoped row)
15Rail ContextWindowMetercontext budget, fetched once—Keep + Fixpoll on each turn completion (it goes stale today)
16Transcript message bubbletext, streaming state—Keep—
17ModelBadgemodel id, guessed provider/cost (ChatTranscript.tsx:69-73), selection line never renderedstatus bar MODELMergefolds into the trace summary row: resolved model, selection reason (your pick / auto / fell back), real cost from the turn
18tool_receipt chipsone chip per tool call—Collapseinto the trace: summary shows 3 tools · 3 receipts ✓; per-tool rows appear on expand
19receipt_claims chipclaims verdict counts—Keep as interruptclean verdicts render inside the trace; any fabricated/unverified claim stays an inline amber interrupt
20skill_activated chipskill id + “not this one”—Keepinline, because it is actionable
21delegation_spawned eventemitted, never rendered—Addtrace row Delegated to agent 3 · task 812 linking to the task
22research_milestone eventemitted, never rendered—Addtrace row Research · 3 waves · 12 claims verified · 1 contradiction
23~75 AgentEventKinds never reaching chat——Add, filteredonly turn-correlated kinds, into the trace: tool_timed_out, compaction_triggered, context_truncated, injection_detected, budget_alert, scope_violation, replan_triggered, semantic_drift_detected, doubt_reported, pav_phase_changed, llm_call_completed; hopper/mesh/workflow families stay on their surfaces
24StatusLine “{phase} · Ns”in-flight task phase—Keepbecomes the live header of the trace row while streaming
25”Done · $x” rowturn cost—Mergeinto the trace summary
26Harness issue line (8 s poll)harness issuesHarness surfaceKeep as interruptinterrupt chip only when it affects this session
27Composer DriveConsole clutchFree / Effic. / Bal. / Genius; meaning only in title—Renamefull words, Free · Efficient · Balanced · Genius, with a one-line visible hint under the group on hover/focus; add Responsive if routing defines it
28DriveConsole cost meterglobal spendstatus barKeep, relabelSpend $12.34 / $50 (global), or scope it to the session and say so
29Composer “session $x / $y” (slashRouter.ts:48)global cost labelled “session”DriveConsole meterDeletewrong label and a duplicate
30Risk button “Moderate”risk levelgrounding toggle overlapsRenameRisk: Moderate
31”grounding: off” togglepost-reply confidence checkRisk popover textRename + MoveCheck replies inside the Risk popover, so the two controls stop contradicting each other
32”Run on Auto” tier pickertiers + keyed modelsstatus bar MODELRenameModel: Auto with tiers Auto / Local / Mesh / Cloud; version strings only from the catalog (see the model track)
33”Intent” toggleGoal / Constraints / Acceptance / Effortrail “Intents” (a different meaning)Keepthe rail stops using the word (row 12)
34Send mode “Quick chat / Background task”execution mode; plan mode has no menu entry—Keep + Fixadd “Plan”, or delete the unreachable state
35Run buttonaria says “(Enter)”, hint shows ⌘↵—Fixaria and hint agree
36AttentionBudgetMeterattention minutes, auto-collapseNeeds YouKeeprename its “budget” to “attention”
37SecretaryToastsuggested task—Rename”Suggested task”
38Panels ▾ menudock panel toggles, “Mercatus”sidebar says “Market”Renameone label source (LEXICON), drop NAV_LABELS drift
39Orphans: ChatModelPicker, ChatMessage, ResearchSummaryCard, AttentionStrip, buildTranscriptTimeline, dead chatDock block (App.tsx:1865)——Delete or reviverevive ChatAgentEventRow + PhaseChip inside the trace; delete the rest
40useChatVerbosity (no UI; verbose == normal)——Adda Quiet / Normal / Verbose control that sets the trace’s default expansion
FindingSeverityRecommendation
No per-turn account of what happened: tools, receipts, routing reason and grounding are scattered or missing🔴 CriticalCollapsed trace row per assistant turn (matrix rows 17–25), with an inspector for depth
Two emitted event kinds and ~75 engine events never reach the user; delegation_spawned vanishes silently🔴 CriticalRender them in the trace with a filter policy (row 23); a contract test keeps Rust and TS in sync
The same global numbers appear 3–4 times; “session” labels a global figure🔴 CriticalOne home per fact (the table above); delete the composer duplicate
Model names are pinned, stale versions (“opus-4-8”, “sonnet-4-6”), shown as if current🔴 CriticalLatest-model resolution from the OpenRouter catalog; display only resolved versions (plan track B)
Clutch abbreviations “Effic.” / “Bal.” with meaning only in tooltips🟡 ModerateFull words and a visible hint
”Intents” in the rail means routing; “Intent” in the composer means a goal spec🟡 ModerateRename the rail section “Routing”
Risk “Moderate” and “grounding: off” read as contradictory controls🟡 ModerateMerge grounding into the Risk popover
Context meter is fetched once per session and goes stale🟡 ModerateRefresh on turn completion
Research popover shows hardcoded health🟡 ModerateReal data or nothing
Run button aria text disagrees with its visible hint🟢 MinorAlign
plan send mode is unreachable🟢 MinorAdd it to the menu or delete it
  • What draws the eye first: the brass-outlined composer and the RUN/RESUME button. That is correct for an idle session; while a turn runs, the eye should go to the live trace header, which today is a small StatusLine.
  • Reading flow: sidebar → transcript → composer, with a right rail that is taller than the transcript and full of global counters (chat--default capture: nine agent rows push Resources below the fold). The rail competes with the conversation instead of annotating it.
  • Emphasis: amber (text-amber-*, ×39) marks prices, the active clutch, risk and warnings alike, so a warning does not stand out. Reserve the warn token for states needing attention.
ElementIssueRecommendation
Status colourtokens --color-status-pass/fail/warn/info exist but only 2 components use them; chat, composer and status bar hardcode amber/rose/emerald/cyan Tailwind classes and raw #0b0b0eRoute all status colour through the four tokens; add a lint rule
Type scalechips and values use 8, 9, 10, 11 and 12 px; tracked uppercase uses ~7 letter-spacings (0.12–0.32em)Two label styles (display caps at 0.13em per Limes conventions.md, micro caps at 0.08em) and a minimum of 11 px for data
Glow effectsLoquela glows (Loquela.tsx:711, :819) and index.css:125Remove; Limes says “No glows, no neon”
Label sourcesNAV_LABELS vs LEXICON drift (“Mercatus”/“Market”, “Runs”/“Review”, “Search Index”/“graphify”/“VoxGraph”)One label source
Latin modeevery label has an la variant; internal names (Loquela, Oratio, Secretary, Axis Inspector) leak into English modeEnglish mode never shows Latin or code names

Accessibility (axe, wide viewport, chromium)

Section titled “Accessibility (axe, wide viewport, chromium)”
  • Color contrast: fail. Two serious violations per chat state, for example text-zinc-500 mono at 11 px on #11151a (3.79:1) and text-brass/70 at 10 px (4.12:1); both need 4.5:1. Approvals has 3, Activity 1.
  • Heading order: fail (moderate) on chat empty/error; the Activity and Approvals surfaces lack an h1; Activity has 2 unlabeled selects (critical).
  • Text readability: 9–10 px mono data is below comfortable reading size and makes the contrast problems worse.
  • Review coverage gap: 3 chat review states (model-picker-open, session-menu-open, rails-overlay-open) target elements that no longer exist, so those views are not captured.
  • The Phase 5 receipt and claims chips carry only server-derived data, and model-supplied ids never reach trusted chrome. Keep that rule for every trace row.
  • The research debugger’s stage stepper, with its Payloads / Violations inspector, is the right pattern for deep inspection. Reuse it; don’t build a second one.
  • The dockview layout already supports opt-in panels and a condensed width mode, so moving blocks between homes does not need new layout machinery.
  • The LIVE / POLL / OFFLINE freshness pill is honest and cheap.
  • The attention-budget meter’s auto-collapse is a good precedent for progressive disclosure.
  1. Transcript level shows messages, one trace summary per turn, and interrupt chips only for things a human must act on (approval, fabricated claim, injection, budget exceeded, scope violation, harness issue affecting the session).
  2. Everything else is inside the trace, grouped by step, with consecutive identical events coalesced (3× tool_timed_out).
  3. Default expansion follows the verbosity control: Quiet = collapsed, Normal = collapsed with failures auto-expanded, Verbose = expanded.
  4. Every trace row is server-derived (registry names, ledger ids, counts); raw model text is never rendered as chrome.
  5. Engine families not correlated to a turn (hopper, mesh, workflow) stay on their own surfaces.
ConceptUseStop using
Model selection for a turnRoutingIntents, auto-route, Auto · Router, Cascade
Money spentSpend (with scope: global / session / turn)Budget burn, OR Spend, “session” for global
Attention minutesAttentionbudget
Cost/quality presetMode: Free, Efficient, Balanced, Genius (and Responsive if defined)Clutch, Effic., Bal.
Acceptable riskRiskbare “Moderate”
Post-reply confidence checkCheck repliesgrounding
Items waiting on a humanNeeds youApprovals (as the umbrella term)
Model versionthe catalog-resolved id, e.g. claude-opus-5-5, or the family (claude-opus (latest)) when unresolvedany hardcoded version string