docs: Phase 1 audit — TOKEN-AUDIT.md + COMPONENT-INVENTORY.md

Read-only audit of ui/src (token-auditor):
- TOKEN-AUDIT.md: every hardcoded color/spacing/radius/type/shadow value,
  frequencies, locations, near-duplicate clusters, exact-match cross-ref
  against ui/src/index.css tokens (only 6 exact hex matches, all in
  lib/status-colors.ts — confirms PRIOR-ART), DESIGN.md conflicts
  (radius lg/xl pinned to 0px making the scale non-monotonic; no shadow
  tokens exist), and a Needs-human-decision list (incl. the 3,115
  Tailwind palette-class sites scope question).
- COMPONENT-INVENTORY.md: 24 primitives (in sync with shadcn registry),
  206 feature components, 73 pages, verified duplicate leads, shadcn
  candidates — all consolidation/swap items recommendations-only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
scotttong 2026-07-06 13:50:13 -07:00
parent 1448946b7f
commit 38532283e9
2 changed files with 627 additions and 0 deletions

377
COMPONENT-INVENTORY.md Normal file
View File

@ -0,0 +1,377 @@
# 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
| 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 `IssueRow`s, 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 |
| `ProductivityReviewBadge.tsx` | Review-status badge |
**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.
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.
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.
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.
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`.
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).
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).
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.
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).
10. **Toast system has no installed shadcn primitive to compare against**`ToastViewport`/`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).

250
TOKEN-AUDIT.md Normal file
View File

@ -0,0 +1,250 @@
# TOKEN-AUDIT.md — Design Token Drift Audit (Phase 1)
Run scope: `ui/src/` only, on branch `design/token-extraction`. Read-only audit — no source files were modified. See `DESIGN.md` for the token layer contract and `PRIOR-ART.md` for prior findings (only 6/220 exact-mappable).
**Method:** ripgrep over `ui/src/**/*.{ts,tsx,css}`. Counts below are current as of this run (2026-07-06), not the PRIOR-ART numbers. Test files (`*.test.ts(x)`) are included in totals where noted but broken out separately — they are not shipped UI, but they do encode the same hardcoded values and will need companion updates if Phase 2 changes the values they assert against.
## Executive summary
| Category | Site count | Files | Existing-token exact matches |
|---|---:|---:|---:|
| Hex color literals (`#...`) | 206 (150 source / 56 test-only) | 51 | ~14 sites (case-insensitive match only — see below) |
| `rgb()/rgba()/hsl()/hsla()` literals | 52 | ~30 | 0 (all are opaque decorative gradients / canvas fills) |
| Tailwind arbitrary color brackets (`bg-[#..]`, `text-[#..]`, `border-[#..]`) | ~72 | ~10 | overlaps with hex count above (same literals, bracket-wrapped) |
| Tailwind palette utility classes (`bg-red-500`, `text-amber-600`, etc. — not literal hex but not project tokens either) | **3,115** | **145** | 0 (Tailwind's built-in oklch palette, never routed through `index.css`) |
| `text-[Npx]` arbitrary font-size | 730 | 137 | 0 |
| `tracking-[N em]` arbitrary letter-spacing | 202 | ~40 | 0 |
| `w-[...]` / `h-[...]` arbitrary size | 124 / 166 | ~60 combined | 0 (mostly px/rem/vh/dvh/calc, some `var(--radix-*)`) |
| `min-w/max-w/min-h/max-h-[...]` | 194 | ~45 | 0 |
| `p*/m*-[...]` arbitrary spacing | 14 | 9 | 0 (mostly `env(safe-area-inset-*)` and `calc(theme(spacing.N)-2px)`) |
| `gap-[...]` arbitrary | 8 | 2 | 0 |
| `rounded-[...]` arbitrary radius | 25 | 10 | 0 (4 of these are **stock shadcn registry values**, not local drift — see below) |
| `shadow-[...]` arbitrary shadow | 38 | 21 | 0 |
| inline `style={{ }}` literals with a hardcoded value | ~90 sites across 58 files | 58 | see cross-ref |
| Chart/status color constant arrays (`.ts`) | 6 files, ~35 distinct hex | 6 | 1 partial (`#2563eb`) |
Total distinct hardcoded-value **sites** (excluding the 3,115 Tailwind-palette-class sites, which are reported separately because enumerating each is not useful) is roughly **1,550** across color/spacing/radius/type/shadow categories. Including the Tailwind palette-class sites, the true "every visual value should be a token" count DESIGN.md principle 2 implies is **~4,650+**. This is far larger than PRIOR-ART's ~220 — either the codebase grew substantially since that audit, or that audit undercounted Tailwind arbitrary values and palette classes (it reported "~193 color / 23 radius / 7 type" — consistent with counting only hex/oklch color literals and rounded-brackets, not `text-[Npx]` or Tailwind palette-class usage). **Flagged for human decision**: whether Tailwind palette-class usage (`bg-red-500` etc.) is in scope for Phase 2 token extraction, since DESIGN.md principle 2 says "no hex, no raw px" but doesn't explicitly call out named Tailwind color classes, and mechanically tokenizing 3,115 sites is a materially bigger job than the rest of this audit combined.
---
## 1. Color literals
### 1.1 Hex colors — exact/near matches against `index.css` tokens
`index.css` status/brand hex values (all defined in `:root`, mode-independent — see DESIGN.md brand tier):
```
--status-agent-idle: #a8aeb2 --status-task-backlog: #a8aeb2 --status-task-cancelled: #a8aeb2
--status-agent-running: #2563eb --status-task-in_progress: #2563eb
--status-agent-paused: #f59e0b --status-task-todo: #f59e0b
--status-agent-error: #dc2626 --status-task-blocked: #dc2626
--status-task-in_review: #7c3aed
--status-task-done: #22c55e
--status-task-icon-backlog: #52585d --status-task-icon-todo: #cc7a00 --status-task-icon-done: #16a34a
--status-task-icon-cancelled: #52585d
(.dark overrides) --status-task-icon-backlog: #9a958a -todo: #fbbf24 -in_review: #9474f0 -done: #34d06f -cancelled: #9a958a
--paperclip-doc-annotation-highlight-*: #fef08a / #fde047 / #fef9c3 (light), #a16207 / #ca8a04 / #854d0e / #713f12 (dark)
--agent-1a..10b: 20 fixed brand hex (gradient stops), unique, not reused elsewhere in ui/src
```
**Exact matches found (case-insensitive value match) in `ui/src/lib/status-colors.ts`:**
| Hardcoded value | Site | Token it matches | Safe to swap without visual change? |
|---|---|---|---|
| `#A8AEB2` | `status-colors.ts:112,120,144` | `--status-agent-idle` / `--status-task-backlog` (`#a8aeb2`) | Yes — value identical, case differs only |
| `#2563EB` | `status-colors.ts:113,121,145` | `--status-agent-running` / `--status-task-in_progress` (`#2563eb`) | Yes |
| `#F59E0B` | `status-colors.ts:114,122,146` | `--status-agent-paused` / `--status-task-todo` (`#f59e0b`) | Yes |
| `#DC2626` | `status-colors.ts:115,123,149` | `--status-agent-error` / `--status-task-blocked` (`#dc2626`) | Yes |
| `#22C55E` | `status-colors.ts:147` | `--status-task-done` (`#22c55e`) | Yes |
| `#7C3AED` | `status-colors.ts:148` | `--status-task-in_review` (`#7c3aed`) | Yes |
| `#52585D` | `status-colors.ts:112,144` | `--status-task-icon-backlog` (light) (`#52585d`) | **Needs human decision** — token is mode-dependent (dark override `#9a958a`); this hardcoded value is used as a **fixed** chip background/text/border tint across both modes (it appears inside a class string with a separate literal `dark:` variant already, e.g. `dark:text-[#9A958A]`). The light value happens to equal the icon token; the surrounding hardcoded `dark:` variant (`#9A958A`) also happens to equal `--status-task-icon-backlog`'s dark override. So the *pair* is exactly reproducible via the token + its `.dark` override, but only if both the light and dark literals are replaced together — a partial swap would break one mode. |
| `#9A958A` | `status-colors.ts:112,144` | `--status-task-icon-backlog` `.dark` override (`#9a958a`) | Same caveat as above — pair with `#52585D` |
These 6 pairs (12 literal sites) in `status-colors.ts` are the largest concentration of **exact, likely-safe** matches found in this audit — consistent with PRIOR-ART's finding that exact matches are rare and cluster narrowly. All other hardcoded hex in the file (`#F5F3F0`, `#DBEAFE`, `#1D4ED8`, `#FEF3C7`, `#B45309`, `#FEE2E2`, `#991B1B`, `#DCFCE7`, `#188A3C`, `#EDE9FE`, `#5B21B6`, `#6E6960`/`#6e696024` alpha variants, and all the `dark:bg-[#...NN]` alpha-suffixed variants) have **no exact-value token match** — they are chip background tints (e.g., `#F5F3F0` light chip fill) that were hand-tuned independently of the base hue tokens and don't derive from them via any documented formula.
**`agentStatusBadge` (status-colors.ts:111-116) and `brandChipBadge` (status-colors.ts:143-149) are byte-for-byte identical maps** (same 4 shared keys: gray/blue/amber/red, same hex, same dark alpha suffixes) — flagged as a literal duplicate object, not just a duplicate value. `brandChipBadge` additionally has a `green` and `violet` entry `agentStatusBadge` lacks. **Needs human decision**: collapse `agentStatusBadge` into `brandChipBadge` (drop the redundant map) — this is a code dedup, not a token question, but it directly affects how many sites Phase 2 needs to touch.
### 1.2 Hex colors — no match, chart/status color arrays (mint new tokens)
Six files define standalone hex color arrays/maps for chart or dot rendering, none derived from `index.css`:
- `components/ActivityCharts.tsx:125-184` — priority colors (`critical #ef4444`, `high #f97316`, `medium #eab308`, `low #6b7280`) and a **second, differently-valued** status-color map (`todo #3b82f6`, `in_progress #8b5cf6`, `in_review #a855f7`, `done #10b981`, `blocked #ef4444`, `cancelled #6b7280`, `backlog #64748b`) that does **not** match `lib/status-colors.ts`'s `issueStatusColor`/`taskStatusVar` naming-to-hue mapping at all (e.g., `in_progress` is violet-ish `#8b5cf6` here vs. blue `#2563eb` in the canonical status system). **Flag: internal inconsistency**, not just missing tokens — this chart appears to use an entirely independent palette from the rest of the app's status system.
- `pages/OrgChart.tsx:162-169` — agent status dot colors (`running #22d3ee`, `active #4ade80`, `paused #facc15`, `idle #facc15`, `error #f87171`, `terminated #a3a3a3`) — again independent from `--status-agent-*` (compare `running`: `#22d3ee` here vs `#2563eb` token).
- `lib/timeline/layout.ts:131-136,162``TIMELINE_COLORS` (`delegated #5b9bf6`, `automation #f4b740`, `cancelled #9aa3ad`, `now #2dd4bf`) plus a runtime `hsl(hue 62% 52%)` per-issue hash color (line 162) — deliberately unbounded (hash-based), cannot be tokenized as discrete values; the 4 named constants can be.
- `pages/CompanySkills.tsx:549-551` — a 12-color palette array for skill/tag chips (`#6366f1, #0ea5e9, #10b981, #f59e0b, #ef4444, #8b5cf6, #ec4899, #14b8a6, #f97316, #22c55e, #3b82f6, #a855f7`).
- `lib/color-contrast.ts:9,71-72``DARK_BG {24,24,27}` (documented as zinc-900/`#18181b`), `TEXT_LIGHT #f8fafc`, `TEXT_DARK #111827` — contrast-calculation reference colors, functionally load-bearing (not decorative), needs care in Phase 2.
- `context/ThemeContext.tsx:20-21``DARK_THEME_COLOR #18181b`, `LIGHT_THEME_COLOR #ffffff` — sets the `<meta name="theme-color">` tag; same value family as `color-contrast.ts`'s `DARK_BG`.
- `lib/worktree-branding.ts:28,49` — fallback black/white pair (`#000000` / `#f8fafc` / `#111827`), same contrast-pair pattern as above.
**Cluster: 3 separate near-identical "readable text on dark/light" pairs** exist (`color-contrast.ts`, `worktree-branding.ts`, and implicitly `ThemeContext.tsx`'s theme-color), using `#f8fafc`/`#111827` twice and `#18181b`/`#020617`-family dark backgrounds inconsistently. **Flagged for human review** — looks like copy-paste convergence on the same Tailwind `slate-50`/`gray-900`-ish pair from three independent implementations; do not merge without checking each call site's actual contrast requirement.
### 1.3 Hex colors — project-color fallback cluster (widely repeated)
`#6366f1` (indigo) and `#64748b` (slate) are used as **default/fallback colors for user-configurable project colors** (`project.color ?? "#6366f1"` pattern) in 14 total sites:
- `#6366f1`: `pages/ProjectDetail.tsx:789`, `pages/CompanySettings.tsx:272`, `pages/CompanySkills.tsx:549`, `pages/PipelineSettings.tsx:2959,2974`, `plugins/bridge-init.ts:502,515`, `components/issue-properties/IssueProperties.tsx:181,1576,1649`, `components/NewIssueDialog.tsx:1489,1504` (+2 in test files)
- `#64748b`: `pages/Routines.tsx:751,766`, `components/MarkdownEditor.tsx:1355`, `components/RoutineRunVariablesDialog.tsx:426,441`, `components/RoutineList.tsx:121`, `components/IssueColumns.tsx:362`, `components/routine-sections/editable-sections.tsx:193,208`, `components/ActivityCharts.tsx:184` (also appears as the `backlog` chart color, see 1.2)
Neither matches an `index.css` token. **This is the single highest-value token-minting candidate in the whole audit** — one new `--project-color-fallback` (or two, if indigo/slate serve different call sites deliberately — `#6366f1` seems to be the "new project" default color picker seed, `#64748b` the "no project assigned" muted-slate fallback) would collapse 14+ sites. **Flag for human decision**: are these two meant to be the same fallback (a copy-paste drift) or intentionally different (new-project-color-picker-seed vs. no-project-slate)? The file-level pattern suggests two distinct call-site families (issue/project-creation UI uses indigo; routine/schedule UI uses slate), which argues for two tokens, not one.
### 1.4 Hex colors — miscellaneous singletons
- `components/ActivityFeed.tsx:286-288` and `components/FeedCard.tsx:432``#959596` (muted feed actor/verb text), 4 sites, 2 files. No token match. **This matches PRIOR-ART's called-out "muted feed text" gap cluster** — confirms it's still present and still untokenized.
- `components/OnboardingWizard.tsx:1722``bg-[#1d1d1d]` (dark decorative panel), singleton.
- `components/IssueChatThread.tsx:1451``bg-[#2563EB]` for "human's own message" bubble — **exact match** to `--status-agent-running`/`--status-task-in_progress` (`#2563eb`), but comment at line 1450 calls it "Liveness blue" independently — likely coincidental reuse of the same brand blue rather than a deliberate token reference. Flag as exact-match candidate but note the semantic mismatch (chat-bubble liveness vs. task-status liveness are different concepts that happen to share a hue).
- `pages/InviteUxLab.tsx:199,411``brandColor="#114488"` (lab/showcase-only, hardcoded prop value for a demo).
- `pages/CompanyEnvironments.tsx:427-431` — xterm.js terminal theme colors (`#0a0a0a`, `#f5f5f5`, `#22d3ee`, `#020617`, `#2563eb55`) — third-party terminal library config, arguably belongs on the documented allowlist (terminal chrome is intentionally distinct from app chrome).
- `fixtures/issueChatUxFixtures.ts:91``#0f766e` fixture data, out of runtime scope (test/demo fixture, not rendered UI chrome) but technically under `ui/src/`.
- `lib/mention-chips.ts:193``stroke="#000"` inside an inline SVG string (icon mask), singleton, likely intentional pure-black regardless of theme (needs human check).
---
## 2. `rgb()/rgba()/hsl()/hsla()` literals (52 sites, non-test)
Overwhelmingly **decorative gradient hero backgrounds** using `rgba(...)` inside Tailwind arbitrary `bg-[linear-gradient(...)]` / `bg-[radial-gradient(...)]` values:
- **UxLab/showcase pages** (majority of sites): `pages/IssueChatUxLab.tsx` (7), `pages/SystemNoticeUxLab.tsx` (5), `pages/InviteUxLab.tsx` (7), `pages/RunTranscriptUxLab.tsx` (2), `pages/ProfileSettings.tsx` (2) — these ARE routed (`/ux-lab/*`, `/design-guide`), not build-excluded, so they are in scope per DESIGN.md, but they are explicitly demo/showcase surfaces rather than product screens.
- **Production surfaces with the same gradient pattern**: `pages/Dashboard.tsx:222` (red budget-alert gradient), `pages/Costs.tsx:842` (subtle white gradient card), `components/AccountingModelCard.tsx:31`, `components/SidebarAccountMenu.tsx:163`, `components/BudgetIncidentCard.tsx:46`. Each gradient is a **unique** rgba tuple (no two production sites share the same gradient stops) — this is bespoke decoration per surface, not a systematic pattern, and is a strong "Needs human decision" candidate: minting one token per gradient (5+ new tokens) preserves pixels but does not reduce the "one way to say alert-card decoration" debt DESIGN.md principle 1 wants; collapsing them changes pixels.
- **Functional (non-decorative) uses**: `lib/mention-chips.ts:169``rgba(r,g,b,0.22)` computed at runtime from a hash-derived color (cannot be a static token). `components/CompanyPatternIcon.tsx:128,131``rgb(...)` canvas fill computed from props (same — dynamic, not tokenizable as a single value). `lib/timeline/layout.ts:162``hsl(hue 62% 52%)` runtime hash color (same). `components/FileViewerSheet.tsx:370,379``var(--paperclip-code-highlight-bg, rgba(250,204,21,0.12))` — this one **already has a CSS-var fallback pattern**, i.e., it's half-migrated: the var doesn't exist in `index.css` yet, only the inline fallback does. **Recommend this becomes the actual token** (`--paperclip-code-highlight-bg` / `-border`) in Phase 2 since the call site already expects it.
**Needs human decision**: whether one-off decorative gradients (Dashboard, Costs, AccountingModelCard, SidebarAccountMenu, BudgetIncidentCard, and all UxLab pages) should each mint a bespoke token (verbatim per DESIGN.md guardrails) or be added to the documented allowlist as "intentional decorative opt-outs" — minting ~15 near-duplicate gradient tokens that will never be reused elsewhere seems to work against the spirit of "tokens are the only source of visual values" even though it satisfies the letter of it.
---
## 3. Tailwind arbitrary bracket values (spacing / size / radius / type / shadow)
### 3.1 Font-size — `text-[Npx]` (730 sites, 137 files) — THE dominant cluster
| Value | Site count | Representative files (abbreviated where >10) |
|---|---:|---|
| **11px** | 417 | 104 files use it at least once. Heaviest: `pages/Secrets.tsx` (28), `pages/CompanySkills.tsx` (24), `components/transcript/RunTranscriptView.tsx` (20), `components/IssueChatThread.tsx` (17), `pages/TeamCatalog.tsx` (15), `components/IssueRunLedger.tsx` (15), `pages/AgentDetail.tsx` (12), `components/ProjectProperties.tsx` (12), `components/OnboardingWizard.tsx` (12), `components/IssueRecoveryActionCard.tsx` (12), + 94 more files |
| **10px** | 236 | 77 files. Heaviest: `components/IssueChatThread.tsx` (17), `components/transcript/RunTranscriptView.tsx` (15), `pages/TeamCatalog.tsx` (14), `pages/IssueDetail.tsx` (11), `components/CommentThread.tsx` (9), + 72 more |
| **13px** | 26 | `pages/IssueChatUxLab.tsx`, `pages/TeamCatalog.tsx`, `pages/Pipelines.tsx`, `pages/CompanySkills.tsx`, `pages/SystemNoticeUxLab.tsx`, `components/SidebarStarredProjects.tsx`, `components/SidebarAgents.tsx`, `components/SidebarProjects.tsx`, `components/SidebarAccountMenu.tsx`, `components/IssueChatThread.tsx`, `components/ArtifactsPanel.tsx`, `components/timeline/WorkTimelineChart.tsx`, `components/SidebarNavItem.tsx`, `components/Sidebar.tsx` |
| **12px** | 25 | `pages/CompanySkills.tsx`, `pages/SystemNoticeUxLab.tsx`, `plugins/launchers.tsx`, `components/DocumentDiffModal.tsx`, `components/DevRestartBanner.tsx`, `components/IssueAssignedBacklogNotice.tsx`, `components/IssueBlockedNotice.tsx`, `components/JsonSchemaForm.tsx` |
| **9px** | 13 | `pages/Secrets.tsx`, `pages/CompanySkills.tsx`, `components/AgentConfigForm.tsx`, `components/NewAgentDialog.tsx`, `components/OnboardingWizard.tsx`, `components/ActivityCharts.tsx`, `components/IssueFiltersPopover.tsx` |
| **15px** | 9 | `pages/Pipelines.tsx`, `pages/PipelineSettings.tsx`, `pages/IssueDetail.tsx`, `components/IssueDocumentsSection.tsx`, `components/PipelineItemBodyDocument.tsx`, `components/routine-sections/editable-sections.tsx`, `components/IssueAttachmentsSection.tsx` |
| **14px** | 4 | `components/IssueDocumentsSection.tsx`, `components/SourceResolvedFoldCallout.tsx`, `components/SystemNotice.tsx`, `components/IssueRecoveryActionCard.tsx` |
**FLAGGED CLUSTER — near-duplicate micro type scale.** This is the single largest and most consequential drift cluster in the codebase. 9/10/11/12/13/14/15px are all used, frequently in the *same component* for what appears to be the same semantic role ("small metadata label" vs. Tailwind's own `text-xs` = 12px baseline, which would cover several of these already). Sidebars alone (`Sidebar.tsx`, `SidebarNavItem.tsx`, `SidebarAgents.tsx`, `SidebarProjects.tsx`, `SidebarStarredProjects.tsx`, `SidebarAccountMenu.tsx`) all independently use `text-[13px]`, suggesting one genuine shared intent ("sidebar row label size") implemented as six separate arbitrary values instead of one. **PRIOR-ART's drafted "9 named `.type-*` intent styles incl. `micro` 11px / `nano` 10px" (see PRIOR-ART.md) directly targets this cluster** — this audit confirms the cluster is still present at large scale (653 of the 730 sites are 10px or 11px alone) and is the strongest candidate for that scale decision. **Do not merge here** — verbatim-extract each occurrence's exact px value into its own token per DESIGN.md Phase 2 guardrails; the human scale-collapse step (README "after the run" step 2) is where 9/10/11/12/13/14/15 get resolved into a real scale.
### 3.2 Letter-spacing — `tracking-[N em]` (202 sites)
| Value | Count |
|---|---:|
| 0.18em | 67 |
| 0.14em | 40 |
| 0.16em | 38 |
| 0.2em | 16 |
| 0.12em | 12 |
| 0.22em | 10 |
| 0.08em | 10 |
| 0.24em | 6 |
| 0.1em | 3 |
**FLAGGED CLUSTER** — 9 distinct tracking values across ~40 files, all in the 0.080.24em band (uppercase eyebrow/label letter-spacing, judging by co-occurrence with `text-[10/11px] uppercase` in the same class strings observed during sampling). Likely another case of "one intent, many literal values" — needs human collapse decision, not this run's job.
### 3.3 Width/height/min/max — arbitrary bracket values
`w-[...]`: 124 sites. `h-[...]`: 166 sites. `min-w/max-w/min-h/max-h-[...]`: 194 sites. Combined ~484 sites across roughly 90 files. Values are overwhelmingly **not near-duplicates of each other** in the way font-size is — they're bespoke per-surface panel/dialog/sidebar dimensions (`320px`, `220px`, `12rem`, `calc(100dvh-2rem)`, `85vh`, `var(--radix-popover-trigger-width)`, `var(--new-issue-dialog-height)`). A few small clusters worth flagging:
- **`30px`** appears as both a `w-[30px]` (4 sites) and `h-[30px]` (7 sites) — likely a consistent "icon button" footprint; check if these should route through a `size-*` token instead of duplicated w/h pairs.
- **`88px`** appears in both `w-[88px]` (2) and `h-[88px]` (4) — possibly the same avatar/tile footprint.
- **`220px`** (`h-[220px]`, 9 sites) and **`120px`** (`h-[120px]`, 7 sites) recur across otherwise-unrelated components (dialog/panel min-heights) — candidate for a semantic "compact panel min-height" token but flagged, not merged.
- `env(safe-area-inset-*)` and `var(--radix-*-trigger-width/height)` sites (≈15 total) are **not candidates for tokenization** — they reference runtime platform/library values, not design decisions; recommend allowlisting them explicitly rather than trying to wrap them in a token.
Representative file hotspots: `pages/CompanySkills.tsx` (16 `h-[`, 3 `w-[`), `pages/Pipelines.tsx` (9 `h-[`, 8 `w-[`), `components/OnboardingWizard.tsx` (6 `h-[`), `pages/CompanyImport.tsx`/`CompanyExport.tsx` (5 each).
### 3.4 Padding/margin — arbitrary bracket values (14 sites, small)
Mostly platform-safe-area handling, not design values:
- `pages/IssueDetail.tsx`, `components/MobileBottomNav.tsx`, `components/Layout.tsx` (×2), `components/RoutineRunVariablesDialog.tsx` — all `env(safe-area-inset-*)`**recommend allowlisting**, not tokenizing (platform-derived, not a design value).
- `components/ActivityFeed.tsx:p-[18px]`, `components/FeedCard.tsx:p-[18px]` — exact-duplicate 18px padding across 2 files, no token match — small clean mint candidate.
- `components/ui/tabs.tsx:p-[3px]`**stock shadcn value** (verified against current shadcn/ui registry — not local drift).
- `components/IssueChatThread.tsx:p-[15px]`, `components/IssueChatThread.test.tsx:p-[15px]` — matched pair.
- `components/IssueRow.tsx` — two `calc(theme(spacing.N)-2px)` expressions — these reference Tailwind's own spacing scale via `theme()`, arguably already "tokenized" in the loosest sense (derived from the Tailwind default scale, not a raw literal), but the `-2px` offset itself is a raw magic number. Flag for human review of intent.
### 3.5 Radius — arbitrary bracket values (25 sites)
| Value | Sites |
|---|---|
| `rounded-[28px]`, `rounded-[32px]`, `rounded-[24px]` | `pages/IssueChatUxLab.tsx`, `pages/ProfileSettings.tsx`, `pages/SystemNoticeUxLab.tsx`, `pages/InviteUxLab.tsx` (×6), `pages/CompanySettings.tsx` (`14px`) — all showcase/demo hero-card radii, no two pages agree on 24 vs 28 vs 32 |
| `rounded-[8px]` | `components/artifacts/ArtifactCard.tsx` (+test), `components/artifacts/ArtifactGroupCard.tsx` (×3) — internally consistent within the artifacts family |
| `rounded-[4px]` | `components/StatusBadge.tsx`, `components/ui/checkbox.tsx` (**stock shadcn value**, verified against registry) |
| `rounded-br-[4px]` / `rounded-bl-[4px]` | `components/IssueChatThread.tsx` — chat-bubble corner-clip, intentional asymmetric radius (speech-tail effect) |
| `rounded-[2px]` | `components/ui/tooltip.tsx` (**stock shadcn value** — tooltip arrow) |
| `rounded-[inherit]` | `components/ui/scroll-area.tsx` (not a literal value — keyword, skip) |
**Conflict with DESIGN.md — see section 6.** `--radius-lg` and `--radius-xl` are hard-set to `0px` in `index.css`'s `@theme inline` block, meaning every plain `rounded-lg` (188 uses) and `rounded-xl` (97 uses) class in the whole app currently renders **square, not rounded** — this is a live, current-state fact about the token layer itself, not a component-level drift issue, but it means the ~285 sites using `rounded-lg`/`rounded-xl` are silently at 0px and any future "fix" to those tokens will be a highly visible, non-zero visual change across most of the app.
### 3.6 Shadow — arbitrary bracket values (38 sites, 21 files)
Every value is a unique multi-stop `box-shadow` (drop shadows for hero cards, mostly `rgba(15,23,42,0.0X)` "cool black" tints at varying blur/spread). No two files share an identical shadow string except:
- `shadow-[0_24px_60px_rgba(15,23,42,0.08)]` (5 sites) and `shadow-[0_30px_80px_rgba(15,23,42,0.10)]` (3 sites) — both cluster around UxLab hero cards.
- `shadow-[0_-12px_28px_rgba(15,23,42,0.08)]` (3) and `shadow-[0_-12px_28px_rgba(0,0,0,0.28)]` (3) — same geometry, different color-mode tint (likely a light/dark pair that should have been expressed as one token with mode-aware color, not two separate arbitrary strings).
- `shadow-[0_1px_0_rgba(15,23,42,0.02)]` (3), `shadow-[0_0_0_2px_hsl(var(--background))]` (3) — the latter is interesting: it already references a CSS var (`--background`) inside an arbitrary value rather than a literal, i.e. partially tokenized.
Concentrated in: `pages/IssueChatUxLab.tsx`, `pages/RunTranscriptUxLab.tsx`, `pages/ProfileSettings.tsx`, `pages/InviteUxLab.tsx`, `pages/SystemNoticeUxLab.tsx`, `pages/AgentDetail.tsx`, `components/IssueThreadInteractionCard.tsx`, `components/ChatComposer.tsx`, `components/SourceResolvedFoldCallout.tsx`, `components/KeyboardShortcutsCheatsheet.tsx`, `components/LiveRunWidget.tsx`, `components/BudgetPolicyCard.tsx`, `components/BudgetSidebarMarker.tsx`, `components/SidebarNavItem.tsx`, `components/IssueRecoveryActionCard.tsx`, `components/environment-variables-editor/index.tsx`, `components/IssueChatThread.tsx`, `components/DocumentAnnotationLayer.tsx`, `components/CompanyPatternIcon.tsx`, `components/ActiveAgentsPanel.tsx`, `components/SystemNotice.tsx`.
**Needs human decision** — no existing shadow tokens exist in `index.css` at all (zero `--shadow-*` custom properties defined); every one of these 38 arbitrary shadows needs a brand-new token, and given how few are exact duplicates, this is close to "38 tokens for 38 sites" unless a human collapses them by visual similarity first.
### 3.7 Other bracket categories (small)
- `top/left/right/bottom-[...]` (14 sites): mostly `env(safe-area-inset-*)`, `50%`/`-50%` dialog-centering (shared with the stock shadcn `dialog.tsx`/`alert-dialog.tsx` pattern — **not local drift**), and one `top-[1px]` (`components/EntityRow.tsx:53`) hairline-alignment nudge.
- `z-[...]` (10 sites): `z-[1]`, `z-[2]`, `z-[9999]`, `z-[60]`, `z-[120]`, `z-[200]` — an ad hoc z-index scale with no documented tiers. **Flag**: no `--z-*` tokens exist; recommend a human-reviewed z-index scale decision, not in scope for this run's verbatim extraction beyond listing.
- `scale-[0.98]` (4 sites, all `pages/Inbox.tsx`) — consistent value, single file, easy mint.
- `blur-[2px]` (`components/IssueChatThread.tsx:3868`), `blur-[1px]` (`components/ChatComposer.tsx:253`) — 2 sites, 2 distinct values.
- `stroke-[2.3]` (`components/MobileBottomNav.tsx:109`) — SVG stroke-width, singleton.
---
## 4. Inline `style={{ }}` literals (58 files contain `style={{`)
Not every inline `style` is a hardcoded value — many pass through dynamic props (`style={{ width: size }}`). Filtering to literal-value cases:
- **Project-color fallback pattern** (`backgroundColor: x.color ?? "#6366f1"` / `"#64748b"`) — see section 1.3, 14 sites, already counted there.
- `components/AsciiArtAnimation.tsx:344``style={{ fontSize: "11px", fontFamily: "monospace" }}` — duplicates the 11px cluster (3.1) via inline style instead of Tailwind class; same value, different mechanism — worth noting Phase 2's codemod needs to handle both `text-[11px]` AND `fontSize: "11px"` forms.
- `components/MarkdownBody.tsx:193,197``borderRadius: "calc(var(--radius) - 4px)"`, `fontSize: "0.7rem"` — the radius line already routes through the `--radius` token (good pattern, not drift); the `fontSize: "0.7rem"` (=11.2px) is a **new near-duplicate of the 11px cluster** in yet another unit (rem vs. px) — flag for the human scale decision.
- `pages/CompanyEnvironments.tsx:422``fontSize: 12` (numeric, xterm.js option, third-party config — recommend allowlist).
- `pages/CompanySkills.tsx:579``fontSize: Math.round(size * 0.42)` — computed at runtime from a prop, not tokenizable as a static value.
- `components/WorktreeBanner.tsx:25``boxShadow: \`inset 0 -1px 0 ${branding.textColor}18\`` — dynamically computed from user branding color, not tokenizable.
---
## 5. Font-weight
No raw numeric `font-weight:` or `fontWeight:` declarations were found in component/page source outside `index.css` itself (which has 6 legitimate `font-weight: 500/600/700` declarations inside `.paperclip-markdown`/`.paperclip-markdown-codeblock-action` rules — these are the token layer, not drift). All font-weight in components goes through Tailwind's built-in `font-medium`/`font-semibold`/`font-bold` classes, which is compliant with DESIGN.md (weight isn't a token gap here). **No action needed for font-weight.**
---
## 6. Conflicts with DESIGN.md
1. **`--radius-lg` / `--radius-xl` are pinned to `0px`, and the base `--radius` is `0`, in the current `index.css`.** DESIGN.md principle 3 says "the final scale is a design decision made by a human after reviewing the token audit," implying the scale is still open — but the *current* values already silently zero out two full tiers of the radius scale sitewide (188 `rounded-lg` + 97 `rounded-xl` sites render square today). This isn't a conflict in the sense of a bug, but it does mean: (a) any component using `rounded-lg`/`xl` believing it gets a rounded corner is visually wrong today, and (b) `rounded-2xl`/`rounded-3xl` are NOT overridden (still Tailwind's stock 1rem/1.5rem), so the scale is non-monotonic as configured (`sm`=6px, `md`=8px, `lg`=0px, `xl`=0px, `2xl`=16px stock, `3xl`=24px stock) — a real inconsistency in the token file itself, not just in components. **Recorded here per DESIGN.md instruction to log conflicts rather than guess a fix.**
2. **`agentStatusBadge` and `brandChipBadge` in `lib/status-colors.ts` are literal duplicate objects** (see 1.1) — this isn't a DESIGN.md violation per se (DESIGN.md doesn't forbid duplicate non-visual code), but it directly undercuts principle 1 ("one way to say each thing") and inflates the token-migration site count; flagged here since Phase 2's codemods will otherwise "fix" the same value twice under two different export names.
3. **No `--shadow-*` tokens exist at all** in `index.css`, despite 38 arbitrary shadow values in components (3.6). DESIGN.md principle 2 lists "shadow" explicitly as a category that must route through tokens — today there is no token family for it to route through. Not a contradiction of DESIGN.md so much as a gap DESIGN.md anticipates Phase 2 will need to fill from scratch (mint, don't normalize).
4. **Tailwind palette utility classes** (`bg-red-500`, `text-amber-600`, etc., 3,115 sites) are a form of hardcoded value DESIGN.md's principle 2 language ("no hex, no raw px") doesn't unambiguously cover — these aren't hex literals or raw px, they're named utility classes backed by Tailwind's *own* built-in oklch palette, entirely separate from `index.css`'s token values. Whether this counts as "in scope" for the zero-hardcoded-value gate is genuinely ambiguous from the text of DESIGN.md and is the single biggest scope question for Phase 2. **See "Needs human decision" below.**
No other conflicts found — the rest of the codebase's approach (semantic tier for chrome, brand tier for agent/status colors, domain tier for chips/annotations) is followed consistently by the parts of the app that DO use tokens (e.g., `.status-chip`/`.status-fill` color-mix helpers, `.paperclip-mdxeditor` CSS-var bridge).
---
## 7. Off-limits / out-of-scope areas
- No `ui/src/components/theme-editor/` directory exists on this branch (confirmed via `find`) — KNOWN-DUPLICATES.md's off-limits note is currently moot here but preserved for when/if that code lands.
- No playground/experimental-theme paths found under `ui/src/` on this branch.
- Everything outside `ui/` (server, adapters framework config, CLI) is out of scope and was not scanned.
- `ui/storybook/` was inventoried only for the Phase-0 baseline-scope question (existing stories), not scanned for hardcoded values — it is fixture/test infrastructure, not shipped product UI.
---
## 8. Needs human decision (required section)
1. **Tailwind palette-class scope question** — are `bg-red-500`-style classes (3,115 sites / 145 files) in scope for Phase 2 token extraction? This is the largest single decision blocking Phase 2's actual size estimate. Recommend a separate, explicit ruling before Phase 2 starts, since it 10x's the mechanical work if in-scope.
2. **Micro type-size cluster (9/10/11/12/13/14/15px, 730+ sites)** — do not merge; this audit only inventories. PRIOR-ART's drafted 9-style `.type-*` system (incl. `micro`/`nano`) is the leading candidate for the eventual collapse decision. Needs a human to pick the real scale.
3. **Letter-spacing cluster (0.080.24em, 9 distinct values, 202 sites)** — same treatment as #2; no scale currently exists to collapse into.
4. **Radius token conflict (`--radius-lg`/`-xl` = 0px while `2xl`/`3xl` are untouched Tailwind stock)** — is the 0px lg/xl a deliberate brand choice (square corners) that should be preserved as-is, or a regression that should be fixed as part of the eventual radius-scale decision? PRIOR-ART's drafted scale (`sm 6 / md 8 / lg 10 / xl 14 / 2xl 16 / full`) assumes non-zero lg/xl — reconciling that draft with the current 0px reality is a human call.
5. **Project-color fallback duplication** (`#6366f1` indigo vs `#64748b` slate, 14 sites, section 1.3) — one token or two? File-level pattern suggests two distinct intents (new-project seed color vs. no-project-assigned muted slate) but this needs confirmation from whoever owns that UI, not an inference from this audit.
6. **`agentStatusBadge` vs `brandChipBadge` literal duplicate maps** (`lib/status-colors.ts`) — collapse to one export? This is a code-dedup call, adjacent to but not strictly a token-value question; flagged here because it changes Phase 2's site count.
7. **One-off decorative gradients/shadows on production surfaces** (Dashboard, Costs, AccountingModelCard, SidebarAccountMenu, BudgetIncidentCard — section 2; all of section 3.6) — mint ~20 bespoke, never-reused tokens (satisfies the letter of "tokens are the only source of visual values") or add a documented allowlist entry for "intentional one-off decoration" (satisfies the spirit of not inflating the token file with singletons)? DESIGN.md permits allowlisting "third-party overrides" but these are first-party decorative choices, so the allowlist criteria need a human ruling on whether it stretches to cover them.
8. **Chart color palettes disagree with the canonical status system**`ActivityCharts.tsx`'s per-status hex map does not match `lib/status-colors.ts`'s hue mapping (e.g. `in_progress` renders violet-ish in the chart, blue in chips/icons elsewhere). Is this an intentional "charts get their own palette" design decision, or drift that should eventually re-point at `--status-task-*`? Flagging only — not resolving, per DESIGN.md's out-of-scope "no visual redesign" rule.
9. **Contrast-pair triplication** (`color-contrast.ts`, `worktree-branding.ts`, `ThemeContext.tsx` all define their own light/dark text or theme-color hex pairs, section 1.2) — candidates for one shared constant, but each has slightly different call-site semantics (WCAG contrast math vs. `<meta theme-color>` vs. branding fallback); needs a human to confirm they're actually meant to be identical before consolidating.
10. **Test-file hardcoded values (56 hex sites, plus proportional shares of the other categories)** — Phase 2's codemods will need a policy on whether test files get rewritten in lockstep with the components they assert against, or left alone (asserting against literal values that no longer appear verbatim in source once tokenized). Not addressed here since DESIGN.md's Phase 2 spec is silent on test files.