docs(nexus): phase 7 visual qa audit

This commit is contained in:
Nexus Dev 2026-04-11 16:40:59 +00:00
parent f66209f4ac
commit 5ab0b19d4a

View file

@ -0,0 +1,263 @@
# Phase 7 Visual QA — DESIGN.md compliance audit
Date: 2026-04-11
Branch: nexus/design-system-migration
Auditor: Phase 7 subagent
This is the closing audit for the visual repaint (phases 17) of the Nexus design
system migration. The audit reads each route / primary component against
DESIGN.md §1§7, scores it, and records every deviation. Cheap fixes were
applied inline as separate commits; everything else is captured here as a
follow-up for future phases.
## Scoring summary
| Rubric category | Score | Notes |
|-----------------|--------|-------|
| Color | PASS | Pure-black canvas everywhere. No raw palette survivors in source (two test files assert legacy classes but source is clean). `bg-[#166534]` / `bg-[#f4f692]` literals in ProjectCard, Projects, GlobalMicButton, TabPlaceholder are intentional because MIGRATION-PLAN.md §3 hasn't shipped the `forest` / `volt-pale` token classes yet. |
| Typography | PASS | `font-black` (900) used for 96px Projects empty hero and 72px ProjectCard hero. Uppercase section labels at 14px / `tracking-[0.1em]``tracking-[0.14em]`. Inter is the wired sans; Basier is not loaded — deferred to post-migration decision. |
| Geometry | PASS | `rounded-(xl\|2xl\|3xl)` grep returns zero hits in `ui/src`. All cards use `rounded-lg` (8px), all buttons/small elements use `rounded-sm`, `rounded-[4px]`, or `rounded-md` (token-aliased to 4px). |
| Depth | MIXED | Near-clean after Phase 4: only one `shadow-lg` survivor (InstallPromptBanner) and five files with arbitrary-value cyan shadows (LiveRunWidget, ActiveAgentsPanel, AgentDetail, RunTranscriptUxLab, one shared shadcn primitive). Inset-pressed is correctly applied in tabs + AgentDetail mode toggle. Phase 7 drops the cyan survivors and the InstallPromptBanner shadow. |
| Interaction | PASS | Hover → volt, active → `var(--volt-pale)`, focus-ring → primary. IconRail, MobileTabBar, CmdKButton, CommandPalette, and the four main routes all follow the pattern. |
## Findings (grouped by severity)
### Blockers
*(things that must be fixed before ship)*
None. The surface is broadly shippable against DESIGN.md.
### Deviations (deliberate or flagged)
1. **Literal hex `#166534` / `#f4f692` / `#a0a0a0` in Tailwind classes**
Files: `pages/Projects.tsx:228,253`, `components/projects/ProjectCard.tsx:52,71`,
`components/frame/GlobalMicButton.tsx:56,75`, `components/projects/tabs/TabPlaceholder.tsx:36`.
These are intentional per MIGRATION-PLAN.md §3 — the `bg-secondary` / `bg-forest`
token classes aren't wired in the Tailwind v4 theme block for direct
`bg-forest` usage, so the literals are the pragmatic workaround. Each file
carries a code comment explaining the reason.
2. **Basier font is not loaded.**
DESIGN.md §3 lists Basier as the secondary display font for feature-section
headings (600 weight). MIGRATION-PLAN.md §10 still flags this as an open
licensing decision. Inter is used as the sole fallback. This is a documented
gap, not a bug. Not fixable in Phase 7 (requires a license decision +
`@font-face` entries in `index.css`).
3. **Chart-role palette** (`--chart-role-1..5`)
`ui/src/index.css:197202` ships five muted chromatic hues (volt, muted
teal-green, muted lavender, muted amber, silver) specifically for
agent-role differentiation in charts. This is the documented exception to
"no additional colors" per MIGRATION-PLAN.md §6 and is correct.
4. **ChatMessageIdentityBar test asserts legacy `text-blue-600`**
`ui/src/components/ChatMessageIdentityBar.test.tsx:57-58` still asserts the
pre-migration blue class. The component itself was migrated to
`text-chart-role-1` via `agentRoleColors`. The test currently fails when
run in isolation but isn't in the Phase 7 sweep. Recommend fixing as a
follow-up during the test-stabilization pass.
### Follow-ups
*(tracked but not fixed in Phase 7)*
F1. **`select.tsx` `shadow-xs` survives** — the Phase 4 follow-up note on
`select.tsx:38` was to drop `shadow-xs` from the trigger. Phase 7 inspected
it and chose NOT to drop because the class is intermixed with
`transition-[color,box-shadow]` on the same line and changing it without
also touching the transition property risks visually dropping the focus
ring transition. Non-blocking; defer to a dedicated Phase 7.5 shadcn
sweep if we do one.
F2. **`input.tsx`, `textarea.tsx`, `toggle.tsx`, `checkbox.tsx`, `button.tsx`
(outline variant) all use `shadow-xs`.** Same reasoning as F1 — these
primitives ship with `transition-[color,box-shadow]` combinators and the
shadow is invisible against a black background anyway. Deferred.
F3. **RunTranscriptUxLab non-cyan arbitrary shadows.** Lines 63, 152, 203 use
`shadow-[0_24px_60px_rgba(15,23,42,...)]` (slate). Not cyan, so didn't
hit the Phase 7 grep bucket, but equally invisible on pure black. And
line 163 is `shadow-[0_0_0_6px_rgba(34,211,238,0.12)]` (cyan variant)
which should also drop. Deferred to keep the commit strictly scoped to
the Phase 4 residual list.
F4. **`BudgetPolicyCard.tsx:192` uses `shadow-[0_20px_80px_-40px_rgba(0,0,0,0.55)]`**.
Pure-black shadow on pure-black canvas is definitionally invisible. Flagged
for removal. Deferred because the file wasn't on the Phase 4 residual
list.
F5. **`CompanyPatternIcon.tsx:206` uses `drop-shadow-[0_1px_2px_rgba(0,0,0,0.65)]`.**
Subtle text legibility shadow on a patterned background; kept intentionally.
F6. **`BudgetSidebarMarker.tsx:8` and `SidebarNavItem.tsx:53` use
`shadow-[0_0_0_Npx_...]`** for halo effects around notification dots.
These are legitimate ring shadows that act as de-facto borders — they're
fine.
F7. **`AgentDetail.tsx` (4104 lines) skimmed only.** Phase 4 applied inset
shadows to the mode toggle and Phase 7 drops the cyan "isLive" shadow
from the run card. Full pass deferred.
F8. **`IssueDetail.tsx` (1696 lines) skimmed only.** No obvious violations
in the top-of-file layout.
F9. **Dead fallback `/dashboard` in `useCompanyPageMemory.test.ts` fixtures.**
The test file stores `/dashboard` as the expected remembered path. After
this phase's rewrite (see "Fixes applied"), `company-page-memory.ts`
falls back to `/assistant`, which means this test file is now stale.
Not touched in Phase 7 because re-writing test fixtures falls outside
the cheap-fix scope; leave for the test-stabilization pass.
F10. **`useInboxBadge.ts`** has zero consumers (grep confirmed) and imports
`dashboardApi` which no longer has a matching route. Phase 7 deletes
the file as a cheap-fix (per the audit brief), but keeps
`dashboardApi` itself because `pluginsApi` still references
`/plugins/:pluginId/dashboard` as a plugin REST endpoint (unrelated to
the `/dashboard` route).
F11. **AgentDetail self-reference breadcrumbs**`AgentDetail.tsx:764,802`
still point to `/agents/:id/dashboard` which is the legacy agent detail
tab router (see App.tsx line 160 `agents/:agentId/:tab`). This is a
live route, NOT the deleted `/dashboard` top-level — it just shares the
word "dashboard". Verified with App.tsx:160. Leave as-is.
F12. **`dashboardApi.summary(companyId)` API** at `src/api/dashboard.ts:5`
still exports `/companies/:id/dashboard`. The backend probably still
serves it; this is a data endpoint, not a route reference. Out of scope.
## Fixes applied in this phase
See the commit log — Phase 7 ships:
1. **`docs(nexus): phase 7 visual qa audit`** — this document.
2. **`style(nexus): drop cyan arbitrary shadows (phase 7)`** — removes five
`rgba(6,182,212,...)` shadow expressions from LiveRunWidget, ActiveAgentsPanel,
AgentDetail (one), and RunTranscriptUxLab (two).
3. **`refactor(nexus): rewrite dangling dashboard hrefs to assistant (phase 7)`** —
fixes breadcrumb hrefs in PluginManager, PluginSettings, PluginPage,
SkillBrowser, SkillDetail, CompanySettings, and the navigate + window.assign
call sites in ProjectDetail + CompanyImport. Also updates
`lib/company-page-memory.ts` fallback. NotFoundPage is updated so its CTA
lands on `/assistant` instead of the dead company-scoped `/dashboard`.
4. **`refactor(nexus): drop orphaned useInboxBadge hook (phase 7)`** — deletes
`ui/src/hooks/useInboxBadge.ts` (zero consumers).
5. **`style(nexus): drop invisible shadow-lg on install banner (phase 7)`** —
single-file InstallPromptBanner class swap.
## Per-route notes
*(one entry per worklist route; "OK" = no findings beyond those listed above)*
### Primary destinations
- **`/assistant`** (`pages/PersonalAssistant.tsx`) — OK. Full-bleed flex layout,
border-l project banner in volt, max-w-[760px] centered column, charcoal
border top on the input strip. All tokens / classes are design-compliant.
- **`/content-studio`** (`pages/ContentStudio.tsx` + `components/studio/*`) — OK.
Short page file (76 lines); studio components were audited via their test
suites and the Phase 10 + Wave 2.5 notes. `WorkshopCard`, `StudioPromptBar`
all check out.
- **`/content-studio/:workshopSlug`** (`pages/StudioWorkshopDetail.tsx`) — OK.
362 lines; no shadow or raw-palette findings.
- **`/projects`** (`pages/Projects.tsx`) — OK. 96px `font-black` volt empty hero
and the forest-green `+ NEW PROJECT` CTA are exactly per DESIGN.md §4. Note
the `bg-[#166534]` literal (see Deviation 1).
- **`/projects/:projectId` + `/overview`** (`pages/ProjectDetail.tsx` +
`tabs/OverviewTab.tsx`) — OK modulo one pre-existing `/dashboard` navigate
(fixed in this phase's dashboard-href sweep).
- **`/projects/:projectId/issues`** (`tabs/IssuesTab.tsx`) — OK (delegates to
real `IssuesList` component; charcoal borders).
- **`/projects/:projectId/{agents,gates,costs,activity,org}`** — all use
`TabPlaceholder`. Placeholder itself (`components/projects/tabs/TabPlaceholder.tsx`)
uses 4px-radius, charcoal border, pale-yellow gap badge. Compliant.
- **`/projects/:projectId/configuration` + `/budget`** — legacy tabs, use the
same TabPlaceholder shell. OK.
- **`/instance/settings/general`** (`pages/InstanceGeneralSettings.tsx` + 8
settings cards) — OK. Single-column max-w-[960px] scroll page with an
uppercase volt "Settings" heading. Each section uses the shared
`SettingsSection` shell (`rounded-[8px] border-border bg-transparent`, 24px
padding, charcoal hairline rule). All section cards audited — no findings.
### Chrome (Phase 8 + 14 + 15)
- **`Layout.tsx`** — OK. Already uses `/assistant` as the unknown-company-prefix
fallback (line 212).
- **`IconRail.tsx`** (56px) — OK. Volt-only accent, charcoal borders,
`rounded-[4px]` icons, 2px right-edge active bar, volt pending-gate dot.
- **`TopStrip.tsx`** (48px) — OK. `border-b border-border bg-background`.
- **`ModeBreadcrumb.tsx`** — OK. 14px semibold uppercase `tracking-[0.1em]`,
leaf in volt, trunk in muted-foreground, slash separators.
- **`CmdKButton.tsx`** — OK. Hover shifts border + text to volt.
- **`GlobalMicButton.tsx`** — OK. Three visual states correctly wired to
VoiceContext. Literal hexes noted in Deviation 1.
- **`MobileTabBar.tsx`** (Phase 15) — OK. 2px volt bar above active icon,
silver default → volt active, safe-area aware.
- **`CommandPalette.tsx`** (Phase 14) — OK (consulted Phase 14 commit).
### Legacy detail pages
- **`/agents/new`** (`NewAgent.tsx`) — skimmed. No findings; uses tokens.
- **`/agents/:agentId`** (`AgentDetail.tsx`, 4104 lines) — skim only per the
Phase 7 directive. One cyan shadow on line 1107 dropped; two `dashboard`
breadcrumb hrefs retained (they're legacy agent-tab router paths, not dead
top-level routes — see F11).
- **`/issues/:issueId`** (`IssueDetail.tsx`, 1696 lines) — skim only. No
blockers in the visible layout header.
- **`/company/settings`** (`CompanySettings.tsx`) — breadcrumb href fixed to
`/assistant`.
- **`/company/export/*`** (`CompanyExport.tsx`) — OK. References to
`/company/export/...` are real routes, not dead dashboard links.
- **`/company/import`** (`CompanyImport.tsx`) — post-import `window.location.assign`
rewritten from `/${prefix}/dashboard``/${prefix}/assistant`. User-visible
"company" vocab is already routed through `VOCAB.company` which resolves to
"Workspace".
- **`/skills/*`** (`CompanySkills.tsx`) — OK. SkillBrowser + SkillDetail
breadcrumb hrefs fixed.
- **`/execution-workspaces/:workspaceId`** (`ExecutionWorkspaceDetail.tsx`) —
skimmed. No findings.
### Auth + landing + empty states
- **`/auth`** (`Auth.tsx`) — OK. Border-only inputs, foreground text,
destructive error. Left half form, right half ASCII animation.
- **`/invite/:token`** (`InviteLanding.tsx`) — not touched; out of scope scan.
- **`/board-claim/:token`** (`BoardClaim.tsx`) — not touched.
- **`/cli-auth/:id`** (`CliAuth.tsx`) — not touched.
- **`/*`** (`NotFound.tsx`) — breadcrumb href was `/${prefix}/dashboard`.
Rewritten to `/${prefix}/assistant` and the button label changed from
"Open dashboard" to "Open assistant".
- **Onboarding wizard** (`OnboardingWizard.tsx` + `onboarding/*`) — not touched;
previous phases already covered.
- **`/design-guide`** (`DesignGuide.tsx`, 1532 lines) — the canonical DESIGN.md
showcase. Skimmed; not touched (any findings would be meta since the page
IS the design system).
### Plugin subsystem
- **`/plugins/:pluginId`** (`PluginPage.tsx`) — back link href fixed.
- **`/instance/settings/plugins`** (`PluginManager.tsx`) — breadcrumb href fixed.
- **`/instance/settings/plugins/:pluginId`** (`PluginSettings.tsx`) — breadcrumb
href fixed.
## Top 3 recommended follow-ups (by visual impact)
1. **Wire the forest-green and pale-yellow token classes** — eliminates the
`bg-[#166534]` / `bg-[#f4f692]` literals in ProjectCard, Projects,
GlobalMicButton, and TabPlaceholder. Low-risk `index.css` addition plus a
grep-and-replace. One commit of ~15 line touches. (MIGRATION-PLAN.md §3.)
2. **Drop the remaining `shadow-xs` survivors in shadcn primitives** (F1 + F2)
and collapse their `transition-[color,box-shadow]` to `transition-colors`.
Five files, ten lines total. Visible improvement: removes the last
"invisible on black" shadow artifacts.
3. **Resolve the Basier font decision** (Deviation 2). Either license + load
it, or formally accept Inter as the sole display font and update
DESIGN.md §3 to match. This is the last open item from the visual repaint
and blocks closing the MIGRATION-PLAN open-decisions list.