paperclip/doc/design/COMPONENT-INVENTORY.md

42 KiB

COMPONENT-INVENTORY.md — Component Inventory (Phase 1)

Run scope: ui/src/components/ and ui/src/pages/ on branch design/token-extraction. Read-only audit — no source files modified.

All consolidation, merge, and shadcn-swap recommendations in this document are RECOMMENDATIONS ONLY. Per GOAL-PROMPT.md and DESIGN.md, no component merges, deletions, or swaps happen in this run. They become human-approved follow-up runs ("Run 2"/"Run 3").

Counts

Execution recovery reuses the existing transcript header and task status. Routine phases add no list badges, status cards, or reconciliation dialogs. Only a transient reconnection changes the header text. Automatic recovery decisions remain in the local run log. Storybook Tasks / Execution recovery demonstrates quiet task lists, native and legacy transcript headers, and dashboard composition.

Area Count
Shared primitives (ui/src/components/ui/) 24
Feature components, flat (ui/src/components/*.tsx) 178
Feature components, nested subdirs (access/, artifacts/, environment-variables-editor/, interrupt-handoff/, issue-output/, issue-properties/, routine-sections/, search/, timeline/, transcript/) 28
Feature components total 206
Pages (ui/src/pages/, incl. pages/secrets/) 73
Grand total 303 (roughly matches DESIGN.md's "24 + ~277")

1. Shared primitives — ui/src/components/ui/ (24)

All 24 checked against the live shadcn registry via npx shadcn@latest diff (network-available in this environment). Aggregate diff and per-component spot checks (button, dialog) both returned "No updates found" — these are currently in sync with the upstream registry source.

Component File Registry name Variants (props) Purpose
Alert Dialog alert-dialog.tsx alert-dialog (Radix primitive passthrough) Confirm/destructive-action modal
Avatar avatar.tsx avatar size (sm/default), shape (circle/square via data-shape) User/agent avatar with fallback initials
Badge badge.tsx badge variant (default/secondary/destructive/outline) Generic pill label
Breadcrumb breadcrumb.tsx breadcrumb (Radix/plain nav passthrough) Page breadcrumb trail
Button button.tsx button variant (default/destructive/outline/secondary/ghost/link), size (default/sm/lg/icon/icon-sm/icon-xs) CTA / action button, all tiers
Card card.tsx card Header/Title/Description/Content/Footer subparts Bordered content container
Checkbox checkbox.tsx checkbox (Radix passthrough) Boolean input
Collapsible collapsible.tsx collapsible (Radix passthrough) Expand/collapse section
Command command.tsx command Dialog/Input/List/Item/Group/Separator/Shortcut ⌘K palette primitive (backs CommandPalette)
Dialog dialog.tsx dialog (Radix passthrough), expandable max-width transition Modal dialog
Dropdown Menu dropdown-menu.tsx dropdown-menu Item/CheckboxItem/RadioItem/Sub/Separator/Shortcut Context/action menu
Input input.tsx input (native input passthrough) Text input
Label label.tsx label (Radix passthrough) Form field label
Popover popover.tsx popover (Radix passthrough) Floating panel
Radio Card radio-card.tsx not a standard registry name (custom, card-shaped radio option) Large selectable option card (onboarding/settings pickers)
Scroll Area scroll-area.tsx scroll-area (Radix passthrough) Styled scroll container
Select select.tsx select (Radix passthrough) Dropdown select
Separator separator.tsx separator orientation Divider line
Sheet sheet.tsx sheet side (top/right/bottom/left) Slide-in drawer
Skeleton skeleton.tsx skeleton (plain div passthrough) Loading placeholder
Tabs tabs.tsx tabs (Radix passthrough) Tab navigation
Textarea textarea.tsx textarea (native passthrough) Multi-line text input
Toggle Switch toggle-switch.tsx not a standard registry name (registry has switch) (custom on/off toggle) Boolean toggle control
Tooltip tooltip.tsx tooltip (Radix passthrough) Hover/focus hint

Note: radio-card and toggle-switch are not standard shadcn registry component names (the registry ships radio-group and switch respectively) — these were custom-built or heavily renamed/adapted rather than installed from the registry, so shadcn diff cannot check them against an upstream source. See shadcn-candidates section 4c.

Stock-value note: several "arbitrary Tailwind bracket values" flagged in TOKEN-AUDIT.md inside these primitives (checkbox.tsx rounded-[4px], tooltip.tsx rounded-[2px], command.tsx max-h-[300px], avatar.tsx text-[10px]) were verified to match the current shadcn/ui registry source verbatim — they are not local drift, just registry boilerplate that itself doesn't route through a token layer. Phase 2 will still need to touch them (DESIGN.md's gate is "zero arbitrary values in ui/src/components/**" with no carve-out for components/ui/), but they are not evidence of local customization.


2. Feature components — ui/src/components/ (206)

Grouped by rough domain area. One line each; variants column is props-based where notable, blank where the component is largely propless/single-purpose.

2.1 Issue / task surfaces (largest cluster)

Component Purpose
IssueRow.tsx Single row in a task list (status glyph, title, chips)
IssuesList.tsx List/board container rendering many IssueRows, sort/filter/group
IssueColumns.tsx Column-layout config for the issues list/board
IssueGroupHeader.tsx Section header when list is grouped (by status/project/assignee)
IssueProperties.tsx 1-line re-export barrel → issue-properties/IssueProperties.tsx
issue-properties/IssueProperties.tsx Full task detail properties panel (2,301 lines) — status, assignee, labels, project, dates
IssueChatThread.tsx Task comment/chat thread (agent + human messages)
IssueThreadInteractionCard.tsx Rich interaction card embedded in the chat thread (approvals, tool calls)
IssueRecoveryActionCard.tsx Recovery-action prompt card in a stalled/errored task thread
IssueScheduledRetryCard.tsx Scheduled-retry status card in task thread
IssueRunLedger.tsx Run/cost ledger table for a task
IssueMonitorActivityCard.tsx Monitoring/activity summary card on a task
IssueBlockedNotice.tsx Banner when a task is blocked
IssueAssignedBacklogNotice.tsx Banner when a backlog task gets assigned
IssueDocumentAnnotations.tsx / issue-output/ variants Doc-annotation highlight overlay on task documents
IssueDocumentsSection.tsx Documents tab/section on task detail
IssueAttachmentsSection.tsx Attachments tab/section on task detail
IssuePlanDecompositionsSection.tsx Sub-task/decomposition list section
IssueRelatedWorkPanel.tsx Related-issues panel
IssueReferenceActivitySummary.tsx / IssueReferencePill.tsx Inline task-reference chip + activity rollup
IssueSiblingNavigation.tsx Prev/next sibling task nav
IssueLinkQuicklook.tsx / IssuesQuicklook.tsx Hover-preview popover for a linked task / task list
IssueFiltersPopover.tsx Filter builder popover for the issues list
IssueWorkspaceCard.tsx Card summarizing a task's execution workspace
IssueContinuationHandoff.tsx Handoff-to-next-run card
NewIssueDialog.tsx Create-task dialog

2.2 Agent / execution

Component Purpose
AgentCapsule.tsx The brand "capsule" motif — 3-state agent avatar (slot/configured/online)
AgentActionButtons.tsx Start/stop/pause agent action row
AgentBubbleActionRow.tsx Action row (copy/vote/timestamp/menu) under a conf-room agent chat bubble — only one implementation found, confirming PRIOR-ART's concern about a duplicate is resolved
AgentConfigForm.tsx Agent configuration form (model, instructions, etc.)
AgentIconPicker.tsx Icon picker for agent avatars
AgentProperties.tsx Agent detail properties panel
ActiveAgentsPanel.tsx Sidebar/dashboard panel of currently-running agents
LiveRunWidget.tsx Live run status widget
RunChatSurface.tsx Shared chat-surface shell used by both conf-room and task chat
ClaudeSubscriptionPanel.tsx / CodexSubscriptionPanel.tsx Provider-specific subscription/quota panels — parallel, provider-specific (not a duplicate; see 3.4)
ProviderQuotaCard.tsx / QuotaBar.tsx Generic quota display card / bar

2.3 Chat / composer

Component Purpose
ChatComposer.tsx (380 lines) Shared lean composer (conf-room + reused where a single-line/simple composer suffices)
MarkdownEditor.tsx (1,425 lines) Rich MDX-based editor with mention autocomplete — task comment composer
OnboardingChat.tsx Onboarding-flow chat surface
CommentThread.tsx Generic comment-thread renderer (non-task contexts)
MarkdownBody.tsx / WorkspaceFileMarkdownBody.tsx Rendered markdown output (read-only) — general vs. workspace-file-scoped

KNOWN-DUPLICATES.md lead verified: ChatComposer vs MarkdownEditor-based task composer — confirmed genuinely different in scope (380 vs 1,425 lines; ChatComposer has no mention-autocomplete/MDX machinery). Per KNOWN-DUPLICATES.md this was a deliberate non-unification (PAP-101) — flagged in section 5, not recommended for merge.

2.4 Status / chips / badges

Component Purpose
StatusIcon.tsx Interactive status glyph + popover to change status (wraps StatusGlyph)
StatusGlyph.tsx Pure SVG glyph renderer per status (sm/md/lg), no interactivity
StatusBadge.tsx Custom <span> pill components: StatusBadge (generic run/goal/approval), AgentStatusBadge, heartbeat capsule — does not wrap the shadcn Badge primitive (see shadcn candidates 4a)
PriorityIcon.tsx Priority-level icon
ExternalObjectStatusIcon.tsx / ExternalObjectStatusSummary.tsx / ExternalObjectPill.tsx External-object (linked PR/doc/etc.) status glyph, rollup summary, and inline pill — a third, deliberately separate status-presentation family
BlockedReasonChip.tsx Chip explaining why a task is blocked
SourceTrustBadge.tsx / SourceResolvedFoldBadge.tsx / SourceResolvedFoldCallout.tsx Trust/fold badges for external content sources

KNOWN-DUPLICATES.md lead verified: StatusIcon / inline-mention chips / task chips are intentionally three separate systems (StatusIcon+StatusGlyph = task status glyph family; ExternalObjectStatusIcon/Pill/Summary = a second, external-object-specific family; mention chips in lib/mention-chips.ts + markdown CSS = a third, generic "chip in prose" family). Documented here per instruction, not merged.

2.5 Sidebar / navigation

Component Purpose
Sidebar.tsx / SidebarShell.tsx / SecondarySidebar.tsx / InstanceSidebar.tsx Primary/secondary/instance-scoped sidebar shells
SidebarSection.tsx / SidebarNavItem.tsx Sidebar section grouping + nav item row
SidebarProjects.tsx / SidebarStarredProjects.tsx Project list / starred-project list in sidebar
SidebarAgents.tsx Agent list in sidebar
SidebarAccountMenu.tsx / SidebarCompanyMenu.tsx Account and company switcher menus
SidebarServerInfo.tsx Server/instance info footer
RequestCollapsedSidebar.tsx Collapsed-state sidebar for request/approval views
RoutineSubSidebar.tsx Routine-scoped sub-sidebar
MobileBottomNav.tsx Mobile bottom tab bar
BreadcrumbBar.tsx (uses ui/breadcrumb) Page breadcrumb bar wrapper
PageTabBar.tsx In-page tab bar
CommandPalette.tsx ⌘K command palette (wraps ui/command)

Sidebar text-size cluster (cross-ref TOKEN-AUDIT.md 3.1): Sidebar.tsx, SidebarNavItem.tsx, SidebarAgents.tsx, SidebarProjects.tsx, SidebarStarredProjects.tsx, SidebarAccountMenu.tsx all independently use text-[13px] — six components implementing what looks like one shared "sidebar row label" intent as six separate arbitrary values. Flagged for the human type-scale decision, not merged here.

2.6 Pipelines / routines / goals

Component Purpose
PipelineHealthWarnings.tsx / PipelineLivenessBanner.tsx Pipeline health/liveness banners
PipelineItemBodyDocument.tsx / PipelineStageHistoryPanel.tsx / PipelineWorkReferences.tsx Pipeline stage detail panels
PipelinesExperimentalGate.tsx Feature-flag gate wrapper for pipelines
RoutineList.tsx / ManagedRoutinesList.tsx Routine list views
RoutineActivityRow.tsx / RoutineHistoryTab.tsx Routine activity row + history tab
RoutineRunVariablesDialog.tsx / RoutineVariablesEditor.tsx Routine run-variable input dialog/editor
RoutineSaveBar.tsx / RoutineTriggerCard.tsx Routine save-bar + trigger config card
routine-sections/editable-sections.tsx (+2 more) Editable routine-section blocks
ScheduleEditor.tsx Cron/schedule editor
GoalTree.tsx / GoalProperties.tsx / NewGoalDialog.tsx Goal hierarchy tree, detail panel, create dialog

2.7 Finance / budget

Component Purpose
AccountingModelCard.tsx / BillerSpendCard.tsx / FinanceBillerCard.tsx / FinanceKindCard.tsx / FinanceTimelineCard.tsx Various finance-dashboard cards — 5 distinct card shapes for different finance groupings (model/biller/kind/timeline); worth a human check for whether all 5 are truly distinct or 2-3 could share a base card
BudgetIncidentCard.tsx / BudgetPolicyCard.tsx / BudgetSidebarMarker.tsx Budget incident, policy config, sidebar marker
MetricCard.tsx Generic metric-display card

2.8 Files / documents / artifacts

Component Purpose
FileTree.tsx General file-tree renderer + buildFileTree/FileTreeNode model
PackageFileTree.tsx Thin wrapper around FileTree (wrapLabels default) — reuses FileTreeProps, not a duplicate
WorkspaceFileBrowser.tsx Execution-workspace file browser — defines its own parallel tree-node model (WorkspaceFileTreeNode, buildWorkspaceFileTree, compareTreeNodes) instead of reusing FileTree's FileTreeNode/buildFileTree — see suspected duplicates (3.2)
FileViewerSheet.tsx File content viewer sheet (wraps ui/sheet)
FileTree.tsx's parseFrontmatter Shared frontmatter parser (also exported, reused elsewhere)
DocumentAnnotationLayer.tsx / DocumentAnnotationPanel.tsx Document highlight/annotation overlay + side panel
DocumentDiffModal.tsx Document diff viewer modal
DocumentFrameHeader.tsx Header chrome for an embedded document frame
ArtifactFileChip.tsx / artifacts/ArtifactCard.tsx / artifacts/ArtifactGroupCard.tsx / ArtifactsPanel.tsx Artifact chip, card, grouped-card, and panel — internally consistent rounded-[8px] cluster (TOKEN-AUDIT 3.5)
ImageGalleryModal.tsx Image gallery lightbox modal

2.9 Onboarding / access / identity

Component Purpose
OnboardingWizard.tsx / OnboardingWizardVariant.tsx Onboarding flow + an alternate variant — worth a human check on whether the variant is still live or leftover from an A/B (see 3.5)
FrontDoor.tsx Landing/entry gate component
CloudAccessGate.tsx / ConferenceRoomChatGate.tsx / PipelinesExperimentalGate.tsx Three separate feature-flag/access gate wrappers — same pattern (children-if-enabled), each hand-rolled per feature; candidate for a shared FeatureGate primitive (flagged, not built here)
Identity.tsx Avatar + name identity chip (deriveInitials helper)
MembershipAction.tsx Membership accept/decline action row
access/ (2 files) Access-request related components
ReportsToPicker.tsx / ExecutionParticipantPicker.tsx / InlineEntitySelector.tsx / SearchableSelect.tsx Four distinct entity-picker components — overlapping purpose (pick a person/entity from a list); see suspected duplicates (3.3)
SecretBindingPicker.tsx / environment-variables-editor/ (5 files) Secrets/env-var binding UI

2.10 System / chrome / misc

Component Purpose
Layout.tsx App shell layout
EmptyState.tsx The single canonical empty-state component — confirmed only one exists, matching DESIGN.md principle 1
SystemNotice.tsx Global system notice banner
DevRestartBanner.tsx Dev-mode restart-required banner
ToastViewport.tsx + context/ToastContext.tsx Fully custom toast notification system — no shadcn sonner/toast primitive installed (see shadcn candidates 4a)
KeyboardShortcutsCheatsheet.tsx ⌘K-adjacent shortcuts help modal
PageSkeleton.tsx Loading skeleton (wraps ui/skeleton)
RouteErrorBoundary.tsx Route-level error boundary
ThemeToggle.tsx Light/dark toggle
CopyText.tsx Copy-to-clipboard text control
EntityRow.tsx Generic entity list row (used across several list contexts)
SwipeToArchive.tsx Mobile swipe-to-archive gesture wrapper
ScrollToBottom.tsx Scroll-to-bottom floating button
FoldCurtain.tsx Collapsible "show more" curtain/fade
InlineEditor.tsx Generic inline-edit-in-place control
JsonSchemaForm.tsx JSON-schema-driven dynamic form renderer
TrustPresetSection.tsx Trust-level preset picker section
WorktreeBanner.tsx Worktree-branding banner
CompanyPatternIcon.tsx Canvas-rendered company pattern/avatar icon
CompanySwitcher.tsx / CompanySettingsSidebar.tsx Company switcher + settings sidebar
ProjectProperties.tsx / ProjectTile.tsx / ProjectWorkspaceSummaryCard.tsx / ProjectWorkspacesContent.tsx Project detail panel, tile, workspace summary card, workspace content
ApprovalCard.tsx / ApprovalPayload.tsx Approval request card + payload renderer
ExecutionWorkspaceCloseDialog.tsx Close-workspace confirm dialog
ResponsibleUserDenialNotice.tsx Denial-notice banner
BootstrapPendingPage.tsx Bootstrap-pending full-page state
MissingPluginTabPlaceholder.tsx Placeholder when a plugin tab isn't installed
StandaloneBrowserControls.tsx Standalone-mode browser chrome controls
AsciiArtAnimation.tsx Decorative ASCII animation (boot/loading)
OpenCodeLogoIcon.tsx Static logo icon
StarToggle.tsx Star/favorite toggle button
OutputFeedbackButtons.tsx Thumbs up/down feedback buttons on agent output
KanbanBoard.tsx Kanban board view (alternate to IssuesList list view)
agent-config-primitives.tsx Shared field primitives for agent config forms
PropertiesPanel.tsx Generic properties-panel shell (used by Issue/Project/Agent/Goal Properties)

2.11 Nested subdirectories

Directory Files Purpose
access/ 2 Access-request UI
artifacts/ 2 ArtifactCard, ArtifactGroupCard
environment-variables-editor/ 5 Env-var/secret editor rows and index
interrupt-handoff/ 1 InterruptHandoffViews.tsx
issue-output/ 5 Task output file tiles / sections
issue-properties/ 5 Task properties panel (full impl, see 2.1)
routine-sections/ 3 Editable routine section blocks
search/ 3 Search result row, HighlightedText, MatchSourceChip
timeline/ 1 WorkTimelineChart.tsx
transcript/ 1 RunTranscriptView.tsx

3. Pages — ui/src/pages/ (73)

Grouped by area; one line each.

Page Purpose
Dashboard.tsx Home/overview dashboard
IssueDetail.tsx / IssuesList route pages Task detail + list routes
AgentDetail.tsx / Agents.tsx Agent detail + list
ProjectDetail.tsx / Projects.test.tsx-adjacent list page Project detail + list
Pipelines.tsx / PipelineSettings.tsx Pipeline list + settings
Routines.tsx Routine list/detail
Org.tsx / OrgChart.tsx Org directory + org chart visualization
Costs.tsx Cost/budget dashboard
Timeline.tsx Work timeline view (wraps timeline/WorkTimelineChart)
Inbox.tsx Notifications/requests inbox
Approvals.tsx / ApprovalDetail.tsx Approval queue + detail
CompanySettings.tsx / CompanySkills.tsx / CompanyEnvironments.tsx / CompanyAccess.tsx / CompanyImport.tsx / CompanyExport.tsx Company-scoped settings sub-pages
TeamCatalog.tsx Team/role catalog
UserProfile.tsx / ProfileSettings.tsx User profile view + settings
Secrets.tsx / secrets/* (5 files) Secrets management + sub-tabs (import-from-vault, definitions, my-secrets, presentation helpers, missing-banner)
PluginManager.tsx / PluginSettings.tsx Plugin management + per-plugin settings
AdapterManager.tsx Adapter (agent runtime) management
CloudUpstream.tsx / CloudUpstreamUxLab.tsx Cloud-upstream connection page + its UxLab showcase twin
InviteLanding.tsx / InviteUxLab.tsx Invite acceptance landing page + UxLab showcase twin
BootstrapSetupUxLab.tsx Bootstrap-setup flow showcase
ResponsibleUserDenialUxLab.tsx Denial-flow showcase
SystemNoticeUxLab.tsx System-notice showcase
IssueChatUxLab.tsx / RunTranscriptUxLab.tsx Chat + transcript showcases
DesignGuide.tsx Design-system showcase/reference page
BoardChat.tsx / BoardClaim.tsx Board (concierge) chat + claim flow
NotFound.tsx 404 page
CliAuth.tsx CLI auth handoff page
JoinRequestQueue.tsx Join-request queue
InstanceAccess.tsx / InstanceGeneralSettings.tsx / InstanceExperimentalSettings.tsx Instance-level settings pages
ExecutionWorkspaceDetail.tsx / ProjectWorkspaceDetail.tsx Execution/project workspace detail pages
Companies.tsx Multi-company switcher/list
CompanyImport.tsx / CompanyExport.tsx Import/export company data
RoutineDetail.tsx Routine detail route

UxLab pages are real routes (confirmed via App.tsx/ux-lab/*, /design-guide), not build-excluded demo code, so all hardcoded values inside them are in-scope per DESIGN.md even though they're showcase surfaces rather than product screens a typical operator visits daily.


4. Shadcn candidates

All items below are recommendations only — no swaps happen in this run.

4a. Custom components duplicating an available shadcn primitive

Custom component Duplicates Recommended replacement Expected visual impact
ToastViewport.tsx + context/ToastContext.tsx shadcn's sonner-based toast pattern (not currently installed) Install sonner/toast primitive, migrate useToastActions/useToastState call sites to it Low if the registry's toast is restyled to match current toneClasses/toneDotClasses tint system; the current implementation already has custom tone colors (sky/emerald/amber/red) that would need to carry over as variant props — a naive swap would look different unless those tones are preserved
components/StatusBadge.tsx (StatusBadge, AgentStatusBadge) shadcn Badge primitive (installed, ui/badge.tsx) Keep as a distinct component (status badges need the .status-chip color-mix mechanic Badge's variant system doesn't support) but consider having it render <Badge> internally with a custom class rather than a bare <span>, for consistency of base styles (focus ring, disabled states, etc.) Low — internal implementation change only if done carefully; skip if it risks the WCAG-tuned status hues
Hand-rolled bordered-container <div>/<ul> patterns (rounded-md border bg-card-shaped) found in ~26 files (pages/AdapterManager.tsx, pages/TeamCatalog.tsx, pages/InstanceAccess.tsx, pages/BootstrapSetupUxLab.tsx, pages/ResponsibleUserDenialUxLab.tsx, pages/CliAuth.tsx, pages/BoardClaim.tsx, pages/BoardChat.tsx, pages/InstanceGeneralSettings.tsx, pages/ProjectWorkspaceDetail.tsx, pages/Timeline.tsx, pages/PluginManager.tsx, pages/JoinRequestQueue.tsx, pages/InstanceExperimentalSettings.tsx, App.tsx, components/BootstrapPendingPage.tsx, components/IssuePlanDecompositionsSection.tsx, components/BlockedInboxView.tsx, components/ApprovalCard.tsx, + ~7 more) vs. shadcn Card (installed, only imported in 21 files) Card/CardContent Low-to-medium — many of these are <ul> list wrappers, not literal card content; a swap would need per-site judgment, not a blanket codemod. Flagging the cluster, not asserting every site should change.
Hand-rolled pill/badge-shaped <span>s (rounded-full px-2 text-[...]) outside Badge usage, ~34 files Badge (installed, 35 files already use it) Badge with a custom className for color Low if colors are preserved as className overrides
plugins/launchers.tsx generic plugin-shell overlay (role="dialog", hand-rolled backdrop bg-black/45, manual z-index math, rounded-xl/rounded-2xl) Dialog / Sheet / Popover (all installed) Case-by-case: this component multiplexes dialog/drawer/popover shell types from one plugin-host abstraction, which none of the three installed primitives do individually — a clean swap likely means keeping the multiplexer but delegating each shellType branch to the matching installed primitive instead of a fully custom <div> tree Medium — this is the most structurally custom overlay in the codebase; recommend closer human review before treating it as a simple swap

4b. Installed shadcn components drifted from the registry

npx shadcn@latest diff (run from ui/, network available) reported "No updates found" for the aggregate diff and for spot-checked button and dialog individually. No drift detected against the current registry for any of the 22 standard-named primitives. (radio-card and toggle-switch aren't standard registry names so diff cannot evaluate them — see 4c.)

4c. Raw Radix/plain elements where an installed shadcn wrapper exists

  • No raw @radix-ui/* imports were found outside ui/src/components/ui/ (rg -l '@radix-ui' -g '*.tsx' -g '!components/ui/*' returned zero files) — every Radix primitive in the app is properly routed through the components/ui/ wrapper layer. This is a clean result; no action needed.
  • One raw hand-rolled modal (plugins/launchers.tsx, role="dialog" on a plain <div>) exists where Dialog/Sheet wrappers are installed — see 4a above, same finding, cross-listed here because it's also a "plain element where a wrapper exists" case.
  • radio-card.tsx and toggle-switch.tsx are functioning as de facto custom primitives sitting in the ui/ folder alongside real shadcn components, but they were not installed via the registry (no matching registry names). Recommend a human decide whether to (a) leave them as intentionally custom, documenting why the registry's radio-group/switch don't fit, or (b) evaluate swapping to the registry versions if the customization was incidental rather than deliberate.

5. Suspected duplicates (with evidence)

All of the below are leads for human review, not verdicts, per KNOWN-DUPLICATES.md's framing.

5.1 Seed leads from KNOWN-DUPLICATES.md — verified

Lead Finding
ChatComposer vs MarkdownEditor-based task composer Confirmed genuinely distinct (380 vs 1,425 lines; different prop interfaces ChatComposerProps vs MarkdownEditorProps; MarkdownEditor owns mention-autocomplete machinery — findMentionMatch, computeMentionMenuPosition, placeCaretAfterMentionAnchor — that ChatComposer has none of). Matches KNOWN-DUPLICATES.md's note that this was a deliberate non-unification (PAP-101). Needs human decision: re-confirm this split should remain permanent, or revisit unification now that both have matured further.
AgentBubbleActionRow.tsx duplicate check Only one file found (components/AgentBubbleActionRow.tsx). The concurrent-work duplicate PRIOR-ART/KNOWN-DUPLICATES.md warned about is not present on this branch — resolved.
StatusIcon vs inline-mention chips vs task chips Confirmed three separate, intentionally distinct systems: (1) StatusIcon/StatusGlyph — task status glyph, drives from --status-task-icon-* tokens; (2) ExternalObjectStatusIcon/ExternalObjectPill/ExternalObjectStatusSummary — a parallel system for external (PR/doc/etc.) object status, independent color/severity model (externalObjectStatusIcon, externalObjectStatusToneSeverity in lib/status-colors.ts); (3) mention chips (lib/mention-chips.ts + .paperclip-mention-chip CSS in index.css) — generic "entity reference in prose" chip unrelated to status at all. Documented per instruction, not merged.

5.2 New leads found during this audit

Lead Evidence
FileTree.tsx vs WorkspaceFileBrowser.tsx tree models FileTree.tsx exports buildFileTree, FileTreeNode, countFiles, collectAllPaths, parseFrontmatter. WorkspaceFileBrowser.tsx independently defines its own WorkspaceFileTreeNode/WorkspaceFileTreeFolderNode/WorkspaceFileTreeFileNode, buildWorkspaceFileTree, compareTreeNodes, finalizeTreeFolder — a parallel tree-building implementation rather than reuse of FileTree's exported helpers. PackageFileTree.tsx by contrast correctly wraps FileTree (reuses FileTreeProps). Needs human decision: was WorkspaceFileBrowser's separate model a deliberate choice (different node shape needs — e.g. it may need workspace-specific metadata FileTreeNode lacks) or copy-paste-and-diverge drift?
agentStatusBadge vs brandChipBadge (lib/status-colors.ts) Byte-for-byte identical maps for the 4 shared keys (gray/blue/amber/red — same hex, same dark-mode alpha suffixes); brandChipBadge additionally has green/violet. Cross-referenced in TOKEN-AUDIT.md section 1.1. Recommend collapsing agentStatusBadge into brandChipBadge (or making it an alias) since they are provably identical, not just similar.
Entity-picker family: ReportsToPicker.tsx, ExecutionParticipantPicker.tsx, InlineEntitySelector.tsx, SearchableSelect.tsx Four components with overlapping "pick one entity from a searchable list" purpose. Not verified identical (each has domain-specific filtering — reports-to org hierarchy, execution participants, generic inline selection, generic searchable select) but the prop-surface overlap (all take an options list + selected value + onChange) is a plausible consolidation candidate. Flagged, not verified as true duplicates — would need a closer prop-by-prop diff in a follow-up run.

5.3 Corrections after closer inspection

  • OnboardingWizardVariant.tsx (10 lines) is not a duplicate of OnboardingWizard.tsx (1,786 lines) — it's a thin routing wrapper (export function OnboardingWizardVariant() { return <OnboardingWizard />; }) left over from a since-retired experimental-flag variant (per its own doc comment: "Conference-room chat is now the only surface left behind enableConferenceRoomChat; onboarding stays available without that experimental flag"). Both are routed in App.tsx. No action needed — this is a naming leftover, not visual/logic duplication; candidate for a trivial rename-and-inline cleanup in a future non-visual refactor, out of scope here.
  • components/IssueProperties.tsx is a 1-line re-export barrel (export { IssueProperties } from "./issue-properties";) — not a duplicate of components/issue-properties/IssueProperties.tsx, just a compatibility import path.

5.4 Finance card family — needs closer human review

AccountingModelCard.tsx, BillerSpendCard.tsx, FinanceBillerCard.tsx, FinanceKindCard.tsx, FinanceTimelineCard.tsx — five components with card-shaped, finance-dashboard purposes and naming that overlaps enough (BillerSpendCard vs FinanceBillerCard) to warrant a closer look than this audit had time for. Not verified as duplicates; flagged as a lead only.

5.5 Feature-gate wrapper pattern (not a duplicate, but a repeated pattern)

CloudAccessGate.tsx, ConferenceRoomChatGate.tsx, PipelinesExperimentalGate.tsx each independently implement the same "render children only if flag X is enabled, else render fallback" shape. Not byte-identical (each checks a different flag/hook), so not a strict duplicate, but a strong candidate for a shared FeatureGate/ExperimentalGate primitive that takes a flag-check function as a prop. Flagged as a recommendation, not built here.


6. Needs human decision (required section)

  1. ChatComposer vs MarkdownEditor-based composer — KNOWN-DUPLICATES.md already flags this as deliberately unmerged (PAP-101). This audit confirms the split is real (not accidental) given the large capability gap. Human call: keep permanently split, or revisit now. DECIDED (Run 2 review, DECISION-SHEET.md C1): keep the split, documented as deliberate — user re-confirmed after visual review.
  2. FileTree.tsx vs WorkspaceFileBrowser.tsx independent tree models — is WorkspaceFileBrowser's separate WorkspaceFileTreeNode model justified by different data needs, or is it drift that should be refactored onto FileTree's exported buildFileTree/FileTreeNode? Needs someone who knows both call sites' actual data shapes. DECIDED (Run 2 review, DECISION-SHEET.md C2): investigate data-shape needs as Run 3 prep; refactor onto FileTree only if shapes align. RESOLVED (Run 3 investigation): KEEP SEPARATE — shapes do not align; see §7.1.
  3. agentStatusBadge vs brandChipBadge (lib/status-colors.ts) — these are provably byte-identical for their 4 shared keys. Recommend collapsing, but doing so touches every call site importing agentStatusBadge, so it's a human-approved Run 2/3 item, not automatic. RESOLVED (Run 2 review, DECISION-SHEET.md A1): collapsed — agentStatusBadge had zero importing call sites and was deleted; brandChipBadge is the single map.
  4. Entity-picker family (ReportsToPicker, ExecutionParticipantPicker, InlineEntitySelector, SearchableSelect) — plausible consolidation candidate on prop-surface similarity alone; needs a closer prop-by-prop and behavior diff (not done in this pass) before any merge recommendation can be made with confidence. DECIDED (Run 2 review, DECISION-SHEET.md C3): prop-by-prop diff queued as Run 3 prep; no merge without it. RESOLVED (Run 3 investigation): all four KEEP SEPARATE — no copy-paste drift found; see §7.2.
  5. Finance card family (5 components, section 5.4) — needs a domain-knowledgeable human to confirm whether all 5 are truly distinct dashboard needs or 2-3 could share a base FinanceCard. DECIDED (Run 2 review, DECISION-SHEET.md C4): keep all five; revisit only if a sixth appears.
  6. Hand-rolled card-shaped containers vs. Card primitive (~26 files) and hand-rolled pill spans vs. Badge (~34 files) — both are large, low-risk-looking consolidation opportunities, but "low risk" was assessed at a glance only; a real swap pass needs per-site visual verification (this is exactly what Storybook snapshots from Phase 0 would catch if these were touched). DECIDED (Run 2 review, DECISION-SHEET.md C5): queued for Run 3 as the shadcn-swap list, with per-site snapshot verification.
  7. plugins/launchers.tsx custom multiplexed overlay — the most structurally custom modal-like component in the app; recommend a dedicated closer look before deciding whether/how to route it through Dialog/Sheet/Popover, since it currently does something none of the three do alone (switch shell type per plugin action). DECIDED (Run 2 review, DECISION-SHEET.md C6): dedicated review task; excluded from Run 3.
  8. radio-card.tsx / toggle-switch.tsx non-standard "shadcn" primitives — confirm whether these were deliberately custom-built (and should stay documented as such) or are stale/incidental deviations from radio-group/switch that should be swapped in a later run. DECIDED (Run 2 review, DECISION-SHEET.md C7): deliberately custom — documented as such; no swap.
  9. StatusBadge/AgentStatusBadge not wrapping the installed Badge primitive — worth a human call on whether unifying the base markup (while keeping the custom .status-chip color-mix mechanic) is worth the churn, or whether the current bespoke <span> approach should just be documented as an intentional, permanent exception (similar to the StatusIcon/ExternalObject/mention-chip three-way split already documented). DECIDED (Run 2 review, DECISION-SHEET.md C8): documented as an intentional exception — the WCAG-tuned .status-chip mechanic stays bespoke.
  10. Toast system has no installed shadcn primitive to compare againstToastViewport/ToastContext is fully custom because no sonner/toast component is installed at all. Human call: install one and migrate, or formally document the custom toast as the system's permanent choice (it already has a working tone/variant system). DECIDED (Run 2 review, DECISION-SHEET.md C9): decision deferred to Run 4, when the toast's palette colors get retokenized; sonner-behind-a-pushToast-facade is the alternative to evaluate then.

7. Run 3 investigation verdicts (C2/C3)

Investigated Jul 7 2026 on design/component-convergence per DECISION-SHEET.md C2/C3. Both are read-only verdicts; no merges executed (neither met the "copy-paste drift with identical data shapes" bar).

7.1 C2 — WorkspaceFileBrowser vs FileTree tree models: KEEP SEPARATE

Deliberate divergence, not copy-paste drift. No shared git ancestry: git log --follow shows FileTree.tsx originating in 3c73ed26b (plugin host surface, #5205) and WorkspaceFileBrowser.tsx independently in 468edd8b2 (workspace file viewer, #7681); no rename/copy relationship, and the only commit touching both is the mechanical token retune (c07e650cd).

Data shapes are distinct, not overlapping:

  • FileTreeNode (FileTree.tsx:18-25): kind: "dir"|"file", keyed on path, children on every node, optional action string. Built by buildFileTree(files: Record<string, unknown>) — a flat path→content map fed static blobs (CompanyExport.tsx:696/762/832, CompanyImport.tsx:783/903, AgentDetail.tsx:1902).
  • WorkspaceFileTreeNode (WorkspaceFileBrowser.tsx:211-227): kind: "folder"|"file" (different literals), composite key = kind:workspaceId:relativePath, baked-in depth, folder-only lazy flag, and file nodes embed the entire WorkspaceFileListFileItem server DTO (~15 fields: capabilities, previewKind, byteSize, download URLs — packages/shared/src/types/workspace-file-resource.ts:50-94). Built from an array of server DTOs with rootPath prefix-stripping.

Behavior domains differ: FileTree is an eager, static tree with tri-state checkboxes, badges/tones, frontmatter parsing, and a full roving-tabindex keyboard grid; WorkspaceFileBrowser is a server-paginated live browser with lazy folders, per-folder "load more" via useQueries, a debounced search combobox, and opposite sort order (folders-first vs FileTree's deliberate files-first, FileTree.tsx:96-99). Neither uses the other's load-bearing features; a merge would be a superficial shared-recursion abstraction both sides immediately override.

7.2 C3 — Entity-picker family: all four KEEP SEPARATE

No copy-paste lineage: four independent creation commits by different efforts — InlineEntitySelector (3709901db, Feb 26), ReportsToPicker (61f53b647, Mar 20), ExecutionParticipantPicker (be518529b, Apr 7, extracted from inline pill UI), SearchableSelect (50ae8fc65, Jun 24, #8597, born cmdk-based). git log --follow shows no cross-file rename/copy.

Per-component rationale:

  • ReportsToPicker: no search, no keyboard nav, domain Agent[] single-select with unique manager-state UI (terminated-manager banner, stale-ID handling, ReportsToPicker.tsx:92-104). 2 call sites. A thin bespoke widget, not a drifted copy.
  • ExecutionParticipantPicker: the only multi-select (toggle set) and the only one with async data fetching (useQuery user directory); encodes selections as agent:<id>/user:<id> tagged strings coupled to execution-policy plumbing. Not behavior-preservingly mergeable into any single-select.
  • InlineEntitySelector vs SearchableSelect (the only real candidate pair): different engines (hand-rolled input + manual Arrow/Enter/Tab keyboard machine with tab-advance chain driving NewIssueDialog's field flow, InlineEntitySelector.tsx:154-189/90-101 — vs cmdk Command shouldFilter={false}, SearchableSelect.tsx:246); different option shapes ({id,label,searchText} flat + synthetic none-row vs {key,value,label,title,searchText,disabled} grouped + generic <TValue,TOption>); each has unique load-bearing features (recent-ordering/onConfirm vs createItem/deriveGroups/scoreOption/veto-close). ~33 call sites on InlineEntitySelector incl. the plugin bridge (plugins/bridge-init.ts:389/479). High-risk, low-reward rewrite — default keep-as-is holds.

7.3 Interactive-card affordance (Run 3 review feedback)

Card gained an interactive prop — pointer cursor, quiet hover (border→foreground/20 + shadow-md lift), focus-visible ring — used when the whole card is a click target (e.g. Companies selector). Skills tiles (CompanySkills SkillCard) and artifact cards (ArtifactCard/ArtifactGroupCard) apply the same recipe verbatim since they cannot render through Card (button/Link semantics). Static container Cards stay affordance-free by design.

In-task connections — 2026-09-07

Reusable surface Production owner Hosts / coverage
Connection request card ui/src/features/connections/ConnectionIntentInteractionBody.tsx Task timeline, interaction card, design guide; pending, reuse, authorizing, retry, resolved, audience and error stories
Connection setup flow ui/src/features/connections/ConnectionSetupFlow.tsx Connections page and task dialog share provider forms, OAuth, validation and additive installs
OAuth handoff OAuthConnectStateScreen in the shared setup module Entry, starting, open window, blocked popup, closure, callback failure, retry and new-tab fallback
Identity and agent access AccessStep in the shared setup module Personal, organization, dedicated agent, unavailable identity and loading; task host fixes install reach to the requester
Setup completion ConnectionSetupCompletionScreen in the shared setup module Page and dialog; identity, granted agent access and enabled actions

Independently addressable examples live under Connections/In-task connections in Storybook. The task composer remains available while a card is pending. These components use the existing token and primitive layers.