# 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)
