Files
rutster/docs/superpowers/specs/2026-06-29-default-ui-design.md
opencode controller 42060b68e3
Some checks failed
CI / fmt (pull_request) Failing after 58s
CI / clippy (pull_request) Failing after 33s
CI / test (1.85) (pull_request) Failing after 32s
CI / test (stable) (pull_request) Failing after 47s
CI / deny (pull_request) Failing after 41s
spec(ui): default operator console design (later-rung, not yet scheduled)
Design record for the operator console — the downstream non-technical
operator's surface. **Not scheduled for current build;** lands after the
spearhead (steps 1-6) and the ACD/escalation rungs (capability ladder
rungs 1-2). The current build target is slice-2; the next build target
after slice-2 is slice-3 (the OpenAI Realtime adapter). An agent that
proposes building the console now is wrong — point it at spec §0.

Records the visual-brainstorming session's resolved decisions so they
don't get re-litigated at implementation time:

§1 Principles (load-bearing):
- GUI is a pure API client — **green-zone, never an insider** (ADR-0008);
  holds no privilege a third party couldn't get from the public API.
- Config-as-code is the single source of truth; the UI is a reconciled
  *view* (the k8s model — declarative desired-state in git, the engine
  reconciles; console + CLI + git are views over it).
- Operator persona, not builder. The builder authors flows in **code +
  an AI pair** (ARCHITECTURE 'DX spine'); this console is NOT the
  authoring surface for the technical builder. **No drag-drop authoring
  canvas** — a deliberately-closed decision (vision-revision §8).
- Ships in the batteries-included distro and the all-in-one image
  (the reference GUI is bundled — 'boom' includes a usable console).

§2 IA — Ops Console shell: left nav + center panel + persistent live rail.
Dense single-screen; for an operator who lives in it. Live rail always
visible regardless of the selected section.

§3 Dashboards — Live Ops (default landing) + Quality (sibling view). Maps
the k8s split: Live Ops = live operational state; Quality = the data-owned
eval loop (capability ladder rungs 3→4).

§4 Flows editor — master/detail + a generated, navigable preview map.
The preview map is a *view* rendered from the config, not a drag-drop
authoring canvas. Earns its place by showing branching (the escalation
split) the linear stage-list flattens.

§5 Declarative flow schema (illustrative YAML; format is open). The config
the console edits via the API; the builder edits in git; the engine
reconciles. The map is generated from this structure.

§6 Other sections (conventional): Agents · Numbers · Queues · People ·
Recordings · Settings.

§7 Architecture: separate app, public API only; never reaches into the FOB.
Config writes go through the reconcile path. Live data rides the event
stream off Valkey (ADR-0005); never the 20 ms media loop.

§8 v1 vs. later scoping. §9 open decisions (flow config format, map
rendering depth, quality metrics, live transport, framework).

Cross-refs ADR-0002 (fused vertical + composable platform), ADR-0006
(WebRTC ingress / escalation UX), ADR-0007 (rented trunk / Numbers
config), ADR-0008 (FOB / green-zone — the UI is green-zone),
ARCHITECTURE.md 'DX spine' + 'GUI & extension architecture', PORT_PLAN §5
(contact-center domain).
2026-06-29 20:27:25 -04:00

228 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Default UI — Design (the operator console)
- **Status:** Design accepted (direction). **Not yet scheduled — a later-rung surface.**
- **Date:** 2026-06-29
- **Origin:** Visual brainstorming session (the deployability + default-UI thread of the 2026-06-29
strategic review).
- **Related:** [ADR-0002](../../adr/0002-north-star-and-fused-core.md) (fused vertical + composable
platform), [ADR-0006](../../adr/0006-ingress-posture.md) (WebRTC ingress / escalation UX),
[ADR-0007](../../adr/0007-trunk-rented-transport.md) (rented trunk / Numbers config),
[ADR-0008](../../adr/0008-fob-and-green-zone.md) (the UI is **green-zone**),
[ARCHITECTURE.md](../../ARCHITECTURE.md) "DX spine" + "GUI & extension architecture",
[PORT_PLAN.md](../../PORT_PLAN.md) §5 (contact-center domain).
---
## 0. Scope & sequencing — READ THIS FIRST (esp. agents)
**Do not implement this yet.** This is a **design record**, not an implementation plan. The UI is a
**later-rung surface** that lands *after* the spearhead (steps 16: media → tap → brain → barge-in →
rented-transport PSTN → spend cap) **and** the ACD/escalation rungs (capability ladder rungs 12). The
current build target is **slice-2 (the agent tap)**. An agent that proposes building the console now is
wrong — point it here, at this section.
When the UI *is* built, it gets its own slice spec + implementation plan at that time. This document
records *what* it should be and *why*, so those decisions don't get re-litigated.
This UI is also **not** the authoring surface for the technical builder. The builder authors flows in
**code + an AI pair** (config-as-text, git-versioned — ARCHITECTURE "DX spine"). This console is for the
**downstream non-technical operator**. There is **no drag-drop authoring canvas** (a deliberately-closed
decision — vision-revision §8). These two facts are load-bearing; do not blur them.
---
## 1. Principles (load-bearing)
1. **The GUI is a pure API client — green-zone, never an insider.** A separate application, outside the
FOB trust boundary ([ADR-0008](../../adr/0008-fob-and-green-zone.md)). It holds **no privilege a third
party couldn't get** from the public API. No backdoors, no coupling to internal config files
(FreePBX's mistake). This guarantees API completeness — if the console can do it, the API can.
2. **Config-as-code is the single source of truth; the UI is a reconciled *view*.** The Kubernetes
model (ARCHITECTURE "GUI & extension architecture"): declarative desired-state lives in git and is
reconciled by the engine; the API/CLI/console are views over it. The operator edits in the console
**or** the builder edits in git — same source of truth, reconciled. The console is the *dashboard*,
never a side-channel.
3. **Operator persona, not builder.** See §0. The console serves the person running the contact center
day-to-day, exactly as integrators built operator UIs on top of Asterisk.
4. **Ships in the batteries-included distro and the all-in-one image** (the reference GUI is bundled —
"boom" includes a usable console). The ecosystem can build rival GUIs on the same public API.
---
## 2. Information architecture — the Ops Console shell
Chosen shell: **left nav + center panel + a persistent live rail.** Dense, single-screen; for an
operator who lives in it.
```
┌─────────────────────────────────────────────────────────────────────┐
│ rutster ▸ Acme Support ◴ on shift │ top bar (tenant, session)
├───────────┬─────────────────────────────────────────┬───────────────┤
│ Dashboard │ │ LIVE │
│ Flows │ │ 12 active │
│ Agents │ main panel (selected section) │ 87% contained │
│ Numbers │ │ $4.10/hr │
│ Queues │ │ ⚠ 1 escalation│
│ People │ │ │ persistent
│ Recordings│ │ (always in │ live rail
│ Settings │ │ view) │
└───────────┴─────────────────────────────────────────┴───────────────┘
left nav center panel live rail
```
- **Left nav** sections: Dashboard · Flows · Agents · Numbers · Queues · People · Recordings · Settings.
- **Live rail** (right, persistent): active calls, containment %, spend rate, escalation alerts. Fed by
the live event stream (§7). Always visible regardless of the selected section.
---
## 3. Dashboards
Two dashboards. **Live Ops is the default landing**; **Quality is a sibling view** (a nav/tab sibling of
the dashboard, one click away). This maps the k8s split: Live Ops = live operational state, Quality = the
data-owned eval loop.
### 3.1 Live Ops (default)
Leads with **working the floor**:
- Compact KPI strip: containment · calls · AHT · spend · escalations (today).
- **Live calls table:** caller · flow · who (🤖 agent persona / queue / 🧑 human) · state · duration ·
actions. Actions include **listen** and **take over** — the escalation gesture (capability **rung 2**),
which joins a WebRTC leg via the audiohook/barge primitive ([ADR-0006](../../adr/0006-ingress-posture.md),
PORT_PLAN §2 audiohook).
### 3.2 Quality (sibling)
Leads with **watching the AI**:
- **Containment trend** (today / 7d / 30d) on calls *you own*.
- **"Where AI handed off"** — escalation reasons ranked ("asked for refund" ×8, "couldn't find order"
×5, "wanted a human" ×4), each drilling into the calls/transcripts. This is the **data-owned failure
signal** that feeds the self-improvement loop (capability **rungs 3→4**) — the differentiation, made
visible. Beware the containment-vs-resolution metric trap: surface *resolution*, not just deflection.
---
## 4. Flows editor — the "call-flow builder"
The console's flagship surface, and the answer to "what is a call-flow builder in the AI era." It is a
**structured config editor**, not a drag-drop IVR canvas. A flow = what happens on an inbound call.
**Shape: master/detail editor + a generated, navigable preview map.**
```
┌─ Flows ▸ Acme Support ───────────────────────────────────────────────┐
│ PREVIEW — generated from config · click a node to edit │
│ 📞 Number → 🤖 Agent → ↗ Escalation? ─┬─ ✓ contained → 📝 Post-call │ generated map
│ └─ ✗ stuck → 🧑 Queue → Human │ (read-only, navigable)
├──────────────┬───────────────────────────────────────────────────────┤
│ STAGES │ AGENT — selected │
│ 📞 Inbound │ persona [ Support Bot ▾ ] │ master / detail
│ 🤖 Agent ◂ │ knowledge base [ Acme Docs ▾ ] │ editor
│ 🕐 Hours │ greeting [ "Hi, Acme support…" ] │
│ ↗ Escalation │ [ + tool ] [ + guardrail ] │
│ 📝 Post-call │ │
└──────────────┴───────────────────────────────────────────────────────┘
```
- **Master/detail:** the stage list (left) + the selected stage's structured form (right).
- **Generated preview map (top):** a horizontal pipeline **rendered *from* the config** — read-only and
**navigable** (click a node → selects that stage below; edits regenerate the map). It is a *view*, not
an authoring canvas. It earns its place by showing **branching** the linear stage-list flattens (the
escalation split). Map and editor stay in sync; the config is the truth.
- **Flow spine (the stages):** `Inbound → Agent → Hours → Escalation → Post-call`, branching at
escalation (`contained → Post-call` / `stuck → Queue → Human`).
- **Agent stage fields:** persona (ref) · knowledge base · greeting · tools · guardrails.
**Why this is "genuinely new design space, not dialplan 2.0":** the conversation half-dissolves into the
agent (the model improvises the dialogue), so what's left to author is the **routing / escalation /
business scaffolding around the agent** — which is mostly structured config, not a node graph.
---
## 5. The declarative flow schema (the source of truth the console edits)
The console reads/writes this through the public API; the builder edits the same files in git; the engine
reconciles. **Illustrative** (format — YAML/TOML/typed — is an open decision; it must be typed, validated,
and LSP-friendly per ARCHITECTURE "DX spine"):
```yaml
# flows/acme-support.yaml — reconciled desired-state; console edits this via the API
flow: acme-support
inbound:
numbers: ["+15550100", "+18005550111"] # → Numbers section maps these (ADR-0007 rented transport)
agent:
persona: support-bot # ref → agents/support-bot.yaml (reusable)
knowledge_base: acme-docs
greeting: "Hi, Acme support — how can I help?"
tools: [order-lookup, refund-status]
guardrails: [no-pii-readback, max-attempts-3]
hours:
timezone: America/Los_Angeles
open: { mon-fri: "09:00-17:00" }
closed_action: voicemail
escalation:
when: [ai_stuck, caller_requests_human, sentiment_negative]
to: queue:support # → Queues / People (rung 2 takeover)
post_call:
- transcript: { to: crm }
- summary_email: { to: owner }
spend:
cap_per_call_usd: 0.50 # ENFORCED in-boundary (rutster-spend), not advisory
```
Agent personas (`agents/*.yaml`), knowledge-base refs, queues, and number inventory are separate
reusable objects referenced by flows. The preview map (§4) is generated from this structure.
---
## 6. Other sections (conventional — described, not mocked)
- **Agents** — reusable AI agent definitions (persona, KB, prompts, tools, guardrails) referenced by
flows. The brain itself is green-zone/external; this configures *how* it's invoked.
- **Numbers** — phone-number inventory and number→flow mapping; rented-transport provider config
([ADR-0007](../../adr/0007-trunk-rented-transport.md): CPaaS keys / out-of-tree SBC endpoint).
- **Queues** — ACD config (skills, overflow, SLAs) — PORT_PLAN §5 domain core.
- **People** — human agents (skills, presence, state); the escalation targets for rung 2.
- **Recordings** — calls / transcripts / CDR; the drill-down target from Quality. Data the operator owns.
- **Settings** — trunk/rented-transport, spend caps, brain/tap config, tenancy, RBAC, secrets refs.
---
## 7. Architecture & data flow
- **Separate app, public API only.** The console talks to the rutster control API (REST/gRPC) for
config CRUD and to the **event stream** (WebSocket/SSE) for live data (the rail + Live Ops). It never
reaches into the FOB; it has no privileged channel ([ADR-0008](../../adr/0008-fob-and-green-zone.md)).
- **Config writes go through the reconcile path**, not direct mutation — the console PUTs desired-state;
the engine reconciles (k8s model). Concurrent git edits and console edits converge on the same store.
- **The preview map is generated client-side from the fetched flow config** — no separate "map" data
model; the config *is* the model.
- **Live data** (rail, live-calls table, containment) rides the event stream off Valkey
([ADR-0005](../../adr/0005-event-bus.md)) — never the 20 ms media loop.
- **Auth:** SSO + RBAC + per-tenant scoping (this is legitimate server *ingress*
[ADR-0006](../../adr/0006-ingress-posture.md) "tap ≠ ingress"). Multi-tenant isolation is enforced
server-side; the console is just a scoped client.
---
## 8. Scope when built — v1 vs. later
**v1 (when scheduled):** the Ops Console shell; Live Ops dashboard; Flows editor (master/detail + preview
map) over the flow schema; Numbers, Agents, Settings enough to stand up a flow. Read-only Recordings.
**Later:** Quality dashboard depth (trend analytics, eval-set export — rung 3→4); rich Queues/People
management; rival-GUI ecosystem docs; theming.
**Non-goals (ratified, do not add):** a drag-drop authoring canvas; the builder's authoring surface (that
is code+AI); any privileged/backdoor channel; coupling to internal config-file formats.
---
## 9. Open decisions
- **Flow config format** — YAML vs TOML vs a typed schema with generated bindings (must be LSP-friendly).
- **Map rendering** — depth of branching shown; how sub-flows / shared fragments render.
- **Quality metrics** — exact containment/resolution definitions (avoid the deflection-gaming trap).
- **Live transport** — WebSocket vs SSE for the event stream to the console.
- **Framework** — out of scope here; an implementation-time decision for the future slice spec.