Skip to content

UI polish overhaul: faster console, work-centric Home, ⌘K, event log, instance name + run time limit - #1286

Closed
jayminwest wants to merge 27 commits into
mainfrom
ui/polish-overhaul
Closed

jayminwest wants to merge 27 commits into
mainfrom
ui/polish-overhaul

Conversation

@jayminwest

Copy link
Copy Markdown
Owner

Summary

This PR polishes the current Direction C UI rather than replacing it. It aims for a faster, more consistent console with a work-centric Home, plus the two APIs the UI was missing (plan pl-fae9, parent warren-468e). It also absorbs the defect and primitive steps of pl-10db that overlap.

  • Faster first load. Every route is lazy-loaded, the ⌘K palette (cmdk) is fetched on first open, and framer-motion is removed. The largest JS chunk drops from ~170KB to ~118KB gzip. The total rises because of route splitting, and the budget raise carries a note.
  • Fewer requests.
    • The lifecycle stream now invalidates the ops overview, so the shell's run counts come from the overview instead of a separate 200-row runs read.
    • Background polls stop while the tab is hidden.
    • GET /plan-runs now returns childStates, which removes the per-row detail read (N+1).
  • Smooth live run view. The event log keeps a bounded buffer and batches updates per animation frame. Run detail refreshes from its own event stream instead of polling every 3s.
  • Shell.
    • Keyboard: a ⌘K command palette, G-then-letter jumps, C for a new run, J/K to move through lists, ? for shortcut help, and ⇧T to switch theme.
    • Sidebar and navigation: an icon sidebar with counts and a primary "New run" button, and a phone bottom nav with icons.
  • Home is the index route. It shows Running now, Needs attention and Shipped, then a track record with a 30-day outcome chart and a day-grouped activity feed. Operations, Telemetry, Events and Instance stay top-level.
  • Shared primitives across every page: Card, PageHeader, StatusDot and StatusText, Segmented, Select, Textarea, Table, Skeleton, Kbd, and Tag. Legacy alias tokens and pixel literals are gone, and the type scale has an 11px floor.
  • Run detail gains a classified event log (views, filter, follow mode) and a stage timeline with live elapsed times. On a phone the timeline becomes a vertical stepper.
  • APIs (warren-a112):
    • GET /instance returns name (WARREN_INSTANCE_NAME) and publicUrl (WARREN_BASE_URL), shown on the Instance page.
    • Runs accept a per-run time limit (maxDurationMinutes), covering the dispatch field in both forms, storage, enforcement and the failure reason.
  • pl-10db defects fixed:
    • The telemetry histogram draws one column per day with a matching axis.
    • Hollow stretched cards now size to their content.
    • Phone layouts are fixed on project detail, dispatch and the runs list.
    • Captions that described implementation now use operator-facing copy.
    • Telemetry tab deep links work at phone widths.
    • The inline theme script, which CSP blocked, is now an external file.
  • Global CSS fix. The * border-colour rule moves into @layer base, so Tailwind border-colour utilities apply again.

Changes

  • src/ui/: the shell, all pages, the new components/ui primitives, pages/home/, the event log and stage timeline, the command palette and keys, and the tokens.
  • Server:
    • Instance facts: name and public URL.
    • Per-run timeout: src/runs/run-timeout.ts, dispatch plumbing, and schema and migrations.
    • childStates on GET /plan-runs (listChildrenForPlanRuns).
    • Regenerated OpenAPI, HTTP docs and CLI reference.
  • Compatibility: the UI treats childStates as optional. Against an older server, the plan-runs page shows progress as "—" instead of crashing.
  • Budgets: the bundle-size budget was re-baselined (deliberate raise, noted in $comment_route_split_2026_09_25). The coverage floor is unchanged; I added tests to hold it.

No Article IX protected paths are touched. This PR is labelled no-automerge because it needs human review.

Test plan

  • bun run check:all passes all 12 gates, after merging the latest main
  • biome check passes
  • tsc --noEmit passes, including tsc -b for the UI
  • bun test passes
  • Manual verification:
    • Screenshots at 1440 dark, 1440 light and 390 dark, against a live instance (GET-only proxy) and a local boot.
    • In the browser: ⌘K search, the G R chord, and the ? sheet.

Follow-ups filed:

  • warren-bd7c: filter /events by ?runId.
  • warren-1145: promote the page-local primitives into components/ui.
  • warren-d28b: remove the test-only deriveStageDurations and lazy-load setup.tsx.

… the legacy alias layer (warren-34be, warren-40f1)

- tokens.css: text-2xs..2xl scale carrying its own line heights, tracking
  tight/wide, radii 3/5/7/10px, status inks replace the shadcn
  *-foreground compatibility layer.
- Codemod: 514 text-[Npx], 172 leading-[Npx], 62 tracking literals and
  58 legacy colour aliases migrated to the scale and base names.
- index.css: 13px body, Inter cv11/ss01, thin palette scrollbars.
- The FOUC theme script moves to public/theme-init.js so the server's
  script-src 'self' CSP no longer blocks it; system theme is dark-first.
…nted/Kbd/Select/Textarea, drop framer-motion (warren-9474, warren-92c4)

- components/ui/status.tsx replaces status-indicator.tsx and the Badge
  status variants: stateTone, StatusDot (live dots breathe), StatusBadge,
  StatusText, PrChip; merged PRs get a purple merge tone.
- Card sizes to content (self-start), PageHeader, SkeletonRows with CSS
  shimmer, Segmented, Kbd, Tag (neutral labels).
- Input/Select/Textarea share one control surface with the 44px/16px
  phone treatment (new text-input token keeps iOS from zooming now that
  text-base is 14px).
- Table: 32px sentence-case header, 44px rows, tabular numerals.
- framer-motion and the unused motion layer are gone; CSS keyframes
  (pulse-ring, fade-in, pop-in, shimmer) replace them and honour reduced
  motion. JS drops from 692KB to 604KB raw; budgets re-baselined.
- src/ui/README.md gains the visual-language rules the pages follow.
Adds a per-run wall-clock cap modeled on the maxCostUsd spend cap:
dispatch override (POST /runs, POST /plan-runs, warren run /
warren plan-run --max-duration-minutes) > agent
frontmatter.maxDurationMinutes > project .warren/config.yaml default.
Dispatch folds the resolved value onto the frozen rendered_agent_json.

The heartbeat watchdog enforces it on every tick for all runtime
providers: a running run past started_at + cap gets a
duration.exceeded system event, a graceful provider.cancel, and a reap
as failed/timed_out. The deadline derives from persisted columns, so
it survives a restart.

Runs list/detail expose maxDurationMinutes (operator-only, like
maxCostUsd). plan_runs gains max_duration_minutes (migrations
sqlite 0057, postgres 0052). Retries and clone/rescue re-runs inherit
the parent's effective cap.
… a What-will-happen rail (warren-9474, warren-3eac, warren-3de0)

- Field/FieldRow/FormSection scaffolding (pages/dispatch/field.tsx), shared
  by both forms; both local controlClass strings are gone
- Resolved-manifest rails become a SummaryCard: operator-worded rows,
  verifiable checks only, raw manifest behind a disclosure, submit actions
- Provider/model row stacks below sm (warren-3de0)
- Timeout placeholder removed; slot marked for warren-a112
- Skeleton controls while projects/agents/plans load; formatError on
  submit errors; issue-status lookups settle before fetching
- Run-branch preview uses warren's real default prefix
- Bundle budget re-baselined (+1.5 KB gzip, within the auto-raise cap)
… routes (warren-a8c9, warren-44a2, warren-b2d6)

- Sidebar: icons replace mono indices, sentence-case sections, Search and
  New run actions, instance name in the brand row. Topbar: sentence-case
  figures and a shortcut-sheet button. Bottom nav: icons, Home first.
- ⌘K palette (cmdk, lazy chunk) plus G-chords, C, /, ?, Shift+T.
- Shell run counts read the ops overview instead of a 200-row runs list;
  the lifecycle stream now invalidates ops-overview.
- Home is the index route: now cards, track record with a 30-day chart,
  and a day-grouped activity feed with J/K selection.
- Every page is a lazy route chunk; entry chunk 170 KB -> 116 KB gzip.
- useTheme instances stay in sync through a window event.
…efresh (warren-4a47, warren-7d17, warren-9474)

- Event log: classified kinds, view Segmented, filter, follow mode with
  Jump to latest, memoized lines, content-visibility line groups.
- useEventStream: bounded 5000-event buffer with trimmed count,
  rAF-batched appends, incremental seq dedupe without re-sorting;
  follow latched at subscribe so a terminal flip no longer replays.
- Stage timeline stepper from the stage timestamps; header reads
  status, outcome, trigger, cost, model; useNow ticks elapsed.
- The 3s poll is replaced by event-driven invalidation with a 20s
  fallback.
- Side panels, steering and preview migrated to Card/Fact primitives.
- check:bundle-size re-baselined with --update (within auto-raise cap).
…log/alert/toast (warren-9474, warren-3eac)

- Auth gate: centered logo card for unreachable (retry + different token), breathing-logo splash.
- Error boundary: friendly card with try again, reload, copy error details.
- Login: shared Field/Input, fixes 'already signed in' shown to spectators.
- Setup: progress bar, whole-row step links, skeleton loading.
- Dialog/alert/toast: animate-pop-in/fade-in, neutral text on tinted surfaces (the -ink tokens are for filled backgrounds only); toasts clear the phone bottom nav.
- Bundle-size budget re-baselined (+1.4 KB raw, within the auto-raise cap).
…ist (warren-b2d6, warren-9474, warren-a8c9)

GET /plan-runs rows now carry childStates (one batched query), so the
list rows and phone cards no longer fetch each plan-run's detail. The
plan-runs page moves onto PageHeader/Card/Table/Select, J/K selection,
skeleton/empty/error states, and a 60s fallback poll. InventoryRowCard
gains a state prop driven by status tones.
# Conflicts:
#	scripts/bundle-size-budgets.json
#	src/ui/src/pages/setup.tsx
…tter (warren-9474, warren-a8c9, warren-3de0, warren-b2d6)

Runs table: status with failure reason, run id + seed, agent/model,
project, trigger tag, started, duration, cost, and delivery as a PR chip
or commit tag; runtime details move to the run-id tooltip. Filter bar on
Input/Select that collapses behind a Filters toggle on a phone, pager in
a card footer, skeleton/empty/error-with-retry states, a single 16px
phone gutter, and a 60s fallback poll behind the lifecycle stream.
…ctions on a phone (warren-9474, warren-a8c9, warren-3de0)

Projects registry becomes a Card/Table with J/K selection, icon row
actions and an empty state; project detail moves to PageHeader (actions
wrap at phone width), Card panels with human labels, skeleton and
error-with-retry states, outline dispatch links, and a 60s ready-plans
poll. The add-project picker drops its spinner for skeleton rows.
…ard rail (warren-0690, warren-9474)

- Child walk is a Card with one responsive row list (hover, run link,
  J/K + Enter via useListKeys), StatusText, PrChip, retry and gate
  Tags, sentence-case status lines; the parent row is items-start so
  the card no longer stretches below its footer (warren-dac1).
- Header: breadcrumb, mono id, StatusBadge, plan Tag, outline Cancel;
  meta line with a ticking elapsed.
- Rail uses FactCard/Fact; Delivery lists PrChips.
- Skeleton loading, error with retry; the 5s poll becomes a 30s
  fallback behind lifecycle-stream invalidation.
- ProblemNote replaces the danger/warning Alert on both detail pages
  (the Alert's -ink text tokens have no dark value); run-detail rail
  cards no longer shrink inside the height-bound aside.
- check:bundle-size re-baselined with --update (within auto-raise cap).
…en-e9cd)

Pad the run-analytics time series to one bucket per calendar day in the
window (weekly past 90 days) and derive the window from UTC midnight so
a 14-day view draws exactly 14 columns with a matching first/last axis.

Refs: warren-0690, warren-e9cd, warren-9474
Drop the viewport-width redirect: the tab strip scrolls sideways on a
phone and every /telemetry/<tab> route renders at any width. A source
guard in src/ui-tests pins that no telemetry module reads the viewport
to redirect.

Refs: warren-0690, warren-6bca, warren-9474
…shed language (warren-9474)

Operations: sans KPI strip, services panel, runs-by-state table on
StatusText, active workloads and recent events cards; state-tone.tsx
replaced by the status primitives. Cards size to their content
(warren-dac1). Telemetry: Card panels, Table primitive, Segmented
window, sentence-case tabs and operator-facing captions (warren-c023).
Event explorer: primitive filters, table rows with expandable payloads,
J/K + Enter, follow mode. Instance: Card facts page, spectator-hidden
fields read "Operator only"; the instance-name placeholder row is gone.

Perf (warren-b2d6): ops overview, runs, and recent events poll every
60s and never in the background; instance facts do not poll; the event
explorer polls every 5s only while following, never in the background.

Refs: warren-9474, warren-0690, warren-dac1, warren-c023, warren-b2d6
# Conflicts:
#	scripts/bundle-size-budgets.json
# Conflicts:
#	scripts/bundle-size-budgets.json
Also wires instance name and public URL into the Instance page
(warren-a112), layers the global border-colour rule so utilities win,
aligns the shell overview poll with the lifecycle stream, defaults
plan-run childStates for older servers, and covers the list and
dispatch helpers.
…tage stepper on phone

A server without childStates now shows a dash instead of "No children".
The run stage timeline stacks vertically below sm instead of scrolling
sideways (warren-7d17).
@jayminwest jayminwest added the no-automerge Human-gated PR: auto-merge workflow must never enable auto-merge label Sep 25, 2026
@jayminwest
jayminwest marked this pull request as draft September 25, 2026 20:33
@jayminwest jayminwest closed this Oct 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-automerge Human-gated PR: auto-merge workflow must never enable auto-merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant