Files
ratatoskr/docs/design/ratatoskr-web-design-brief.md
T
vh 1fcb17730e feat(web): Claude Design console — 3-column wire monitor (v0.20.0)
Adapt the Claude Design "Ratatoskr Console" prototype into the web SPA:
translate out of the .dc.html dialect (x-dc / sc-if / sc-for / {{}} /
DCLogic / external _ds CSS) into single-file / no-CDN / vanilla, and wire
all real /api/* fetch + SSE into its DOM. New 3-column command-console
replaces the tabbed telemetry layout; endpoint set + SSE vocab unchanged.

- left engine-ticker rail: DEBUG + ADMIN + tool/turn-lifecycle merged into
  one timeline (tickerAdd); tools-armed chips; full-detail Bifrost rail pane
  (endpoint / connected / consumer / caps / tools)
- center conversation: per-turn INLINE chain-of-thought
- right resizable affect console: dominant / canonical-mood centerpiece;
  bipolar PAD faders EACH with a turn-to-turn delta + sparkline; P×A mood
  orbit; relations metric rows; canonical directive
- light / dark theme toggle (dark default; full token override —
  surfaces + fg + borders + accent-as-text)
- inlined data-URI favicon (downscaled 1024->64px), kills /favicon.ico 404
- ticker spine re-anchored to a content-height wrapper (was scrolling out of
  view on auto-scroll)
- honest-shape (INV-001): dominant-emotion shows a real OCC emotion (Tier-1)
  or the canonical mood word (Tier-3), never a fabricated one; affect-derived
  grid drops non-emitted metrics (intensity / decay-tau)

All server routes unchanged. web_debug_surface.contract.md amended for the
presenter renames (renderBifrostState->renderBifrost, renderAffectPane->
renderConsole, setPersonaStrip removed).

Verified: pytest tests/test_web_* (84 passed) + node Playwright end-to-end
against personal :8081 (session open, Sindra seeded greeting, live turn SSE,
affect console + relations + bifrost detail, theme toggle, PAD deltas,
no favicon 404).
2026-07-06 21:50:22 -07:00

15 KiB
Raw Blame History

Design brief — ratatoskr-web (Worldtree wire monitor)

For: a visual design pass (Claude Design). Deliverable: a single self-contained HTML prototype, fully populated with representative static data, that an engineer will wire live data into. Do not build a data layer — build the shell and every state, beautifully, with placeholder content in every slot.


1. What you're designing

ratatoskr-web is a developer-grade debug/observability console for a conversational-AI engine (Worldtree). Its tagline is "wire monitor": you open a session with an AI agent, send it turns, and watch that turn flow through every layer of the system at once — the streaming response, the model's chain-of-thought, the tools it can call, the agent's live emotional/persona state, the provider handshake, and the engine's admin lifecycle events — all side-by-side on one screen.

The product IS the observability surface. Chat is just the input. This is not a chat app, not a marketing page, not an end-user product. The user is one developer (occasionally a few LAN peers) staring at a dense instrument to debug what the engine is doing. Think oscilloscope / flight-data console / a well-lit htop, not a messaging UI.

Design values, in priority order:

  1. Information density earns the screen. Every region shows live, changing data. Nothing is decorative filler. A quiet, legible, glanceable density is the whole point — the user reads six data streams at a glance.
  2. Calm under motion. Multiple regions update in real time (token streams, live metrics, event logs). The design must stay readable while things move — no jitter, no attention-grabbing per-item animation. Motion is for state change, used sparingly.
  3. Legibility first. Monospace, high contrast where it counts, generous but not wasteful spacing. This runs for hours; it must not tire the eye.

2. Aesthetic direction — Australis

Use the Australis design system (a cool-toned, terminal-first dark theme — the australis-design skill has the canonical tokens: colors, spacing, radii, shadows, motion). Import/inline colors_and_type.css; don't reinvent tokens.

Non-negotiables from the brand:

  • Dark only. Base is a cool near-black #222531 — never pure #000. The eye rests in low-contrast cool grey; emphasis comes from brightness, not saturation. Layer surfaces up the Sea neutral ramp (#222531 → #373b46#414751`).
  • Palette families: Ice (surface neutrals), Aurora (blue → cyan → green, the primary accents — used generously in that preference order), Dawn (red/yellow/magenta — sparingly, for status only). Semantic: info=blue, success=green, warning=yellow, danger=red.
  • The signature motif is the aurora glow — a low-opacity cyan→blue→green light coming through the top of the screen, plus a 3px aurora focus ring on interactive controls. Lean into this as the one memorable thing.
  • No noise, no textures, no patterns. "The screen is the polar sky — empty, with light coming through it." The one sanctioned gradient is the aurora glow.
  • Never a colored left-border on cards (the LLM-slop trope). Featured cards accent the top edge instead.
  • Type: this instrument is mono-first — that IS on-brand for Australis ("terminal-first"). Use a monospace stack (JetBrains Mono / system mono; see §9 — no web-font CDN allowed). Eyebrows/labels are mono, UPPERCASE, ~11px, wide-tracked (0.080.16em) — use them liberally; they're a system signature.
  • Motion: calm, never bouncy. ~120ms hover, ~200ms state, ~320ms panels. Focus = aurora glow ring. Hover = one step brighter (not lower opacity). A slow (814s) aurora drift on a hairline top band is welcome; nothing else should loop.

The current UI already borrows this palette — you're not inheriting it, you're redesigning the layout and craft from scratch with the brand as the guide. Feel free to rethink the spatial composition entirely (see §10).


3. The two screens

Screen A — Session setup (entry)

A single centered card on the aurora canvas. Fields:

  • Agent — a <select> (populated live; show 34 sample options incl. ratatoskr:sindra, forseti, mimir).
  • Bifrost binding (Tier-3 provider) — a <select>: combined (:8392) / none — observe only / memory (:8391) / affect (:8390).
  • Open session — primary button.
  • An error line (design the error state too — e.g. "agent not available").

Screen B — Live workspace (the main event — 95% of the design effort)

Persistent top bar + status line spanning full width; between them a two-region body: a conversation column (left, dominant) and a telemetry column (right, tabbed). Current split is ~1.85 : 1 — you may re-proportion. The information inventory below is exhaustive; every item needs a home.


4. THE COMPLETE INFORMATION INVENTORY

This is the core of the brief. Design a slot for every item, in a sensible state. Data shapes are given so your placeholders read true.

4.1 Top bar (persistent)

Item Shape / example Notes
Brand ᛯ ratatoskr + eyebrow WIRE MONITOR the mark is a rune glyph; small
Connection status one of: offline, connected (idle), streaming, error dot + label; streaming pulses; color-coded (grey/green/cyan/red)
Persona strip (appears after a session hydrates) dominant-emotion word (love) + PAD bars: P, A, D each PAD bar is bipolar — centered on 0, fills left (negative) or right (positive), value ∈ [1, 1]; live-updates every turn
Session identity ratatoskr:sindra · …381b99f4 agent id + last-8 of session id
Bound-plane badge (when bound) ⇄ combined http://10.100.10.50:8392 plane + endpoint; only when a Bifrost binding is active

4.2 Conversation column (the transcript + composer)

The transcript is a scrollable stream of turns. Design each element:

Element Example content Notes
Turn divider TURN 3 between hairlines uppercase eyebrow, rule lines each side
User prompt echo what's your intensity setting? the user's message, accent-marked
Assistant response streaming Markdown (headings, bold, italic, code, lists, quotes, links) accumulates token-by-token while live; distinct "live" treatment vs settled
Seeded first-message a full assistant turn present before the user speaks (an authored greeting) renders identical to a lived assistant turn — the session can OPEN already showing the agent's opener
Reasoning / "thinking" note ✦ sindra is reasoning··· (italic) ephemeral app affordance — appears while the model reasons, vanishes the instant real text begins; visually distinct from the response so it never reads as engine output
Awaiting-first-token ··· animated heartbeat before the first token
End-of-turn status chips ✓ DONE 1.84s · ✗ ERROR agent_not_available · ⚠ CANCELLED · ✗ WIRE lost small bordered chips; color per state
Composer (pinned bottom) [ message input ] [SEND] Enter=send, Shift+Enter=newline; during a turn the Send button becomes CANCEL (amber)

4.3 Telemetry column (six tabbed panes)

A tab bar + a pane header (with a Copy button) + the active pane body.

Tabs (each: name · keybinding hint · a count badge that flashes on new data): TOOLS ^1 · DEBUG ^2 · THINK ^3 · PERSONA ^4 · BIFROST ^5 · ADMIN ^6. Active tab is accent-marked.

Pane contents — design each, populated:

  1. Tools — the tool inventory the model saw at turn-fire: agent_id, builtin_tools[] (names), bifrost_tools[] (name + description + parameters). Below it, live tool-call events stream in (tool_starttool_result) as the turn runs. Empty state: — live tool events —.
  2. Debug — a raw structured op/lifecycle log (mono lines; new lines flash once). Think tail -f.
  3. Think — the model's full chain-of-thought, per-turn dividers, live-Markdown. Longer prose than the response.
  4. Persona / affectthe richest pane. Contains:
    • The canonical NL directive the engine injects into the agent's context — the literal text: a mood descriptor ("neutral", "faintly excited", ±0.3 bands) + a relationship directive. Show this verbatim, quoted.
    • PAD mood point — pleasure / arousal / dominance current values.
    • relations[] — for each related entity (e.g. the user): trust (ability / benevolence / integrity), warmth, agency, relation_context (a tie-type word like "stranger" / "expressive"), each as a metric row: label · value · Δ-since-last (▲/▼) · unicode sparkline · n (evidence count) · descriptor. Values are 01 with 23 decimals.
    • dominant_emotion (an OCC type: joy/anger/fear/…) + emotions_active[].
    • Design the metric row as a reusable component — it's the densest, most-repeated element in the whole UI. Tabular-aligned numbers, a tiny inline sparkline, a subtle up/down Δ.
  5. Bifrost — the live provider binding (admin-gated): endpoint, connected (bool), capabilities_granted[], consumer_id, tools[]. Self-labeling states: not configured (no admin key) / not bound (session has no live binding) / an auth-denied state.
  6. Admin events — a live event log of the engine's lifecycle broadcast (a ~17-type vocabulary: turn.started, session.created, system.*, …), filtered to the active session. Streaming; timestamped lines.

4.4 Status line (persistent, bottom)

  • Keybinding legend: Enter send · ⇧Enter newline · ^1^6 panes · ^C cancel (rendered as little kbd chips).
  • Version: ratatoskr 0.19.9 (right-aligned).

5. States to design (show these explicitly)

Provide a mock (or a toggle) for each — these are where debug UIs live or die:

  • Setup: loading-agents · ready · create-error.
  • Connection: offline · connected/idle · streaming (pulsing) · wire-error.
  • Turn lifecycle: awaiting-first-token · reasoning (✦) · streaming response · done (+timing chip) · error · cancelled.
  • Panes: empty/placeholder · hydrated/dense · not-configured (admin key absent) · not-bound (Bifrost) · error · a badge flash on new data.
  • Persona pane specifically: a fully-populated relations block AND a cold/empty one (a fresh agent with no accumulated state).

6. Interaction & motion

  • Real-time is the defining trait. The response + thinking panes stream token-by-token; the metric rows tick; event logs append; the persona strip re-animates each turn. Design so all of this is calm — the reader's eye isn't yanked around. Reserve motion for genuine state transitions (turn-start, done, a new event) and keep it short.
  • Keyboard-first. ^1^6 switch panes; Enter/⇧Enter/^C drive the turn. Panes are also clickable. Show focus states.
  • The aurora glow is the interaction signature — focus rings, the top band, the connection pulse, the primary-button hover. Make it the thing someone remembers.
  • Copy-to-clipboard on each pane header (with a copied-confirm state).

7. Layout — you have latitude

The current layout is a fixed two-column split. You may rethink it — as long as every §4 item has a legible home and the density stays high. Directions worth exploring (pick one, commit):

  • A command-console feel: a slim persistent left rail of "instruments," a dominant conversation center, a right telemetry stack.
  • A grid of live tiles (the metrics/panes as a dashboard) with the conversation as the anchor column.
  • The classic monitor split, but with far better hierarchy, grouping, and breathing room than today.

Desktop-first; design at 14401512px wide. Graceful down to ~1100px is a plus (this runs on a dev laptop). No mobile.


8. Deliverable — what to hand back

A single self-contained index.html (inline <style> + <script>; see §9 constraints) that:

  1. Renders Screen A and Screen B (a toggle/hash is fine).
  2. Has representative static placeholder data in every §4 slot and shows the key §5 states (either multiple mocks or lightweight JS toggles). I want to see the design fully populated and dense, not empty scaffolding.
  3. Uses clean, semantic, stable hooks — meaningful ids / classes / data-* on every dynamic slot (the transcript container, each pane body, the PAD bars, a metric-row template, the connection dot, the tab badges, etc.). This is how I wire real data in — treat the DOM structure as an API.
  4. Imports/inlines the Australis tokens; no invented palette.

I will then swap your placeholder content for live fetch() + EventSource calls against the real endpoints (§9). The cleaner and more component-shaped your DOM, the faster and safer that wiring is. A short note listing your mount points / how you'd expect data injected is very welcome.


9. Hard technical constraints (these make it wire-able)

  • Single file. No build step. No CDN. No external network at runtime. This ships to an internal LAN and must work offline. That means: no Google Fonts / no web-font CDN (use a system monospace stack), no CDN JS/CSS libraries, everything inline. (Icons: use unicode glyphs ➜ ✓ ✗ ! ● ✦ or hand-inlined SVG — Australis uses Lucide-style 1.75-stroke line icons; inline them.)
  • Vanilla HTML/CSS/JS. No framework (the production app is framework-free vanilla JS). React/Vue prototypes can't be wired in.
  • All dynamic text is escaped on the real side (untrusted upstream content); assistant/reasoning bodies go through a safe-Markdown renderer (escape-first, whitelist subset). Don't design anything that depends on raw HTML injection.
  • The real data contracts (so your structure maps to the wire — you don't implement these, just leave homes for their outputs):
    • GET /api/agents → agent list (for the setup picker).
    • POST /api/sessions {agent_id, bifrost_plane?}{session_id, agent_id, bifrost?}.
    • GET /api/sessions/{id}/messages{items:[{seq, role, content}], …} (the transcript on open, incl. the seeded first-message).
    • POST /api/turns/{id} {content}{turn_id}, then GET /api/turns/{id}/stream (SSE) — event vocab: text, thinking, tool_start, tool_result, done, error, awaiting_llm_first_token, terminal events. POST /api/turns/{id}/cancel.
    • GET /api/sessions/{id}/tools → tool inventory. GET /api/sessions/{id}/bifrost → binding state.
    • GET /api/affect/{agent_id} / GET /api/agents/{id}/persona_state → PAD + relations + dominant_emotion (the persona pane + strip).
    • GET /api/admin/events (SSE) → the admin lifecycle log.

10. Tone check

The user is an engineer who respects the tool that respects their attention. The winning design is quietly excellent: dense but never cramped, alive but never busy, cool and legible, with the aurora as a single confident signature. Impress by making six live data streams feel calm and readable at a glance — that's the hard, valuable thing here, not decoration.