From 1fcb17730ec6739882528b71624caf026e71e155 Mon Sep 17 00:00:00 2001 From: Vuong Hoang Date: Mon, 6 Jul 2026 21:50:22 -0700 Subject: [PATCH] =?UTF-8?q?feat(web):=20Claude=20Design=20console=20?= =?UTF-8?q?=E2=80=94=203-column=20wire=20monitor=20(v0.20.0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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). --- docs/contracts/web_debug_surface.contract.md | 30 +- docs/design/ratatoskr-web-design-brief.md | 270 +++ persistent-memory.md | 14 +- pyproject.toml | 2 +- src/ratatoskr/web/static/index.html | 1985 +++++++++--------- uv.lock | 2 +- 6 files changed, 1318 insertions(+), 985 deletions(-) create mode 100644 docs/design/ratatoskr-web-design-brief.md diff --git a/docs/contracts/web_debug_surface.contract.md b/docs/contracts/web_debug_surface.contract.md index 0f17836..74f5d2a 100644 --- a/docs/contracts/web_debug_surface.contract.md +++ b/docs/contracts/web_debug_surface.contract.md @@ -3,7 +3,7 @@ contract_version: "2.1" module: "ratatoskr.web" purpose: "v0.19.2 web debug-surface parity: 3 admin/debug panes (Tools inventory, BifrostState, AdminEvents SSE) proxied server-side with the admin key server-held, plus a reliable PAD-refresh poll and a non-engine reasoning indicator in the transcript." target_module: "ratatoskr.web (server.py routes + entrypoint.py + static/index.html)" -scope: "v0.19.2 web debug-surface parity — bring the browser surface (now the PRIMARY debug surface) to TUI parity. THREE new admin/debug panes proxied server-side + TWO transcript affordances. (1) Tools inventory: GET /api/sessions/{id}/tools proxies owner-scoped get_session_tools into the tools pane (what the LLM HAS at turn-fire), above the live tool events. (2) BifrostState pane: GET /api/sessions/{id}/bifrost proxies admin-scoped get_session_bifrost; the admin key is SERVER-HELD (app.state.admin_key from RATATOSKR_ADMIN_API_KEY), never sent to the browser. (3) AdminEvents pane: GET /api/admin/events is an SSE proxy of stream_admin_events, session-filtered SERVER-side (heartbeats + other-session events dropped), re-emitted under a fixed 'admin_event' name so every dotted type renders with one browser listener. (4) PAD refresh: the persona/affect pane polls a bounded window instead of a single 2s shot that raced the post-turn-async affect.emit. (5) Reasoning indicator: an ephemeral, clearly-non-engine transcript line on `thinking` deltas, cleared when text begins. Direct in-session TDD (the #17/#18 pattern); this contract is authored post-implementation to anchor the heid code review (the client wrappers get_session_tools/get_session_bifrost/stream_admin_events are already contracted in the sessions/sse_client specs — this contract governs the WEB proxy + presenter surface only)." +scope: "v0.19.2 web debug-surface parity — bring the browser surface (now the PRIMARY debug surface) to TUI parity. THREE new admin/debug panes proxied server-side + TWO transcript affordances. (1) Tools inventory: GET /api/sessions/{id}/tools proxies owner-scoped get_session_tools into the tools pane (what the LLM HAS at turn-fire), above the live tool events. (2) BifrostState pane: GET /api/sessions/{id}/bifrost proxies admin-scoped get_session_bifrost; the admin key is SERVER-HELD (app.state.admin_key from RATATOSKR_ADMIN_API_KEY), never sent to the browser. (3) AdminEvents pane: GET /api/admin/events is an SSE proxy of stream_admin_events, session-filtered SERVER-side (heartbeats + other-session events dropped), re-emitted under a fixed 'admin_event' name so every dotted type renders with one browser listener. (4) PAD refresh: the persona/affect pane polls a bounded window instead of a single 2s shot that raced the post-turn-async affect.emit. (5) Reasoning indicator: an ephemeral, clearly-non-engine transcript line on `thinking` deltas, cleared when text begins. Direct in-session TDD (the #17/#18 pattern); this contract is authored post-implementation to anchor the heid code review (the client wrappers get_session_tools/get_session_bifrost/stream_admin_events are already contracted in the sessions/sse_client specs — this contract governs the WEB proxy + presenter surface only. v0.20.0 REDESIGN (Claude Design 'Ratatoskr Console' import): the tabbed telemetry column is replaced by a 3-column command-console — a left engine-ticker rail (the DEBUG + ADMIN + tool/turn-lifecycle feeds MERGED into one timeline via tickerAdd, plus a tools-armed chip list + a full-detail Bifrost rail pane) · a center conversation (per-turn INLINE chain-of-thought, replacing the separate Think pane) · a right resizable affect console (dominant/canonical-mood centerpiece + bipolar PAD faders each carrying a turn-to-turn Δ+sparkline + a P×A mood orbit + relations metric rows + canonical directive). ALL SERVER ROUTES UNCHANGED. Single-file/no-CDN/vanilla preserved; adds a light/dark theme toggle (dark default) + an inlined data-URI favicon. Presenter FN renames tracked below (renderBifrostState→renderBifrost; renderAffectPane→renderConsole; setPersonaStrip removed; tickerAdd/setFader/setFaderTrend/renderOrbit/renderDominant/renderDerived/renderRelations/renderDirective added). INV-001/INV-004 held.)." depends_on: - "httpx" - "starlette" @@ -114,24 +114,24 @@ functions: - "POST-002: any non-200, fetch error, or parse error is swallowed (best-effort) — a blank transcript is acceptable; opening the workspace is never blocked." flexibility: "prescriptive" - - name: "web pane renderers (index.html: renderToolsInventory / renderBifrostState / openAdminEvents)" - signature: "renderToolsInventory(inv) ; renderBifrostState(b) ; openAdminEvents(sessionId)" - description: "Render the three new surfaces; all content escaped (INV-004)." + - name: "web pane renderers (v0.20.0: index.html: renderToolsInventory / renderBifrost / openAdminEvents + tickerAdd)" + signature: "renderToolsInventory(inv) ; renderBifrost(b) ; openAdminEvents(sessionId) ; tickerAdd(kind, msg, dim)" + description: "Render the debug/admin surfaces into the 3-column console; all content escaped (INV-004). v0.20.0: BifrostState is now a full-detail LEFT-RAIL pane (renderBifrost, renamed from renderBifrostState); AdminEvents + the raw debug/op log + tool_start/result + turn lifecycle are MERGED into one engine-ticker timeline via tickerAdd (openAdminEvents routes admin_event → tickerAdd; the turn SSE handlers route worker_phase/tool/text_boundary/affect_update → tickerAdd); Tools inventory is a rail chip list (renderToolsInventory)." postconditions: - - "POST-001: every dynamic value (agent_id, tool names/descriptions, endpoint, caps, admin event type + data) is passed through esc() or esc(JSON.stringify(...)); no upstream string reaches innerHTML unescaped." - - "POST-002: openAdminEvents closes a prior EventSource before opening a new one (state.adminES); the admin data blob renders via esc(JSON.stringify(d.data))." - - "POST-003: renderToolsInventory prepends the static inventory ABOVE live tool events without clobbering them (a re-render replaces only the .tools-inventory block)." - - "POST-004: the Tools inventory renders tool NAMES only — a compact comma-joined summary ('what does the LLM have', the debug glance); per-tool DESCRIPTIONS are surfaced in the BifrostState pane's tools list, deliberately NOT duplicated here. (Heid panel Hulda/Regin precision finding — accepted: contract wording clarified, code unchanged; the earlier 'names/descriptions' phrasing in INV-004 refers to the SET of value types that MAY appear across the new panes and must be escaped, not a mandate that every pane render descriptions.)" + - "POST-001: every dynamic value (agent_id, tool names/descriptions, endpoint, caps, consumer_id, admin event type + data, ticker msg/dim) is passed through esc() or esc(JSON.stringify(...)); no upstream string reaches innerHTML unescaped." + - "POST-002: openAdminEvents closes a prior EventSource before opening a new one (state.adminES) and, on stream_error, closes so native EventSource does NOT retry-loop; admin events render into the engine ticker via tickerAdd." + - "POST-003: renderToolsInventory renders builtin + bifrost tool NAMES as rail chips (a compact 'what does the LLM have' glance); renderBifrost renders endpoint + connected + consumer_id + capabilities_granted chips + per-tool name/description rows (the full detail, admin-gated; the admin key stays server-held). tickerAdd bounds the feed to the last 400 rows (a tail, not an archive)." + - "POST-004: tool NAME-vs-DESCRIPTION split preserved — the rail chip list shows names only; per-tool descriptions live in the Bifrost pane's tools list. The engine-ticker spine (.ticker-inner::before) lives on the content-height wrapper so it stays visible when auto-scrolled to the newest entry." flexibility: "open" - - name: "renderAffectPane + trend (v0.19.4 — relation_edge/1 render + sparkline)" - signature: "renderAffectPane(snap) ; pushAffectHistory(snap) ; sparkline(vals) ; trendDelta(vals)" - description: "Render the Tier-3 affect snapshot as PAD mood + the durable per-entity relational model, each value with a Δ-vs-previous + a session-lived sparkline." + - name: "renderConsole + trend (v0.20.0 — unified persona/affect console; supersedes renderAffectPane/renderPersonaPane/setPersonaStrip)" + signature: "renderConsole(snap) ; setFader(axis,v) ; setFaderTrend(axis) ; renderOrbit() ; renderDominant(snap) ; renderDerived(snap) ; renderRelations(snap) ; renderDirective(snap) ; pushAffectHistory(snap) ; sparkline(vals) ; trendDelta(vals)" + description: "ONE render path for BOTH the Tier-1 persona_state snapshot and the Tier-3 affect snapshot (renderConsole), feeding the right affect console: dominant/canonical-mood centerpiece, bipolar PAD faders (each with a turn-to-turn Δ + sparkline), a P×A mood orbit from PAD history, an affect-derived grid, relations metric rows, and the canonical directive. Replaces the v0.19.x split of renderPersonaPane (Tier-1 pane) + renderAffectPane (Tier-3 pane) + setPersonaStrip (top-bar strip, removed — PAD now lives in the console faders)." postconditions: - - "POST-001: reads snap.relations (relation_edge/1: target_entity + trust_ability/benevolence/integrity + warmth as {value,confidence,evidence_count} + agency + relation_context + obligation_balance) — the CURRENT Worldtree emit shape; falls back to the legacy flat snap.valence for an older emitter. SUPERSEDES the #18-D2 contract's valence assumption (Worldtree's #265 Vili rework replaced valence/regard with the relation_edge/trust model; the old renderer read snap.valence and showed an empty 'valence (0)' — the bug this fixes)." - - "POST-002: each metric shows current value + Δ-vs-previous (▲/▼) + a unicode sparkline auto-scaled to its OWN observed range (flat ▄ when sub-0.01 stable — no noise amplification), drawn from AFFECT_HIST (rolling, HIST_CAP=24, session-lived)." - - "POST-003: pushAffectHistory dedupes by emitted_at so the ~4x/turn post-turn PAD poll contributes ONE sample/turn; history is CLIENT-side only (lost on reload — durable cross-session history via a provider-side snapshot log is a deferred follow-up, NOT built here)." - - "POST-004: INV-001 honesty — no fabricated Tier-1 fields (no synthesized dominant_emotion). INV-004 — head() escapes its whole argument (incl. target_entity + relation_context from the snapshot) and metric() escapes every cell; numeric values go through toFixed, never innerHTML-raw." + - "POST-001: reads snap.relations (relation_edge/1: target_entity + trust_ability/benevolence/integrity + warmth as {value,confidence,evidence_count} + agency + relation_context) — the CURRENT Worldtree emit shape; falls back to the legacy flat snap.valence for an older emitter. Tier-1 fields (baseline_pad, mood_drift, dominant_emotion, emotions_active) render WHEN PRESENT, '—' when absent (Tier-3 lacks them)." + - "POST-002: each PAD fader + relation metric shows current value + Δ-vs-previous (▲/▼) + a unicode sparkline auto-scaled to its OWN observed range (flat ▄/— when sub-0.01 stable — no noise amplification), drawn from AFFECT_HIST (rolling, HIST_CAP=24, session-lived). setFaderTrend fills the per-meter Δ+spark slots; renderOrbit plots the last N (P,A) samples as a scaled trail with a pulsing current marker." + - "POST-003: pushAffectHistory dedupes by emitted_at||last_updated_at so the ~4x/turn post-turn PAD poll contributes ONE sample/turn; history is CLIENT-side only (lost on reload — durable cross-session history via a provider-side snapshot log is a deferred follow-up, NOT built here)." + - "POST-004: INV-001 honesty — no fabricated Tier-1 fields. The dominant-emotion centerpiece shows a real OCC dominant_emotion (Tier-1) OR the CANONICAL mood word from canonMood(pad) (Tier-3, dimmed) OR '—'; NEVER a synthesized emotion. The affect-derived grid drops non-emitted metrics (intensity/decay-τ) and shows only real/client-derived cells (baseline/drift real for Tier-1, client-derived samples/volatility). INV-004 — every dynamic value passes through esc(); numerics go through toFixed, never innerHTML-raw." flexibility: "open" - name: "canonical affect-NL (v0.19.5 — vendored Worldtree d2 render canons)" diff --git a/docs/design/ratatoskr-web-design-brief.md b/docs/design/ratatoskr-web-design-brief.md new file mode 100644 index 0000000..fc00687 --- /dev/null +++ b/docs/design/ratatoskr-web-design-brief.md @@ -0,0 +1,270 @@ +# 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 ``: `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 ` +
-
-
- ᛯ - ratatoskr - wire monitor +
+
+ + + + + +
+
+
+
+
+ ❯ + + + +
+
+
+ Enter send + ⇧Enter newline + ⌃C cancel + + + — +
+
+
+ + +
+
-
- offline -
-
-
-
—
-
- -
-
-
-
- ❯ - - - -
-
- -
- -
- tools - -
-
-
no tool events yet
-
waiting for wire telemetry…
-
no chain-of-thought captured yet
-
persona state loads on session open
-
bifrost dispatch state loads on session open
-
admin lifecycle events stream on session open
-
-
-
+ +
-

ᛯ ratatoskr-web

+

ᛯ ratatoskr-web

Internal-LAN debug companion to the Worldtree Conversation API. Pick an agent and open a session to begin watching the wire.

@@ -576,25 +635,19 @@ body {
-
-
- Enter send - ⇧Enter newline - ⌃1–⌃4 panes - ⌃C cancel turn -
-
ratatoskr —
-
-