SPEC-0015: Operator Board v2 (Charm-Web)
SPEC · SPEC-0015 · Status · draft · Date · 2026-07-18 · Implements · ADR-0018 · Requires · SPEC-0003, SPEC-0007, SPEC-0012
Overview
The redesigned human surface: the charm-web design language (ADR-0018) applied across a five-view information architecture — board · todos · endpoints · personas · friends — with a three-lane live patch-panel board, full-page wizards, a global keyboard map, and day/night themes. Supersedes SPEC-0013 (Operator Board). The durable architecture of SPEC-0012 (embedded templates, HTMX + SSE, CSRF, error standards, no-JS fallbacks) is inherited, not restated.
Amended 2026-09-21, with the shared-receiver removal. The sixth view, Providers, was removed with the provider registry (SPEC-0017, retired).
Requirements
Requirement: Design Token System
The UI SHALL be styled exclusively through a charm-web token layer replacing the Operator/brass
tokens: a day theme (lavender-paper, deepened accents) and a night theme (blue-black, neon), both
complete, selected via prefers-color-scheme with an <html data-theme> override. Trust-mode and
todo-state colors SHALL exist as named tokens in both themes, and token text/background pairs SHALL
pass WCAG AA, enforced by porting the existing contrast test to the new palette.
Scenario: Contrast gate
- WHEN the token stylesheet changes
- THEN the automated contrast test fails the build if any declared token pair drops below AA in either theme
Requirement: Typography And Vendored Fonts
The UI SHALL use JetBrains Mono (body/UI/code) and Space Mono (display), vendored as woff2 under
static/fonts/ and embedded — no font CDN. The Zilla Slab and IBM Plex families SHALL be removed.
The font-coverage test SHALL retarget to the new files.
Scenario: Offline serve
- WHEN the binary runs with no outbound network
- THEN all UI text renders in the vendored faces with zero external requests
Requirement: Application Shell And Navigation
The shell SHALL present the top bar (wordmark, ~/operator breadcrumb, MCP-connected indicator,
"+ new" wizard launcher, theme control) and a five-view navigation: board, todos, endpoints,
personas, friends. Every view SHALL end in a key-hint footer rendering the active
keymap. The login page SHALL be restyled in the same language.
Scenario: Navigation offers every view
- WHEN an operator opens any view
- THEN navigation offers all five views, with the active view indicated
Requirement: Theme Toggle
The shell SHALL provide a visible theme control (and the t key) cycling day/night, persisted in
localStorage, applied before first paint without flashing the wrong theme, and without violating
the same-origin CSP (no inline scripts; a tiny external boot script or CSP hash).
Scenario: Returning operator
- WHEN an operator who chose night reloads on a day-preferring OS
- THEN the page paints night from the first frame
Requirement: Patch Panel Board
The board SHALL render three lanes — received, verified, patched through — as the live view of every inbound line. Lane semantics: received holds ephemeral in-flight cards (an event arriving/being verified, SSE-only, never persisted as a distinct state — rejected events leave the lane with a rejection surface, accepted ones advance); verified holds durable todos not yet claimed (trust check passed, normalized, deduped); patched through holds todos claimed by or completed under an agent. Cards SHALL carry provider glyph, title, trust chip, state chip, and age; lane headers SHALL carry live counts. Cards SHALL move between lanes over SSE without reload.
Scenario: A signed webhook crosses the board
- WHEN a signed GitHub webhook is received, verified, stored as a todo, and later claimed
- THEN its card appears in received, advances to verified on todo creation, and moves to patched through on claim — each transition pushed live
Scenario: Rejected caller
- WHEN a webhook fails signature verification
- THEN the payload is not persisted (SPEC-0001 unchanged) and the received lane surfaces the redacted rejection transiently
Requirement: Todos View And Drawer
The todos view SHALL present the durable queue as a filterable table (line, provider, trust, state, age) with the detail drawer (lifecycle timeline, idempotency key, lease, claimed-by, payload, requeue/cancel actions) restyled in the new language, preserving all current actions and SSE row updates.
Scenario: Live claim
- WHEN an agent claims a todo while the view is open
- THEN the row's state chip updates in place without reload
Requirement: Endpoints View And Vend Wizard
The endpoints view SHALL render vended endpoints as cards (principal, persona, queues, verbs, MCP
URL, credential tail with hashed/last-used, expiry countdown, rotate/revoke). Vending SHALL become
a full-page wizard: persona → queues → verbs → credential lifetime → vend, ending in the
one-time credential reveal with copyable .mcp.json. For OAuth-capable clients the reveal SHALL
offer the URL-only variant (SPEC-0016). Scope stays immutable; the wizard SHALL make re-vend the
path for scope changes (SPEC-0007 doctrine).
The endpoint's default claim lease (SPEC-0007 REQ "Endpoint Default Lease") is not scope. Quick vend
and the wizard's lifetime step SHALL take it as an optional field that states the server default and
the allowed range (blank = server default); the confirm summary and the reveal SHALL state it, and a
re-vend SHALL seed it from the source endpoint. Every card SHALL state the effective lease and whose
it is, and an active card SHALL carry a form that changes or resets it in place
(POST /endpoints/{id}/lease, session- and CSRF-guarded like its siblings). A refused value SHALL
re-render the same page with the message and the entered value, never a dead end.
Scenario: Lease edited on the card
- WHEN the operator types
1hinto an active card's lease form and saves - THEN the endpoint's default becomes 3600 seconds, the page returns to that card, and the card
states
1h (3600s)as set for this endpoint
Scenario: Lease refused on the card
- WHEN the operator saves
86401 - THEN the Endpoints view re-renders with 400, that card's form shows the refusal and
86401, and the stored default is unchanged
Scenario: Lifetime chosen at vend
- WHEN the operator selects a 7-day lifetime and completes the wizard
- THEN the endpoint records its expiry, the card shows the countdown, and the reaper enforces it (SPEC-0016 REQ "Credential Lifetime")
Requirement: Personas View And Wizard
The personas view SHALL render persona cards (initials block, name, human-authored prompt, verb subset, derived skills, vended-as usage). Creating/editing SHALL be a wizard whose final step shows a live preview of the A2A Agent Card rendered from unsaved form state, so the operator sees exactly what peers will discover before saving.
Scenario: Preview before publish
- WHEN the operator edits the verb subset in the wizard
- THEN the previewed card's advertised skills update to the derived set before anything is persisted
Requirement: Friends View And Approval Flow
The friends view SHALL separate pending · in your queue from established edges (direction, peer, queues, last-seen, revoke). The approval flow SHALL present "approving is the vend" — showing the scoped endpoint that approval mints — and on approval SHALL surface the vended result explicitly rather than only a toast. Per-direction, revocable, non-transitive semantics (SPEC-0010) are unchanged.
Scenario: Approve mints and shows the grant
- WHEN the operator approves a pending friend request
- THEN the flow completes with the vended endpoint's identity and scope visible, and the edge moves to established
Requirement: Global Keyboard Map
The UI SHALL implement a global keymap — g then a view key (go to view), / (focus filter),
enter (open selection), t (theme) — declared in one registry that both drives behavior and
renders the key-hint footer, so hints never drift from bindings. Bindings SHALL never shadow text
inputs.
Scenario: Hints match behavior
- WHEN a key binding changes in the registry
- THEN the footer hint changes with it, with no second source of truth
Requirement: Wizard Interaction Pattern
Create/vend/connect flows SHALL be full pages (not overlay modals) with server-side step state, back navigation that preserves entered values, and a no-JS fallback that completes the same flow. Destructive and irreversible steps (vend, revoke) SHALL confirm before executing.
Scenario: JavaScript disabled
- WHEN an operator completes the vend wizard with JS disabled
- THEN every step renders server-side and the flow completes identically
Requirement: Live Fragment Architecture
SSE live updates SHALL continue through the existing hub with per-view fragment template files (splitting the shared fragments file), and lane movement SHALL be expressed as typed events with out-of-band removal + insertion. The SSE hub and auth (SPEC-0012) are unchanged.
Scenario: Fragment isolation
- WHEN a todos fragment changes
- THEN no board or friends template file is touched