diff --git a/docs/superpowers/specs/2026-06-29-default-ui-design.md b/docs/superpowers/specs/2026-06-29-default-ui-design.md new file mode 100644 index 0000000..04bd2fa --- /dev/null +++ b/docs/superpowers/specs/2026-06-29-default-ui-design.md @@ -0,0 +1,227 @@ +# 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 1–6: media → tap → brain → barge-in → +rented-transport PSTN → spend cap) **and** the ACD/escalation rungs (capability ladder rungs 1–2). 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.