think
16px
820px

Build report: dashboard UI polish — plain Carbon tables, descriptive copy, tone-coded numbers, live fleet health

Brief: .superpowers/sdd/ui-polish-brief.md · Date: 2026-07-08 · Branch: master

What shipped

A. Off @carbon/ibm-products entirely (the slider-chrome fix)

Root cause confirmed: the app never imported the products-library CSS, so its
Datagrid's column-resize <input type="range"> chrome rendered as raw native
sliders in every header, and column sizing was unmanaged (the cramped
Event-ID/ULID overflow). Fix = migrate, not patch:

  • src/components/operator/CallsDatagrid.tsx → plain Carbon DataTable
    (SLAView's exact JSX pattern, size="sm"). Rows are clickable AND keyboard
    operable (tabIndex=0 + Enter mirrors click); Time cells nowrap
    (.calls-grid__nowrap), Model allowed to wrap (long ids). Keyset Load
    more
    footer, loading skeleton / error notification / honest empty state all
    preserved. Component keeps its historical name so callers/tests are stable.
  • src/components/auditor/AccessLogTable.tsx → plain DataTable. Actor +
    Event ID mono cells are now white-space: nowrap (ULID can never spill into
    Action); container scrolls horizontally only when genuinely needed. Limit +
    Refresh toolbar and all four states unchanged.
  • src/views/OperatorView.tsx → the products-library SidePanel replaced
    with Carbon ComposedModal (ModalHeader/ModalBody, passive, size="md"),
    hosting the existing CallDetailPanel unchanged (trace fan-out + auditor
    "View bodies" deep link intact). Modal is conditionally mounted so closing it
    removes the detail from the DOM.
  • grep -rn "ibm-products" src/no matches (comments included). The
    dependency stays in package.json per the brief (nothing imports it, so it
    no longer ships in the bundle) — removable in a later chore commit.

B. Copy sweep — no API paths/params user-visible

All 12 listed captions/subtitles rewritten descriptively (SLAView ×4,
AnalyticsView ×3, ExecutiveView ×4, OperatorView subtitle). PredictView and
ReportView audited — no leaks existed there. Enforced by tests: each of the six
views (Executive, SLA, Analytics, Operator, Predict, Report) now has a
"renders no API paths or query params in user-facing copy" test asserting the
rendered document.body.textContent matches neither /\/api\// nor
/group_by/. Code comments and query-param identifiers keep their paths
(allowed by the brief).

C. Tone-coded numbers

  • src/lib/tones.ts (+ tones.test.ts) — the single home for thresholds:
  • errorRateTone (0..1): <5% good · 5–20% warning · ≥20% critical
  • latencyTone (ms): <2 s good · 2–10 s warning · ≥10 s critical
  • queueTone (ms): <1 s good · 1–10 s warning · ≥10 s critical
  • gpuUtilTone (pct): <80 good · 80–95 warning · ≥95 critical
  • src/components/charts/ToneNumber.tsx — a span coloured via Carbon
    support tokens in carbon.scss (--cds-support-success,
    --cds-support-warning-text fallback --cds-support-warning,
    --cds-support-error), so g10/g90 themes both track. Colour is the
    SECONDARY carrier — the figure text stays primary (StatTile's a11y rule).
  • Applied to: Overview error-rate tile (untoned at 0 calls — 0/0 is not a
    measurement), SLA fleet p95/p99 tile (toned by p95), SLA error-rate +
    GPU-utilization tiles, SLA per-engine table (p50/p95/p99/max → latencyTone,
    avg queue → queueTone), Analytics rollup error-rate cells. Predict's
    active-warnings tile already carried the exact rule (0 → good, ≥1 → warning,
    any critical severity → critical) — verified, unchanged.
  • Deliberately NOT toned: cache-hit rate (low isn't a fault), token counts,
    and every loading/unavailable/'—' cell (a tone asserts a real measurement).

D. Fleet health: stale P3 placeholder → live GPU strip

OperatorView now renders a compact live strip from client.getGPUNow() (60 s
refetch, retry: false): per GPU gpuN · util X% · mem U/T GB · temp C°C,
util tone-coded via gpuUtilTone. Honest states: empty list → "No GPU sample
in the last 2 min" (dead exporter ≠ error), query error → "GPU telemetry
unavailable", skeleton while pending — never a frozen number.

Tests

353 passed / 0 failed across 42 files (npx vitest run). Behavioral
assertions preserved and extended: row click + Enter-key open the detail
modal, keyset load-more/end-of-results, limit change refetch, all
loading/error/empty states, nowrap mono classes, tone assertions on tiles and
cells (including boundary 5% → warning), the three fleet-strip states, and the
six per-view no-API-copy tests.

Gates

  • npx tsc --noEmit — clean
  • npx vitest run — 353/353 green
  • npm run build — succeeds (pre-existing chunk-size warning only)
  • grep -rn "ibm-products" src/ — empty
  • Repo-wide /api/·group_by grep — hits only in code comments and API-client
    param identifiers (permitted); rendered copy enforced by tests

Judgment calls

  1. Kept @carbon/ibm-products in package.json — brief says dropping it
    from src/ (and therefore the bundle) is enough; flagged as removable.
  2. Comment wording — two file-header comments explaining the migration
    history referred to the old library by name; reworded ("products-library")
    so the strict grep -rn "ibm-products" src/ gate is literally empty while
    the why-history survives.
  3. Model column wraps, Time doesn't — per brief; long model ids would
    otherwise force the whole table wide.
  4. Error-rate tiles untoned at 0 calls — 0/0 isn't a measurement; tone
    only asserts on real figures (same contract as the '—' cells).
  5. ComposedModal is passive and conditionally mounted — the detail is
    read-only (no footer actions) and unmounting on close removes stale detail
    DOM instead of hiding it.