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>
198 lines
8.2 KiB
Markdown
198 lines
8.2 KiB
Markdown
# 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 A0–A7 + 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` | A0–A7 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 R0–R3 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)
|