1fcb17730e
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).
271 lines
15 KiB
Markdown
271 lines
15 KiB
Markdown
# 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.08–0.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 (8–14s) 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 3–4 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_start` →
|
||
`tool_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 / affect** — *the 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 0–1 with 2–3 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 **1440–1512px** 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 `id`s / `class`es /
|
||
`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.
|