ADR-081: UI shell consolidation grammar — scopes, card shape, empty-state prose
Status: Proposed (amended 2026-04-28) Date: 2026-04-24 Extends: ADR-054 (three-mode routes), ADR-058 (five-mode platform UI), ADR-059 (project-first IA), ADR-062 (design-system + component strategy), ADR-065 (persona-aware IA + niceties), ADR-066 (microvisualisations), ADR-070 (playbook UI contract)
Context
The Platform UI (ai-frontend) has reached ~70 shipped routes across the
five modes (Home / Ship / Operate / Infra / Experiment), the persona
homes (/me, /ops, /team), a growing set of organisation-level
fleet routes (/projects, /playbooks, /docs, /pipelines,
/operators, /assets, /build, /patches), and the project trees
(/[project], /platform/[project], /restricted/[project] and
their tabs). Each landed in its own PR, each solved its own local
hierarchy; the aggregate reads as "assembled by many agents" rather
than "built by one team".
On 2026-04-24 a UI consolidation audit of the shipped surface
(ai-frontend/docs/ui-consolidation-audit-2026-04-24.md, shipped in
ai-frontend PR #191 / v0.16.79) graded the whole fleet on four
higher-order axes — navigation shell, card grammar, empty-state
prose, and mode-tinting coverage. The findings that force this ADR:
- 38 cards across 10 shape-clusters. Variance is not inherently
harmful — cluster-A ("H2 plain +
<p class="empty">") already holds 13 of the 38 cards and is the de-facto canonical shape. But cluster-A is emergent, not declared; a new card has no reference to hit. - 33 unique empty-state strings in 8 tone patterns. Ranges from
terse one-liners ("No playbook runs yet.") to multi-paragraph
explainers ("No pushes in the last snapshot. When you push to a
fleet repo, it appears here."). Within-file inconsistency exists
(e.g.
infra/hetznerships both "None." and "No block volumes attached."). Six distinct "Loading…" strings. - Zero mode-tint palette mismatches. The
mode-tokens.css<html data-mode>mechanism is the one part of the shell that works cleanly across every route group — including fleet and project routes that correctly inherit Home's calm slate per ADR-059 §9. Don't touch it.
- Two structural drifts. (1)
modeFromPathOrNullhas no fallback for fleet-scope, so the mode-pill underlines Home on 14 non-Home fleet routes (/pipelines,/playbooks,/docs,/projects,/assets,/build,/patches, and their sub-routes). (2)/experiment(mode home) and/experiments(fleet catalogue) collide — same name, one letter apart, different scopes, different palettes.
The proximate cause of 3-of-4 structural problems is the same
absence: fleet-scope is implicit but not ADR'd. ADR-054 named
modes. ADR-059 §9 named project scope. Fleet scope — the
organisation-level routes that are neither modes nor projects —
accumulated as a fallback through the FLEET_TOP_SEGMENTS
allowlist in mode-store.svelte.ts:121, which exists to exclude
fleet paths from project-path matching, not to document what fleet
scope is. Every fleet-route inconsistency (mode-pill underline,
sidebar content, card layout) traces back to this unnamed scope.
This ADR names the four scopes, declares a canonical card shape, a canonical empty-state grammar, and a canonical loading string. It does not specify Svelte components (that is ADR-062's and gyrum-ui's job) and it does not prescribe a big-bang rewrite (see §7).
Decision
Adopt a four-scope taxonomy for the Platform UI shell, declare a canonical card grammar and empty-state tone, and execute the consolidation as a trickle — one small PR per session against the audit's Top-10 — so parallel product work continues uninterrupted.
§1 — Scope taxonomy (first-class decision)
Four scopes. Each route in the Platform UI belongs to exactly one. Shell behaviour (mode-pill, left rail, palette) is a pure function of the scope.
| Scope | URL shape | Mode pill | Left rail | Palette |
|---|---|---|---|---|
| Mode | /, /ship*, /operate*, /infra*, /experiment (singular) |
Active mode underlined | Mode-specific sections | Mode palette |
| Fleet | /projects, /playbooks, /docs, /pipelines, /operators, /assets, /build, /patches, and their index-style detail pages that aren't inside a project |
Dim (⎯ marker, no underline) |
Fleet sections | Home calm slate (inherited) |
| Project | /[project], /platform/[project], /restricted/[project] and their tabs (Overview / Build / Run / Docs / Todos) |
Dim (⎯ marker, no underline) |
Project tabs with Fleet link below (ADR-059 §9) | Home calm slate (inherited) |
| Build (optional, TBD) | /[buildID], /retro/[buildID], /compare/[idA]/[idB] — detail/report viewers |
Dim | Either Fleet rail or no rail (call-out below) | Home calm slate (inherited) |
Build scope is a deliberate placeholder. The /[buildID] viewer
ships a bespoke StickyHeader with its own status glyphs and a
self-contained breadcrumb — a mini-app nested under the root shell
rather than a page in it. This is ADR-053-era drift and was called
out in the tokens audit before this one. ADR-081 does not force a
decision — either accept Build as a fourth peer scope with
explicit chrome-override rules, or refactor the viewer to fit Fleet
scope with its existing rail. The choice is deferred; §10 records
it as a follow-up open question. Until then, Build routes are
documented-as-drift but not prescribed-as-fixed.
The mode-pill treatment for Fleet and Project is identical —
both are dim, both show the ⎯ marker, neither underlines a mode
tab. A user on /pipelines and a user on /focus both see the
same "no active mode" state; the sidebar content (fleet sections
vs project tabs) tells them which scope they are in. This preserves
the ADR-054/058 five-mode contract for /ship, /operate,
/infra, /experiment and / while giving Fleet + Project a
clean "not-a-mode" visual.
Palette decision. Fleet inherits Home's calm slate rather than
taking its own colour. The audit recorded zero palette mismatches
today because the code already does this via the modeFromPath
fallback, and the operator's experience has been consistent. A
dedicated fleet palette (e.g. a warmer neutral) would close a
symmetry gap but would require new tokens, new contrast testing,
and new cross-surface propagation into gyrum-ui when it lands — for
no present user-facing benefit. Stay with Home-inherited slate.
If a future need emerges (e.g. "fleet routes feel too similar to
project routes"), a sibling ADR revisits the call.
§2 — Card grammar
The canonical card shape, derived from cluster-A in the audit (13 of the 38 shipped cards):
┌─ Title ⦿count ──────────┐
│ ⦿ dot · content · action │ ← 0..n rows
│ ⦿ dot · content · action │
│ [empty state if 0 rows] │
│ [error state with retry] │
└─────────────────────────────┘
Parts:
- Header —
H2title, optional count badge, optional "See all →" link at the right. Leading icon only when it is a semantic-supporting label per ADR-062's visual-language guide (§1.5) — "encouraged but not required". The HomePage Fleet-health and Recent-deploys cards are the only two that carry H2-with-icon today; either promote icons to every card header (Top-10 item #9) or drop the two — don't leave as an outlier. - Row — optional status dot (ADR-062's
<gy-status-pill>token vocabulary) + content (truncates with ellipsis) + optional right-aligned action. Rows are 0..n; a list of zero rows falls through to the empty state. - Empty state — one-line muted text; optional illustration slot; optional CTA. Prose grammar in §3.
- Error state — inline
role="alert"region, one-line message, Retry button. Rendered in place of rows, not overlaying them.
ADR-081 specifies the shape. It does not specify the component.
Component extraction is ADR-062's remit; gyrum-ui lands a
<gy-card-section> (or equivalent) when the trickle PRs have
converged enough card bodies to make the API stable. The audit
already names <SectionHeader> (H2 + count-chip + "See all") as
the first extraction candidate — that work happens in gyrum-ui, not
ADR-081.
Cards that are legitimate outliers (cluster-E link-card grids on
/projects, cluster-I takeover on ProjectRun's "No runtime"
banner, cluster-H chart placeholders on ExperimentHome) do not
need to retrofit to cluster-A. The grammar applies to cards that
render data rows; it does not apply to cards whose job is
navigation or chart canvas. A trickle PR that touches an outlier
card must either adopt the grammar or record the deliberate
deviation in a short comment.
§3 — Empty-state prose grammar
One canonical tone: calm, one sentence, action-oriented when applicable. Three templates cover the full population:
Template A — first-use empty
"No X yet."
Template B — event-driven empty
"No X yet. When Y happens, they'll appear here."
Template C — permanent empty
"No X. This Z only does W."
Examples (every one drawn from the audit):
- Template A: "No playbook runs yet." (
RecentRunsCard) — cluster-A canonical. - Template B: "No pushes in the last snapshot. When you push to a
fleet repo, it appears here." (
/me) —hint-slot form, same grammar. - Template C: "No triggers. This playbook only runs on manual
invocation." (
TriggersList) — asserts state, explains why.
Kill these patterns (the audit's §3.9 worst mismatches):
- Multi-paragraph explainers inside a card empty state. If the region needs three sentences, the page needs a help panel. Move the explanation there.
- Different verbs for the same concept. The audit flagged "reporting" vs "configured" vs "registered" across near-identical card populations. Pick one verb per concept and hold it.
- Placeholder text like "After X tranche" — this was already
killed from
/operatebefore the audit. Don't reintroduce. - Within-file inconsistency. "None." vs "No block volumes
attached." on the same
infra/hetznerpage is the worst shape of drift — both sit next to each other in the same rendered DOM. The three-word phrase with a full stop wins neither on information nor on rhythm. Always prefer the one-sentence form.
Tone tolerance. /me, /ops, and /projects surfaces are
new-user onboarding pages; their multi-sentence "how this populates"
empties are genuinely helpful and are codified as Template B via
the hint slot on the existing <EmptyState> component. Power-user
surfaces (OperateHome, infra detail pages) stay on Template A —
terse, no hint. The split is intentional; the grammar is shared.
§4 — Loading-state grammar
One phrasing: "Loading…" (with the … ellipsis character,
not three periods). Optional noun only when the load is
unambiguous and the noun adds information: "Loading alerts…",
"Loading TODO.md…". Inside a card with an H2, the noun is
redundant with the heading — use the bare "Loading…" form.
Don't invent new verbs ("Fetching…", "Retrieving…", "Pulling…"). Don't mix ellipsis styles. The audit found six distinct "Loading" strings; one is sufficient.
§5 — Mode-pill rule
The mode-pill's active state is a pure function of the scope:
modeFromPathOrNull(pathname) →
'home' | 'ship' | 'operate' | 'infra' | 'experiment' |
'fleet' | 'project' | null
Mapping (all path matches are prefix-matches unless noted):
/→'home'/ship*→'ship'/operate*→'operate'/infra*→'infra'/experiment(exact) →'experiment'/[project-slug],/platform/*,/restricted/*→'project'/projects,/playbooks,/docs,/pipelines,/operators,/assets,/build,/patches→'fleet'/experiments(plural) →'fleet'(see §10 open question)- Everything else →
null
The UI treats 'project' and 'fleet' identically: mode-pill
renders in its dim state with a ⎯ marker in place of any
underline. No highlight. The five underline slots are reserved for
the five modes; they are not overloaded to mean "fleet" or
"project".
Why the function returns 'fleet' rather than null for fleet
routes. Returning null would work for the dim-pill case, but
callers that want to distinguish fleet from "unknown" (sidebar,
analytics, keyboard shortcut enablement) need a named value. The
function returns the scope; the pill's render logic collapses
'fleet' | 'project' into "dim" at the view layer.
This closes Top-10 item #1 (mode-pill underlines Home on 14 fleet
routes) with a bounded two-line change to modeFromPathOrNull.
§6 — Relationship to existing ADRs
ADR-081 is a pure extension — it does not contradict any accepted ADR. It names shapes that were previously emergent and codifies the majority-winning pattern from the shipped surface.
- ADR-054 (three mode-home routes). Unchanged. ADR-081 does not touch the mode-home route contract; it only adds peer scopes (Fleet, Project) that were already implicit.
- ADR-058 (five-mode platform UI). Unchanged. The five-mode pill, palette table, and Cmd+1..5 shortcuts are preserved verbatim. ADR-081 adds the dim state; it does not change any mode's active state.
- ADR-059 (project-first IA). Unchanged. ADR-081 cites §9 as the palette precedent (projects inherit Home's calm slate) and extends the same inheritance to Fleet. ADR-059's project-tab shape (Overview/Build/Run/Docs/Todos) is preserved.
- ADR-062 (design system + component strategy). Unchanged.
ADR-081 references the token vocabulary but does not specify
component APIs. gyrum-ui's
<gy-card-section>,<gy-empty-state>,<gy-error-state>, and<gy-mode-switcher>all consume ADR-081's grammar without ADR-081 prescribing their signatures. - ADR-065 (persona-aware IA). Unchanged.
/me,/ops,/teamare fleet-scope by URL (they are not modes and not projects) and correctly use the Template-B empty-state form as new-user onboarding surfaces. The persona-home exception is named in ADR-065, not redefined here. - ADR-066 (microvisualisations). Unchanged. ADR-081's card grammar leaves the row and header shapes open to embed sparklines and timeline strips exactly as ADR-066 specifies.
- ADR-070 (playbook UI contract). Unchanged except where
ADR-070's empty-state prescriptions for the
<PlaybookRunner>report conflict with §3. In that case ADR-081's three templates win. Audit-verified: ADR-070's shipped empties onRunsList,RunEventsTimeline, andTriggersListalready match Template A or Template C — nothing to change.
§7 — Rollout: trickle, not sweep
Per operator direction on 2026-04-24: "a slow trickle of improvements". One consolidation PR per session after ADR-081 lands. Priority order from the audit's Top-10. Each PR brings one route or card-cluster to the grammar. No "UI redesign week".
Proposed cadence:
- 1-2 trickle PRs per session.
- ~10 small PRs across 10 sessions.
- Each PR touches one route file or one component. Under 200 lines changed. Normal 3-persona review gate.
- Parallel product work continues uninterrupted. Trickle PRs are never a blocker for feature work.
Trickle priority ladder (first eight from the audit's Top-10; items #9 and #10 are opt-in):
- Fix
modeFromPathOrNullto return'fleet'for fleet-scope segments — mode-pill dim state rendering (§5). [Top-10 #1] - Populate
OPERATE_NAVandEXPERIMENT_NAVinSideRailmirroringINFRA_NAV/SHIP_NAV. [Top-10 #2] - Resolve the Platform-project Run tab 404 — either hide the Run link for Platform class or ship a disabled-tab treatment. [Top-10 #3]
- Migrate
infra/repos/[...full_name]H2 treatment to cluster-A shape. [Top-10 #4] - Canonicalise
/me+/opson the shared<EmptyState>component withhintslot, retiring the bespoke.empty-box/.error-boxpair. [Top-10 #5] - Sweep loading strings to
"Loading…"everywhere a noun is redundant with the containing H2. [Top-10 #6] - Collapse
ShipHomeexperiments-table duplication into the/experimentsfleet primitive. [Top-10 #7] - Resolve
/experimentvs/experimentsURL collision — see §10 open question #2.
Items #9 (HomePage H2-with-icon) and #10 (/[buildID] mini-app
chrome) are tracked but not scheduled. Item #9 is a deliberate
tie-break (either direction resolves the outlier); item #10 is the
deferred Build-scope question from §1.
§8 — Enforcement (advisory, non-blocking)
Dana (the docs-advisory persona) gains a lint pass for touched UI files. For every Svelte file in a PR, Dana asserts:
- Card shape compliance. If the file defines a
<section>or<article>with thecardclass and renders data rows, the shape must include a header, rows-or-empty, and rows-or-error — or a justified one-line comment documenting the deviation. - Empty-state prose check. A basic grep over
.emptyandEmptyStateprops checks that text matches one of the three §3 templates by regex (/^No .+ yet\.$/,/^No .+ yet\. When .+\.$/,/^No .+\. This .+\.$/) or the existinghintslot carries the explanation. - Loading string check. All
Loadingstrings match"Loading…"or"Loading <noun>…"with the ellipsis character.
Dana posts findings as PR comments. They do not block merge — the gate is advisory, per the existing review contract (Dana is the fourth reviewer, advisory; Priya/Marcus/Lin carry the 2-of-3 formal gate). Goal: every new UI PR self-audits against the grammar, drift is visible at review time, and the trickle PRs get measurable.
Consequences
Easier.
- New routes have a reference: scope classification is a three-line decision, card shape is a copy-paste, empty-state prose is a template fill-in.
- Fleet-scope is no longer implicit. A new fleet route (e.g.
/operators,/patches) knows its rail source, its mode-pill state, and its palette before it's built. - Mode-pill no longer lies about Home on 14 routes — the fix is bounded and testable.
- Component extraction to gyrum-ui has a grammar to encode. The shape precedes the API rather than emerging from three conflicting call sites.
- The trickle cadence means consolidation is visible progress every session rather than a large dedicated project.
Harder.
- Two structural fixes (mode-pill
modeFromPathOrNullupdate, Dana lint) must land before the trickle can measure itself. Dana's lint rule has to be written in the gyrum-review-pr infrastructure. - The
⎯marker visual is a new design affordance — first trickle PR touching the ModeSwitcher has to make the aesthetic call (em-dash,⦿, plain dim text). Minor; flagged as §10 open question #3. - Legitimate outlier cards (cluster-E link-grids, cluster-I takeovers, cluster-H chart placeholders) now require a one-line justification comment. Small maintenance tax.
- The four-scope taxonomy is a model the team has to carry. A future fifth scope (notifications panel? admin console?) must justify itself against the four or it silently drifts.
What we sign up to maintain.
- The four-scope taxonomy as the shell-contract source of truth.
modeFromPathOrNullis the code-level enforcement point; new top-level path segments extend the fleet list explicitly or they fall intonull. - The three empty-state templates. New prose must land in Template A, B, or C — or justify its deviation. Dana's lint catches drift at review time.
- The "Loading…" phrasing. New loading strings adopt it or fall through to Dana's comment.
- The trickle cadence. If the consolidation pace stalls (no trickle PRs for 3 consecutive sessions), revisit — either the grammar is wrong, or the priority order is wrong, or the effort per PR is under-estimated. Either failure mode is actionable.
Alternatives considered
- Big-bang UI redesign week. Rejected. Would halt product work for days, concentrate risk into one merge, and front-load decisions that the audit did not validate (e.g. component API choices). The trickle approach lets each PR validate a small slice and lets product work continue.
- Give Fleet its own palette (warmer neutral). Rejected for v1. The audit found zero mode-tint mismatches — the current Home-inherited treatment works. Introducing a fleet palette adds tokens, contrast testing, and cross-surface propagation for no present user-facing benefit. Leave as §10 open question if a future need surfaces.
- Use "Alpha" or "catalogue" naming to resolve
/experimentvs/experiments. Captured as §10 open question #2 and task #227 — not resolved in this ADR. Prerequisite bug fix before the trickle begins. - Make Dana's lint a hard gate. Rejected. The grammar is new and the lint rules are heuristic (regex over prose is inherently fuzzy). Hard-gating would cause review friction before the trickle has validated the rules; advisory lets the corpus grow and the rules tighten organically.
- Specify Svelte components directly in ADR-081. Rejected.
ADR-062 owns component specification and gyrum-ui ships the
code. ADR-081 specifies the shape; the component library catches
up. Same separation as ADR-066 (microvisualisations spec) vs
gyrum-ui (
<gy-sparkline>implementation). - Make Build-scope a fourth peer in this ADR. Rejected. The
/[buildID]viewer is ADR-053-era drift with its own bespoke chrome; committing it as a peer scope locks in the drift, and refactoring it to fit Fleet is a larger decision than the trickle can absorb. Defer.
Open questions
The ADR deliberately defers four decisions. Each has enough information to act on but not enough to commit now.
- Build-scope as fourth peer vs refactor to Fleet. §1 and §10
call out
/[buildID],/retro/[buildID],/compare/*. Not blocking trickle PRs — they remain as-is until either a dedicated rewrite lands (tokens-audit punch list) or a later ADR commits the peer-scope interpretation. /experimentvs/experimentsURL collision. Different scopes, different palettes, one letter apart. Captured as task #227 and must resolve before the Top-10 trickle begins — a trickle PR that touches the mode-pill will hit this collision immediately. Candidates: rename/experimentsto/experiments/catalogue, or accept the split and document it. Escalated to the first trickle PR's scope.- Mode-pill dim marker character. §1 and §5 say "
⎯marker" but the character choice (em-dash⎯,⦿, plain dim text, or a purely visual faded treatment) is aesthetic. Defer to the first trickle PR that touchesModeSwitcher.svelte. - Fleet palette. Default is Home-inherited slate (current behaviour, zero mismatches). A warmer neutral is revisitable if fleet/project visual-distinction becomes a user request.
No questions escalated beyond these four.
Amendment 2026-04-28: warp gy-* as canonical fleet UI source
Why now
On 2026-04-28 (~10:50 UTC), while operating /platform/warp during
the deploy-flow swarm, the operator's verbatim reaction was
"fontsizes, everything is wrong" — surfaced against the ai-frontend
shell, not warp's. Warp's UI at warp.gyrum.ai had remained the
operator's most-trusted surface across the same period. The pattern
was already memorialised in operator memory
(feedback_warp_is_design_reference): warp's gy-* component
library is the de-facto fleet design canon, and the design-system
audit direction set by warp #287 ("apply gyrum-ui to warp") had
been pointing the wrong way — gyrum-ui was being treated as
upstream of warp when in reality warp was the more consistent and
operator-trusted source.
ADR-081 §2 and §6 already deferred component-implementation choice to ADR-062 + gyrum-ui, but did not name a concrete component source. Without that naming, trickle PRs and the warp epic #481 children (r1..r4) had no ADR-grounded reason to prefer warp's tokens over factory's accumulated CSS. This amendment closes that gap.
Decision
Warp's gy-* component library at
gyrum-labs/warp/web/src/lib/components/ is the canonical
implementation source for fleet UI. gyrum-ui's contract is to
mirror warp's tokens — not the other way around. New tokens
originate in warp; gyrum-ui re-exports them; ai-frontend (and any
other fleet consumer) pulls them via gyrum-ui.
This amendment is purely additive over the original ADR. It does not change:
- the four-scope taxonomy (Mode / Fleet / Project / Build) from §1;
- the canonical card grammar shape from §2;
- the three empty-state templates (A / B / C) from §3;
- the
"Loading…"rule from §4; - the mode-pill rule and
modeFromPathOrNullshape from §5; - the trickle cadence and Top-10 priority ladder from §7.
The amendment names the component source for the shape that ADR-081 already declared — it does not redraw the shape.
Implementation pointers
The canonical components in warp/web/src/lib/components/ that map
to ADR-081's grammar:
<gy-card>— the card shell that implements §2's overall shape (header + rows + empty/error fall-through).<gy-section-header>— the §2 header with H2 title, optional count badge, and optional "See all →" right-link. This is the audit's first-named extraction candidate.<gy-empty-state>— Templates A / B / C from §3, with ahintslot for Template B's "When Y happens, they'll appear here." form per ADR-081 §3 and ADR-065 persona-home tone tolerance.<gy-error-state>— the inlinerole="alert"region from §2 with the Retry button.<gy-status-pill>— the row status-dot vocabulary referenced in §2 ("ADR-062's<gy-status-pill>token vocabulary").<gy-button>,<gy-badge>— the supporting primitives the row action and count badge depend on.
ADR-081 §2 and §3 are the shape contract; the components above are their implementation source.
Reverse-direction note
Warp #287 ("apply gyrum-ui design system to warp kanban") moved
tokens from gyrum-ui into warp. In the period since, warp's gy-*
library has matured into the operator's most-trusted surface while
gyrum-ui has lagged. This amendment names the dominant flow as the
opposite — warp is upstream; gyrum-ui mirrors. New tokens originate
in warp's component library; gyrum-ui re-exports them; ai-frontend
consumes via gyrum-ui. Warp #287's earlier direction is not
rejected — its tokens-into-warp work seeded the canon — but the
ongoing flow is reversed.
What this unlocks
This amendment is the r0 prerequisite for warp epic #481. The
children r1..r4 — warp #482, #483, #484, #485 — migrate the four
operator-frequent ai-frontend pages onto the warp gy-* canon.
Each child PR cites this amendment version in its PR body so the
principle-aware reviewers can verify against the rule the author
read.
Guideline citation format
PRs migrating to the canon cite Guideline: warp-gy-canon@2026-04-28
in their PR body, per the knowledge-layer convention (CLAUDE.md
"Knowledge & context" section, ADR-117). The citation lets the
persona reviewers confirm the implementation against the exact
amendment version the author worked from.
What this does NOT do
- It is not a big-bang migration. The trickle cadence from §7 still holds — one route or card-cluster per PR, under 200 lines changed, parallel product work uninterrupted.
- It does not refactor warp
gy-*itself. The library is the source; this amendment names it, it does not edit it. - It does not change the trickle cadence for routes outside the four operator-frequent ones identified in warp #481. Other routes follow the §7 ladder unchanged.
- It does not specify component APIs. Component signatures live with warp's library and gyrum-ui's mirror, per the original ADR-081 §6 separation.
Supersedes: none. Superseded by: leave blank until a later ADR reverses this one.
Reference audit: ai-frontend/docs/ui-consolidation-audit-2026-04-24.md
(PR #191 / v0.16.79) — 71 routes, 38 cards, 33 empty-state strings,
2 structural drifts, 0 palette mismatches. This ADR is the
decision layer; the audit is the evidence layer.
Component canon: gyrum-labs/warp/web/src/lib/components/
(the gy-* library; mirrored by gyrum-ui). See
warp.gyrum.ai/CLAUDE.md for the agent-bootstrap doc.