Suggested first sprint: Build confirm copy says
here, code navigates away (marketplace-1); Workspace copy
is contradicted the instant a build starts (builder-1); AI
records and transcribes with no consent step (calendar-1);
Failed workflow creation fails silently, drops draft
(workflows-1); "Pauses for approval" subtitle is inaccurate
(workflows-2); 'All'-secrets vault grant applies with one
click (trust-control-4); Removing a member fires instantly
with no confirmation (chat-2); Delete-message dialog
auto-focuses the destructive button (chat-3);
Install-to-room action has no trigger anywhere
(marketplace-2); Snooze and dismiss vanish with no undo
(command-center-1); Failed call history load offers no
retry (calendar-4); Run button sits above what it will do
(workflows-3); Disconnect revokes access with zero
confirmation (trust-control-1); 'Allow Always' consequence
never stated (trust-control-2)
| ID | Severity | Effort | Unit | Title | File | Line | State | Principle | What | Fix | Confidence |
|---|---|---|---|---|---|---|---|---|---|---|---|
| workflows-1 | P0 | low | workflows | Failed workflow creation fails silently, drops draft | src/app/[slug]/social/workflows/create/_components/CreateWorkflowClient.tsx | 51 | error (create-workflow submit fails) | Error Recovery | If createWorkflowDef rejects (network blip, name collision, backend validation), onCreate just stops the spinner — no error text anywhere — and CreateWorkflowView.tsx:1149 already deleted the localStorage draft before the call resolved, so the built workflow is gone with zero explanation. | Surface e.message from the catch block and only delete the local draft after createWorkflowDef resolves successfully. | 3/3 |
| marketplace-1 | P0 | medium | marketplace | Build confirm copy says here, code navigates away | src/app/[slug]/social/skills-and-apps/_components/BuildProgressPanel.tsx | 58 | building (moments after confirming in BuildConfirmDialog) | Cognitive Dissonance | BuildConfirmDialog tells users the agent 'will build it right here, in your workspace,' but this effect fires navigateToRoom the instant a room ID resolves, redirecting away from the marketplace seconds after confirming. | Stop auto-navigating; keep BuildProgressPanel mounted in place and let users click an explicit 'Open room' button instead. | 2/2 |
| builder-1 | P0 | medium | builder | Workspace copy is contradicted the instant a build starts | src/app/[slug]/social/skills-and-apps/_components/BuildProgressPanel.tsx | 58 | building | Cognitive Dissonance | BuildConfirmDialog says the build happens 'right here, in your workspace,' but the instant roomId is set, this effect calls navigateToRoom, which router.replace()s the browser out of the marketplace into the chat room. | Remove the automatic onOpenRoom call; keep the user on the marketplace and offer an explicit 'View live conversation' link instead. | 2/2 |
| calendar-1 | P0 | medium | calendar | AI records and transcribes with no consent step | src/app/[slug]/social/calendar/_components/LiveCallCard.tsx | 50 | live | Error Prevention | The only signal that a call is being captured is an ambient 'Live meeting stream' label shown after listening has already started; nowhere in this unit (or grep-verified across src/components/meet) is there an explicit consent step before recording/transcription begins. | Add an explicit one-time notice or opt-in before the first AI-listened call, not just an after-the-fact ambient label. | 3/3 |
| trust-control-4 | P0 | medium | trust-control | 'All'-secrets vault grant applies with one click | src/app/[slug]/social/_components/chat/AgentActivityPanel/permissions/ToolRow.tsx | 86 | permissions tab, vault tool row | Friction Calibration | Clicking 'All' (:86-96) optimistically grants a tool access to every stored secret at once (usePermissions.ts:103-120) with no confirmation, even though it's the widest-blast-radius option sitting next to 'Selected' and 'Deny'. | Require a brief inline confirm before switching a tool's vault grant to 'All'. | 3/3 |
| workflows-2 | P0 | high | workflows | "Pauses for approval" subtitle is inaccurate | src/app/[slug]/social/workflows/_components/WorkflowsDemoClient.tsx | 394 | default (workflows list) | [HS] Friction Calibration | The builder's active palette (flowSteps.ts FLOW_BLOCKS, no approval block) and the plan's risk metadata (planMeta.ts RISK_META, 'sends-message'→tone 'send' not 'gate') mean a message- or schedule-triggered workflow with a 'Send a message' step runs and sends with no pause at all. | Either restore an addable approval/review block to FLOW_BLOCKS or soften the subtitle to describe only workflows that include a gate. | 3/3 |
| chat-2 | P1 | low | chat | Removing a member fires instantly with no confirmation | src/app/[slug]/social/_components/chat/RoomMemberDialog.tsx | 260 | default (members modal, admin dropdown) | Error Prevention — no confirmation before an important, hard-to-reverse action | Clicking 'Remove from group' calls Apis.postMatrixKickUser immediately with no confirm step, and silently also kicks the member's paired agent bot (line 262-269) — an admin removing one person loses two participants with no warning. | Add a confirm step (or ConfirmDialog) naming both the member and their paired agent before kicking. | 3/3 |
| chat-3 | P1 | low | chat | Delete-message dialog auto-focuses the destructive button | src/app/[slug]/social/_components/chat/DeleteMessageModal.tsx | 46 | modal (delete confirmation) | [HS] Friction Calibration — irreversible actions should not be the path of least resistance | The dialog overrides Radix's default focus to land on the red 'Delete' button and binds bare Enter to confirm-and-close, even though the copy says 'This cannot be undone' — the safe path (Cancel) gets no such acceleration. | Default focus to Cancel (or require an explicit click) instead of auto-focusing and Enter-binding Delete. | 3/3 |
| marketplace-2 | P1 | low | marketplace | Install-to-room action has no trigger anywhere | src/app/[slug]/social/skills-and-apps/_components/YoursRow.tsx | 59 | Yours tab, per-item overflow menu | Affordance | SkillsAndAppsMarketplace wires state, a mutation, and InstallToRoomDialog for scoped installs, but ItemActionsMenu (the only overflow menu) has no 'Install to a room' item, so no click anywhere can ever open it. | Add an 'Install to a room…' item to ItemActionsMenu and call the existing onInstallToRoom prop from it. | 2/2 |
| command-center-1 | P1 | low | command-center | Snooze and dismiss vanish with no undo | src/app/[slug]/social/_components/chat/AgentActivityPanel/commandCenter/CommandCenterTab.tsx | 2338 | Awaiting Reply list, default view | User Control & Freedom | Resolving a waiting item triggers a persistent toast with Undo (line 2328), but snoozing/dismissing the same card (lines 2338-2349) calls handleSnooze/handleDismiss directly - only a 600ms in-card label plays before the item disappears. | Call showToast with an Undo action from handleSnooze and handleDismiss, matching the Resolve flow. | 3/3 |
| calendar-4 | P1 | low | calendar | Failed call history load offers no retry | src/app/[slug]/social/calendar/_components/CalendarPage.tsx | 244 | error | Error Recovery | The history error state is static text with no retry button, unlike the transcript fetch elsewhere in the same recap panel (SummaryTabContent.tsx TranscriptBody) which does show one — the user's only recovery path is an unlabeled window refocus. | Add a visible Retry button that calls the existing refetch from useCallsHistoryQuery. | 3/3 |
| workflows-3 | P1 | low | workflows | Run button sits above what it will do | src/app/[slug]/social/workflows/run/[id]/_components/RunWorkflowClient.tsx | 175 | default (shareable link, runnable workflow) | F-Pattern | The Run CTA renders in the page header (top) while the WorkflowStepCards preview of what will actually happen renders in a section below it, so a link recipient can hit Run before ever seeing the step list. | Render the step-cards preview above the Run button so consequences are seen before the click. | 3/3 |
| trust-control-1 | P1 | low | trust-control | Disconnect revokes access with zero confirmation | src/app/[slug]/social/_components/chat/AgentActivityPanel/connections/ConnectionDetailView.tsx | 271 | connection detail view | Irreversibility Signals | handleDisconnect (:123-144) calls Apis.deleteConnection the instant the plain-text 'Disconnect' button (:271-278) is clicked — no confirm step, unlike DeleteSecretsConfirmModal's explicit 'cannot be undone' pattern for a comparable action. | Add a Radix confirm dialog mirroring DeleteSecretsConfirmModal before calling deleteConnection. | 3/3 |
| trust-control-2 | P1 | low | trust-control | 'Allow Always' consequence never stated | src/app/[slug]/social/_components/chat/ToolApprovalCard.tsx | 70 | pending tool-approval card in chat | Friction Calibration | 'Allow Always' (:70-77) sits one click beside 'Allow Once' with no copy explaining it permanently grants the tool without asking again (ChatPanel.tsx:3262 fires a durable 'permissions-changed' event) — the two options look almost as similar as their consequences are different. | Add a subline under 'Allow Always' stating it won't ask again for this tool in this chat. | 3/3 |
| builder-2 | P1 | medium | builder | 'Update available' badge has no way to act on it | src/app/[slug]/social/skills-and-apps/_components/YoursRow.tsx | 137 | default | Mental Models | YoursRow accepts onRefine in its prop type (line 55) but never destructures or calls it, and ItemActionsMenu has no Update/Refine item, so the visible upgrade signal has zero click path to the rebuild flow the parent already implements. | Pass onRefine into ItemActionsMenu as an 'Update' menu item, or make the badge itself a button calling onRefine. | 1/2 |
| calendar-2 | P1 | medium | calendar | Recap/scribe panel has no dialog semantics | src/app/[slug]/social/calendar/_components/CalendarPage.tsx | 339 | panel-open (recap or scribe) | Gestalt Figure-Ground | The full-content recap/scribe panel is a raw fixed div with no scrim behind it, no role="dialog", no focus trap, and no Escape-to-close — keyboard focus can tab into the dimmed-looking but fully interactive background (rails, agenda) behind it. | Wrap the panel in the existing Radix Dialog (src/components/ui/dialog.tsx) or add a scrim, role="dialog", and focus trap. | 3/3 |
| builder-3 | P1 | high | builder | Agent 'question' state exists in UI but never fires | src/app/[slug]/social/skills-and-apps/_components/useMarketplaceCreate.ts | 54 | asking | Visibility of System Status | runLiveBuild and sendFollowUp only ever set phase to 'building', 'done', or 'error' — nothing sets 'asking' or appends an agent message, so a build that needs the agent's clarification has no way to surface that on this panel. | Add a message listener on the session room that flips phase to 'asking' and appends the agent's question when it posts one. | 2/2 |
| chat-1 | P2 | low | chat | Usage-cap composer disables send with zero visible explanation | src/app/[slug]/social/_components/chat/UsageLimitBanner.tsx | 20 | error (usage cap reached / isSendingDisabled) | Visibility of System Status / Error Recovery | UsageLimitBanner() literally starts with return null;
before any of its logic runs, so it never renders. When hasNoCredit
disables the composer (ChatPanel.tsx:1300, :4718-4719), the only cue is
a disabled ChatInput with placeholder text and a native
title tooltip — no visible banner, no 'See Plans' CTA
reachable from chat. |
Remove the dead return null; (or re-enable the banner)
so the cap message and upgrade CTA render above the composer. |
3/3 |
| chat-list-1 | P2 | low | chat-list | Dismiss controls invisible on the mobile list | src/app/[slug]/social/_components/ChatListView/components/RailRow.tsx | 131 | default (people/agents sections, mobile drawer) | Fitts's Law | The dismiss icon is invisible until
group-hover/row:visible (RailRow.tsx:131) and only 20x20px;
this exact component renders inside ConvergenceListView which
ChatListView/index.tsx:104-110 mounts full-screen on <md/touch
viewports with no hover, so the control never becomes visible or
reachable there. |
Show the dismiss control at reduced opacity by default and reveal it
via (hover: none)/focus-within instead of hover-only. |
3/3 |
| chat-list-3 | P2 | low | chat-list | All-chats panel text fails contrast on dark surface | src/app/[slug]/social/_components/ChatListView/components/BotSessionsPanel.tsx | 151 | empty / default ("All chats" panel opened via See all chats) | Accessibility Contrast | placeholder:text-content-tertiary (and the same class
at lines 175 and 181) resolves to rgba(255,255,255,0.35) over
--surface-1 #0d1117, which computes to ≈3.2:1 — below the
4.5:1 AA minimum for this ~11-14px text. |
Swap text-content-tertiary for
text-content-secondary (or a Quiet Sky
--text-2 token) on this panel's
placeholder/empty/date-label text. |
3/3 |
| marketplace-3 | P2 | low | marketplace | Room access granted on one click, no confirm | src/app/[slug]/social/skills-and-apps/_components/InstallToRoomDialog.tsx | 159 | install-to-room dialog, room list | Consistency & Standards | Clicking any room name instantly installs the item and grants everyone in that room access — no secondary confirm, unlike ShareDialog's own grant flow, which requires a separate 'Confirm' click before granting. | Select a room first, then require a separate 'Confirm' button, mirroring ShareDialog's grant pattern. | 1/2 |
| marketplace-4 | P2 | low | marketplace | Uninstall removes instantly with no confirmation | src/app/[slug]/social/skills-and-apps/_components/ItemActionsMenu.tsx | 269 | Yours tab overflow menu and the item detail dialog | Error Prevention | Removing any item — including a self-built, unpublished app — fires immediately with no 'Are you sure?' step, while the analogous Share flow requires a full 'Remove sharing?' confirmation with consequence text. | Add a brief confirm step before uninstalling, reusing ShareDialog's 'Remove sharing?' confirm-footer copy and buttons. | 2/2 |
| builder-4 | P2 | low | builder | Primary build button is smaller than a touch target | src/app/[slug]/social/skills-and-apps/_components/MarketplaceHero.tsx | 273 | default | Fitts's Law | The single CTA that starts the entire builder flow is a 32px circle, below the 44px minimum tap target, on the hero that is this unit's front door. | Increase the button to at least size-11 (44px) while keeping the same icon and placement. | 2/2 |
| builder-5 | P2 | low | builder | Failed build offers dismiss, never a retry | src/app/[slug]/social/skills-and-apps/_components/BuildProgressPanel.tsx | 271 | error | Error Recovery | When phase is 'error', the footer shows just a Dismiss button; there's no 'Try again,' so the user must return to the hero and retype the prompt, losing the transcript. | Add a 'Try again' button that resends the last prompt into a fresh build session. | 2/2 |
| builder-6 | P2 | low | builder | Finished build ends without a path to publish | src/app/[slug]/social/skills-and-apps/_components/BuildProgressPanel.tsx | 224 | done | Peak-End Rule | After a build completes, the panel offers preview-toggle and a refine composer but no Publish action, so a successful build has no forward-moving next step and the peak moment lands flat. | Add a primary 'Publish' button next to Open preview that opens the existing PublishDialog for this item. | 2/2 |
| calendar-3 | P2 | low | calendar | Deep-linked recap header never gets enriched | src/app/[slug]/social/calendar/_components/CalendarPage.tsx | 121 | deep-link auto-open | Visibility of System Status | handledRecapParamRef is set on the first run regardless of whether
history has loaded yet; because the effect returns early
once the ref matches, it never re-fires to fill in the real
title/headline once history resolves, leaving a generic 'Recap' header
permanently. |
Re-run the enrichment once history loads by keying the guard on whether a match was found, not just the param. | 3/3 |
| workflows-5 | P2 | low | workflows | Two delete-confirm patterns on one screen | src/app/[slug]/social/workflows/_components/WorkflowsDemoClient.tsx | 551 | selection mode (bulk delete) | Consistency & Standards | Deleting one workflow opens a Radix dialog that says "This can't be undone"; deleting several via selection mode just relabels the same pill button with no irreversibility copy at all. | Reuse the single-item confirm dialog, including its undo warning, for the bulk-delete path too. | 3/3 |
| workflows-6 | P2 | low | workflows | Draft-replace warning isn't a real dialog | src/app/[slug]/social/workflows/_components/CreateWorkflowView.tsx | 1466 | AI mode (regenerating over an existing draft) | Gestalt Figure-Ground | This confirmation is a plain div with role="dialog" sitting inline in the page — no scrim, no focus trap, no Escape handling — so it announces as a dialog to assistive tech while behaving like ordinary scrollable content. | Wrap this warning in the existing Radix Dialog component used elsewhere in this file. | 3/3 |
| trust-control-3 | P2 | low | trust-control | Add-key Save enables before name format is valid | src/app/[slug]/social/_components/chat/AgentActivityPanel/vault/AddSecretForm.tsx | 84 | add-secret form, custom provider | Error Prevention | onValidChange (:81-105) only checks customName.trim().length>0, so 'Add key' enables for names that violate SECRET_NAME_REGEX (e.g. containing a space); that regex is enforced only inside handleSave (:126), so the format error appears only after clicking Save. | Run validateSecretName on change and keep Save disabled until it actually passes, not just on non-empty. | 3/3 |
| trust-control-5 | P2 | low | trust-control | Access-level radio looks pickable but never is | src/app/[slug]/social/_components/chat/AgentActivityPanel/connections/ConnectionDetailView.tsx | 235 | connection detail view | Affordance | The Radio (:235-242) reuses the shared interactive Radio component, whose label keeps hover:bg-button-tertiary (Radio.tsx:20) regardless of the disabled input, so hovering the single non-changeable option still highlights it as though it were selectable. | Replace the disabled Radio with a static read-only label or badge instead of the interactive control. | 3/3 |
| trust-control-6 | P2 | low | trust-control | Icon-only connect button has no accessible name | src/app/[slug]/social/_components/chat/AgentActivityPanel/connections/ConnectionsTabContent.tsx | 431 | connections tab, discover list | Dual Coding | The Plus-icon-only button (:431-443) that starts an OAuth connect flow has no aria-label and no visible text, so screen-reader users hear only 'button' for a consequential, access-granting action. | Add aria-label={Connect ${task.title}} to the connect
button. |
3/3 |
| xc-1 | P2 | low | cross-cutting | High-stakes trust actions stay silent; low-stakes ones get toasts | src/app/[slug]/social/_components/chat/AgentActivityPanel/vault/AddSecretForm.tsx | 218 | success/default | Visibility of System Status | Saving a vault secret (AddSecretForm.tsx:218) and disconnecting an integration (ConnectionDetailView.tsx:123) give zero success/failure toast, while ChatroomSection.tsx:600 and CommandCenterTab.tsx:1715 fire a toastStore confirmation (often with Undo) for comparable or lower-stakes actions. | Wire vault-save and disconnect outcomes into the shared toastStore used by chat and Command Center. | n/a |
| xc-6 | P2 | low | cross-cutting | Legacy tertiary-text token fails contrast on two trust-control screens | src/app/[slug]/social/_components/chat/AgentActivityPanel/connections/ConnectionDetailView.tsx | 280 | default | Accessibility Contrast | ConnectionDetailView.tsx:280 and GroupSelector.tsx:66 both render text-content-tertiary (dark rgba(255,255,255,0.35) over dark panel surfaces) at 12-13px; computed against the surface hexes in the token reference this lands at roughly 3.2-3.4:1, below the 4.5:1 AA minimum for body text. | Swap text-content-tertiary for a token that clears 4.5:1 on dark surfaces, or bump to --text-2. | n/a |
| chat-4 | P2 | medium | chat | Composer toolbar shows nine formatting icons at all times | src/app/[slug]/social/_components/chat/ChatInput.tsx | 1830 | default (composer, empty and filled) | Progressive Disclosure — surface only what most messages need, defer the rest | All nine rich-text formatting buttons render unconditionally above every message, even for a one-line chat reply, adding persistent chrome that competes with typing and sending. | Collapse rarer formatting (quote, code block, lists) behind a single '…' overflow, keep bold/italic/link inline. | 3/3 |
| chat-list-4 | P2 | medium | chat-list | Hand-rolled all-chats dialog has no focus trap | src/app/[slug]/social/_components/ChatListView/components/BotSessionsPanel.tsx | 107 | modal ("All chats" panel) | User Control & Freedom | Unlike ui/dialog.tsx (Radix, focus trap included), this
panel is a raw fixed inset-0 div with only an Escape-key
listener (lines 74-81); Tab can move focus into the dimmed background
behind the scrim. |
Wrap the panel in the shared Radix
Dialog/DialogContent instead of a bespoke
portal div. |
3/3 |
| workflows-4 | P2 | medium | workflows | Create form errors stay hidden until submit | src/app/[slug]/social/workflows/_components/CreateWorkflowView.tsx | 1528 | default (filling the create form) | Error Prevention | Name, room, and flow-step errors (lines 1528, 1605, 1891) only render after the Create button is pressed, so a user filling this long form gets no feedback on missing fields until the end. | Validate required fields inline on blur instead of gating all errors on the submitted flag. | 3/3 |
| xc-2 | P2 | medium | cross-cutting | Confirm ceremony inverted: cheap actions get a modal, costly ones don't | src/app/[slug]/social/skills-and-apps/_components/BuildConfirmDialog.tsx | 165 | default | [HS] Friction Calibration | BuildConfirmDialog.tsx:157-172 puts a full Radix modal in front of a free, fully-reversible create action, while ConnectionDetailView.tsx:271 (revoke access) and RoomMemberDialog.tsx:260 (remove member) - harder to reverse - fire from a bare text button with no modal at all. | Reserve modal ceremony for the higher-consequence actions; add a lightweight confirm to disconnect/remove instead of the build flow. | n/a |
| xc-3 | P2 | medium | cross-cutting | Three unrelated button systems play the role of 'primary CTA' | src/app/[slug]/social/_components/chat/DeleteMessageModal.tsx | 146 | modal | Consistency & Standards | DeleteMessageModal.tsx:146 (bg-destructive, rounded-xl, text-body), BuildConfirmDialog.tsx:169 (bg-button-primary, rounded-token-md, text-subtitle) and ToolApprovalCard.tsx:59 (bg-accent-primary/90, rounded-lg, text-subtitle) each hand-roll the 'commit this action' button differently; core/form/PrimaryButton.tsx is used in only one file across all 8 units. | Route all primary/destructive confirm buttons through one shared button component or token set. | n/a |
| xc-4 | P2 | medium | cross-cutting | Four competing loading-skeleton treatments across units | src/app/[slug]/social/workflows/_components/WorkflowLoadingPlaceholder.tsx | 60 | loading | Consistency & Standards | WorkflowLoadingPlaceholder.tsx:60 uses .animate-qs-skel; ChatListItemSkeleton.tsx uses shadcn ui/skeleton.tsx; SkillsAndAppsMarketplace.tsx:1067-1070 and CallHistoryCard.tsx:194 hand-roll their own bg-wash animate-pulse divs - four different loading signifiers for the same 'content incoming' state. | Standardize every list/card loading state on one shared skeleton primitive. | n/a |
| xc-5 | P2 | medium | cross-cutting | Only marketplace survives a render crash without losing the shell | src/app/[slug]/social/skills-and-apps/_components/SkillsAndAppsPageClient.tsx | 15 | error | Error Recovery | SkillsAndAppsPageClient.tsx:15-32 scopes an ErrorBoundary around just the marketplace with a 'Reload' fallback that keeps the shell; workflows, calendar, chat, and trust-control have no local boundary, so a render error there falls through to layout.tsx:122's app-wide boundary, which replaces the entire UI with 'Return to Home' and loses the session. | Add the same scoped ErrorBoundary-with-Reload pattern around workflows, calendar, and the AgentActivityPanel tabs. | n/a |
| xc-7 | P2 | medium | cross-cutting | Same destructive-remove action confirms in chat, not in trust-control | src/app/[slug]/social/_components/chat/DeleteMessageModal.tsx | 45 | default | Consistency & Standards | DeleteMessageModal.tsx:45 wraps message deletion in a Radix confirm dialog, but RoomMemberDialog.tsx:260 (remove member) and ConnectionDetailView.tsx:271 (disconnect) - both at least as consequential - skip any dialog, firing straight from a text button. | Reuse the same confirm-dialog pattern for every irreversible remove/disconnect action. | n/a |
| chat-list-6 | P2 | high | chat-list | Seven nav rows sit above the conversation list | src/app/[slug]/social/_components/ChatListView/components/ConvergenceRailLayout.tsx | 904 | default (all optional surfaces enabled) | Miller's Law | When every feature flag is on, a user scrolls past up to 7 undifferentiated "lg" nav rows (lines 904-1005) before reaching agents/people/groups — competing for the same scroll real estate as the thing the rail is named for. | Group the app-destination rows under a single collapsible "Apps" header, matching the pattern already used for agents/people/groups. | 3/3 |
| chat-5 | P3 | low | chat | Model-selector button borrows the generic 'more options' icon | src/app/[slug]/social/_components/chat/ChatInput.tsx | 2330 | default (composer bottom toolbar) | Mental Models — icons should mean what users already expect them to mean | The dedicated 'Select model' action reuses the three-dot MoreHorizontal glyph, which every user reads as a generic overflow menu, not a model picker — tooltip/aria-label compensate but the icon itself misleads at a glance. | Swap to a model/chip-style icon (e.g. Sparkles or a labeled chip) distinct from the generic overflow glyph. | 3/3 |
| chat-list-2 | P3 | low | chat-list | Section delete uses a raw glyph, not lucide | src/app/[slug]/social/_components/ChatListView/components/ConvergenceRailLayout.tsx | 1416 | default (custom sections/folders) | Consistency & Standards | Section delete renders a literal "✕" character instead of the
X icon from lucide-react that RailRow.tsx already uses for
the same action one line of logic away, breaking the codebase's own
"lucide-react only" icon rule. |
Replace the "✕" span with the already-imported lucide X
icon at matching size. |
3/3 |
| chat-list-7 | P3 | low | chat-list | Empty people/groups sections offer no guidance | src/app/[slug]/social/_components/ChatListView/components/ConvergenceRailLayout.tsx | 1207 | empty (new user, zero DMs) | Recognition over Recall | When dmRooms/groupRooms are empty, the
section renders only its header (line 1199) with nothing beneath — no
"No conversations yet" copy, just a small unlabeled "+" the user must
notice on their own. |
Render a one-line empty message ("No conversations yet — tap + to start one") under each empty section. | 3/3 |
| chat-list-8 | P3 | low | chat-list | Sidebar resize handle has no rest-state affordance | src/app/[slug]/social/_components/ChatListView/components/DesktopChatListView.tsx | 176 | default (desktop sidebar) | Affordance | The resizer is a bare 5px div that only reveals itself via a hover color change (line 178); there is no grip icon or visible cue that the rail edge is draggable at rest. | Add a small vertical grip-dots icon at the strip's midpoint, visible at low opacity even without hover. | 3/3 |
| marketplace-6 | P3 | low | marketplace | Owner action row can overflow with no wrap | src/app/[slug]/social/skills-and-apps/_components/MarketplaceItemDetailDialog.tsx | 159 | detail dialog, owner of a published app with both preview and live running | Miller's Law | An owner viewing an approved, running app can see Share, Uninstall, Pause Preview, Pause Live, Re-publish, Unpublish and Open app together in one unwrapped row, which can overflow the dialog on narrower widths. | Add flex-wrap (or a horizontal scroll affordance) to the header actions container for owner-heavy states. | 2/2 |
| builder-7 | P3 | low | builder | Close and send icons are too small to tap reliably | src/app/[slug]/social/skills-and-apps/_components/BuildProgressPanel.tsx | 135 | building | Accessibility Contrast | The close control and the follow-up send button (size-7, line 260) both render well under the 44px accessibility hit-target minimum, on a panel used repeatedly during refinement. | Pad both icon buttons to a 44px square hit area without enlarging the icon glyph. | 2/2 |
| command-center-2 | P3 | low | command-center | 'Skip' and 'Close' silently disagree on your rating | src/app/[slug]/social/_components/chat/AgentActivityPanel/commandCenter/daily-brief/NegativeTagPicker.tsx | 68 | Daily brief thumbs-down tag picker open | Mental Models | With no tag selected, 'Skip' (line 70) submits the already-registered thumbs-down, while 'Close' (line 82) silently reverts it to unrated - same context, opposite outcomes, neither label warns of the difference. | Relabel 'Close' to 'Cancel rating', or make it submit an empty-tag rating like Skip. | 3/3 |
| command-center-3 | P3 | low | command-center | Undo link turns danger-red on hover | src/app/[slug]/social/_components/chat/AgentActivityPanel/commandCenter/HandledStrip.tsx | 59 | Handled Strip expanded | Color Psychology | The Undo control for a resolved item hovers to hover:text-danger, the app's error/destructive color, while the equivalent Undo/Restore controls in Awaiting Reply and Deferred & Hidden use a neutral hover - a safe, reversible action reads as risky. | Swap hover:text-danger for the neutral hover:text-content-1 used by the other Undo/Restore controls. | 3/3 |
| command-center-4 | P3 | low | command-center | Triage icon buttons skip aria-label | src/app/[slug]/social/_components/chat/AgentActivityPanel/commandCenter/CardOverflowMenu.tsx | 286 | Awaiting Reply card, overflow controls (default) | Dual Coding | All three triage icon buttons (lines 283, 361-372, 375-388) set only a title attribute; sibling controls in the same folder (CardCheckbox, RatingBar) set aria-label, making these three the accessibility outlier a screen-reader user hits first when triaging. | Add aria-label to the Clock, EyeOff, and Trash2 buttons matching their title text. | 3/3 |
| command-center-5 | P3 | low | command-center | 16px checkbox is the only way to resolve | src/app/[slug]/social/_components/chat/AgentActivityPanel/commandCenter/daily-brief/CardCheckbox.tsx | 22 | Every triage card, default view | Fitts's Law | The primary resolve control on every Waiting/Priority card is a 16x16px box with no extra padding in its wrapping div, well under common ~44px tap-target guidance for a control clicked dozens of times per session. | Wrap the checkbox in a larger invisible hit-area (e.g. p-2) without changing its visual size. | 3/3 |
| calendar-5 | P3 | low | calendar | Join button outranked by New Call button | src/app/[slug]/social/calendar/_components/LiveCallCard.tsx | 134 | live | Fitts's Law | Join — the single most time-sensitive action on the page — uses text-xs/px-3 py-1, smaller and lower-weight than the header's New call button (NewCallMenu.tsx:104), so urgency isn't reflected in size. | Increase Join's padding/font size to match or exceed New call's visual weight. | 3/3 |
| calendar-6 | P3 | low | calendar | Share-link dialog breaks the token system | src/app/[slug]/social/calendar/_components/ShareCallLinkDialog.tsx | 48 | share-link modal | Consistency & Standards | ShareCallLinkDialog uses legacy AIC 'icing' tokens (text-icing-text-primary, text-body, text-subtitle) while every other Calendar surface (CalendarPage, CallHistoryCard, LiveCallCard) uses Quiet Sky tokens (text-content-1, text-content-3), producing a visibly different modal in the same flow. | Restyle with the same Quiet Sky content/surface tokens used elsewhere in the Calendar unit. | 3/3 |
| calendar-7 | P3 | low | calendar | Live-refresh control is a 24px icon target | src/app/[slug]/social/calendar/_components/CalendarPage.tsx | 265 | live | Fitts's Law | The refresh control on the in-progress rail is a 24px icon-only hit target, below the 44px minimum, on a rail that updates a time-sensitive live-calls list. | Increase the button's clickable area to at least 44px while keeping the icon size. | 3/3 |
| calendar-8 | P3 | low | calendar | Three card actions share equal visual weight | src/app/[slug]/social/calendar/_components/CallHistoryCard.tsx | 218 | default (history list) | Von Restorff Effect | Recap, Scribe note, and the Room-navigation pill all use the identical bg-wash/border-line-strong pill style, so the card's most useful action (viewing the recap) has no more visual pull than a room-navigation link. | Give the Recap button a slightly stronger fill or weight than the Room/Scribe pills. | 3/3 |
| chat-list-5 | P3 | medium | chat-list | Dismiss control nests one button inside another | src/app/[slug]/social/_components/ChatListView/components/RailRow.tsx | 117 | default (any row with onDismiss) | Constraints | RailRow.tsx:50 renders the whole row as a native
<button>, then nests another interactive
role="button" span at line 117 inside it — invalid
interactive-in-interactive nesting that can confuse tab order and
screen-reader traversal. |
Render the row as a <div> with
onClick/role="button" so the dismiss control
is a true sibling, not a nested interactive element. |
3/3 |
| marketplace-5 | P3 | medium | marketplace | Build and publish dialogs use a different design language | src/app/[slug]/social/skills-and-apps/_components/BuildConfirmDialog.tsx | 137 | build confirm modal, mid-journey | Consistency & Standards | BuildConfirmDialog, PublishDialog and InstallNameConflictDialog use legacy rounded-token-md/lg corners and text-content-primary/secondary, while cards, rows and the detail dialog in the same flow use rounded-[14px] and text-content-1/3. | Restyle these three dialogs' radii and text colors to rounded-[14px] and text-content-1/2/3, matching the rest of the flow. | 2/2 |
| command-center-6 | P3 | medium | command-center | Recovery tray buried below Getting Started | src/app/[slug]/social/_components/chat/AgentActivityPanel/commandCenter/SnoozedHiddenSection.tsx | 22 | Inbox tab, bottom of scroll | Recognition over Recall | The only place to restore a snoozed or dismissed card starts collapsed and renders below Getting Started and Handled Strip (CommandCenterTab.tsx:2516) - far from Awaiting Reply where the action happened - and one label covers two different semantics (temporary snooze vs indefinite dismiss). | Auto-expand this section briefly right after a snooze/dismiss, or split it into two clearly labeled lists. | 3/3 |