ligbox-ops-platform/specs/030-agentic-ops-ui/spec.md
Ligbox Spec Hub fd491e5859 Implement Spec 030 Agentic Ops Mission Board (UI-A/B/C).
Add agent_incidents dedup, overview/incidents/timeline API, mission board UI with fleet rail, kanban, context panel, mobile tabs, poll and keyboard shortcuts.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-20 06:49:38 +00:00

198 lines
8.2 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.

# Spec 030 — Agentic Ops UI (Mission Board)
**Criado:** 2026-06-20
**Solicitado por:** Roger
**Prioridade:** P1 (UX operacional — pós Spec 029 MVP)
**Status:** ✅ Implementado (UI-A/B/C) — produção VM122
**Sistema:** Desk VM122 · view `agentic-ops`
**Relacionado:** Spec **029** (API + agentes) · Spec **033** (proc-card / modal) · Spec **019** (hub CH-* — futuro link)
**Referências externas (padrões UX, não fork):**
- [Mission Control](https://github.com/builderz-labs/mission-control) — mission board, inbox, timeline, SSE
- [Agent Track Dashboard](https://github.com/jenna-studio/agent-track-dashboard) — kanban por agente/tarefa
---
## Problema actual (029 MVP)
| Sintoma | Causa |
|---------|--------|
| 140+ threads repetidas | Cada tick cron cria finding + thread novo (mesmo cenário) |
| 3 colunas iguais visualmente | Roster, inbox e findings competem sem hierarquia |
| Dropdown de threads inútil | Lista longa, sem agrupamento |
| Operador perde foco | Não há “o que fazer agora” num só sítio |
**Objectivo:** transformar Agentic Ops num **painel de comando** (mission board), não num dump de mensagens.
---
## Princípios de design
1. **Um problema = um card** — deduplicação por `scenario_id` (não por `finding_id`)
2. **Severidade manda** — layout lido de cima: Crítico → Alto → Aviso → OK
3. **Agente visível** — cor/avatar por A0A7 + Vigia (Spec 029 roster)
4. **Acção humana clara** — cada card: título, última detecção, passo sugerido (T0/T1), botões Ack / Abrir thread / Atribuir
5. **Contexto à direita** — thread + chat Copiloto só quando o operador selecciona um card
6. **Status bar sempre visível** — tier T0/T1, Ollama, último tick, contagem aberta
---
## Wireframe — desktop (≥1200px)
```text
┌──────────────────────────────────────────────────────────────────────────────┐
│ AGENTIC OPS [T1 · Ollama OK] último tick 06:32 │ 12 abertos │ ↻ │
├────────────┬─────────────────────────────────────────────────┬───────────────┤
│ FROTA │ MISSION BOARD (kanban horizontal) │ CONTEXTO │
│ │ │ │
│ ● Maestro │ ┌─ CRÍTICO ─┐ ┌─ ALTO ────┐ ┌─ AVISO ──┐ ┌ OK ┐│ Thread #47 │
│ ○ Pulso │ │ (vazio) │ │ FOSSBill │ │ OpenPanel│ │ 5 ││ FOSS VM123 │
│ ○ Trilho │ │ │ │ VM123 │ │ bridge │ │ ││ │
│ ○ Copiloto │ │ │ │ gap 415m │ │ funil 10 │ │ ││ [timeline] │
│ … │ └───────────┘ └───────────┘ └──────────┘ └────┘│ Vigia → humano│
│ │ ↑ card activo (borda) │ │
│ [filtro │ Card: cenário · agente · há 8 min · ack │ [responder] │
│ agente] │ │ │
│ │ Timeline compacta (últimas 24h, collapsible) │ Copiloto A6 │
│ │ ████░░ ticks · 4 findings únicos │ [perguntar…] │
└────────────┴─────────────────────────────────────────────────┴───────────────┘
```
### Mobile / tablet (<1200px)
- Tab bar: **Board** | **Frota** | **Contexto**
- Kanban vira lista vertical por severidade (accordion)
---
## Componentes UI
| Componente | Descrição | Ficheiro alvo (v1) |
|------------|-----------|-------------------|
| `AgenticStatusBar` | tier, ollama, last_tick, counts | `agentic-ops.js` ou `agentic-ops/` |
| `AgentFleetRail` | A0A7 compacto + pulse se finding aberto | idem |
| `MissionBoard` | 4 colunas severidade, cards deduplicados | idem |
| `IncidentCard` | cenário, agente, age, action, CTA | idem |
| `RunTimeline` | últimos N ticks / runs (sparkline ou lista) | idem |
| `ContextPanel` | thread messages + reply + chat A6 | idem |
| `EmptyState` | “Nenhum alerta — último tick OK” | idem |
**Tokens visuais:** reutilizar `styles.css` Desk (`.card`, `.pill`, `.badge`, cores SOC existentes).
**Cores por agente (proposta):**
| ID | Accent | Uso |
|----|--------|-----|
| A0 Maestro | `#6366f1` | coordenação |
| A1 Pulso | `#22c55e` | nós/VM112 |
| A2 Trilho | `#3b82f6` | rede/DNS |
| A6 Copiloto | `#a855f7` | chat |
| Vigia | `#f59e0b` | findings T0 |
| A7 Remediador | `#ef4444` | acções (futuro) |
---
## Modelo de dados — deduplicação (backend)
### Regra
- **Incidente activo** = 1 por `(scenario_id)` enquanto existir finding `open` (não ack)
- Novos ticks **actualizam** o incidente (last_seen, count, latest_finding_id) em vez de criar thread nova
- Thread **reutilizada** via `related_scenario_id` (nova coluna) ou lookup por cenário
### Tabela nova (proposta)
```sql
CREATE TABLE agent_incidents (
id INTEGER PRIMARY KEY,
scenario_id TEXT NOT NULL UNIQUE,
primary_agent TEXT NOT NULL,
severity TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'open', -- open | ack | resolved
title TEXT NOT NULL,
latest_finding_id INTEGER,
occurrence_count INTEGER NOT NULL DEFAULT 1,
first_seen_at TEXT NOT NULL,
last_seen_at TEXT NOT NULL,
suggested_human_action TEXT,
thread_id INTEGER,
acknowledged_at TEXT,
acknowledged_by TEXT
);
```
### API nova (v1.1 agents)
| Método | Path | Descrição |
|--------|------|-----------|
| GET | `/api/v1/agents/incidents` | Lista deduplicada para kanban (`?status=open`) |
| GET | `/api/v1/agents/incidents/{id}` | Detalhe + timeline runs recentes |
| POST | `/api/v1/agents/incidents/{id}/ack` | Ack incidente + fecha thread inbox |
| GET | `/api/v1/agents/overview` | Status bar: last_tick, counts by severity, ollama |
**Compatibilidade:** endpoints 029 (`/inbox`, `/findings`, `/threads`) mantidos; UI 030 consome preferencialmente `/incidents` + `/overview`.
Ver [`contracts/agentic-ui-api.md`](contracts/agentic-ui-api.md).
---
## Fluxos operador
```mermaid
flowchart LR
A[Login Desk] --> B[Agentic Ops]
B --> C{Overview}
C --> D[Mission Board]
D --> E[Seleccionar card]
E --> F[Context Panel: thread]
F --> G{Acção}
G --> H[Ack / Arquivar]
G --> I[Reply humano]
G --> J[Chat A6 T1]
H --> D
I --> F
J --> F
```
---
## Fases de entrega
| Fase | Entrega | Depende |
|------|---------|---------|
| **UI-A** | Status bar + Mission Board (dados actuais `/findings` agrupados no frontend) | — |
| **UI-B** | Backend `agent_incidents` + dedup no tick | UI-A |
| **UI-C** | Fleet rail + timeline + mobile tabs | UI-A |
| **UI-D** | SSE/poll 30s (live refresh) | Spec 029 H3 |
| **UI-E** | Link card → ticket Desk / CH-* hub | Spec 019 |
Detalhe: [`tasks.md`](tasks.md).
---
## Critérios de aceitação
- [ ] Operador vê **≤10 cards** para os 4 cenários recurrentes actuais (não 140 threads)
- [ ] Card mostra: severidade, agente, cenário, “há X min”, acção sugerida
- [ ] Click card abre contexto com thread **única** por cenário
- [ ] Ack remove card da coluna activa
- [ ] Status bar reflecte último tick worker (<15 min em produção)
- [ ] Layout responsivo (3 tabs em mobile)
- [ ] Zero regressão auth JWT / RBAC Spec 027
---
## Fora de scope (030)
- Substituir API 029 por Mission Control externo
- WebSocket full-duplex (fica UI-D / Spec 029 H3)
- Runbooks R0R3 automáticos (Spec 029 H2)
- React rewrite completo do Desk (opcional fase futura `agentic-ops/` Vite)
---
## Referências internas
- [`design/wireframes.md`](design/wireframes.md) detalhe visual e estados
- [`contracts/agentic-ui-api.md`](contracts/agentic-ui-api.md) contrato API v1.1
- Spec 029 [`agents-roster.md`](../029-agentic-ops-runbooks/agents-roster.md)