obsidian-vault/ligbox-ops-platform/specs/010-desk-assist-takeover/spec.md
2026-06-19 17:26:42 +00:00

418 lines
14 KiB
Markdown

# Feature Specification: Desk Assist & Takeover — Intervenção Técnica (010)
**Criado:** 2026-06-10
**Solicitado por:** Roger
**Status:** 📋 **Draft — decisões fechadas, pronta para plano**
**Prioridade:** **P0** (bloqueia operação humana no onboarding)
**Depende de:** Spec 001 (webhooks VM112), Spec 003 (auth/RBAC)
**Relacionada:** Spec 007 (push escalada), Spec 008 (Kanban/SLA), Spec 011 (OTRS futuro)
**API alvo:** `0.9.0-desk-assist` (VM122) + contratos VM112 `assist-v1`
---
## Resumo
Hoje o **Ligbox Ops Desk** é **observacional**: técnicos veem funil, tickets e timeline, mas **não podem intervir** quando o onboarding trava ou o cliente pede ajuda.
**Objetivo Spec 010:** transformar o Desk no **control plane de assistência humana** — com escalada bidirecional (cliente ou técnico), **modo ASM** (técnico substitui o cliente no wizard), pausa de sessão, ticket atribuído e **ações operacionais só via Desk** (nunca embed Proxmox/Carbonio).
O **wizard continua no VM112**; o **Desk (VM122)** orquestra escalada, atribuição, audit e acções API.
---
## Decisões confirmadas (Roger — 2026-06-10)
| # | Pergunta | Decisão |
|---|----------|---------|
| 1 | Quem inicia escalada? | **Cliente** (botão no wizard) **e técnico** (puxar sessão activa no Desk) |
| 2 | Visibilidade do técnico | **Não cego total** — observa até etapa simples; **intervenção relevante a partir de `domain.validated`**; takeover pleno especialmente após `account.created` |
| 3 | Takeover vs co-browse | **ASM — técnico substitui o cliente** (não co-browse guia). Cliente pausado durante assistência |
| 4 | Consoles externos | **Links em nova aba** (Proxmox, Carbonio, Traefik, Cloudflare) — **acções operacionais só no Desk** via API |
| 5 | OTRS | Escalada **fica no Desk (VM122)** por agora. Integração OTRS → **Spec 011** (VM112 ↔ OTRS, futuro) |
---
## Dois modos de operação
```mermaid
stateDiagram-v2
[*] --> Observador: onboarding normal
Observador --> Escalado: cliente pede ajuda OU técnico puxa OU failed OU stale
Escalado --> Assistindo: técnico assume ASM takeover
Assistindo --> Observador: handoff / resolvido
note right of Observador
Etapas started até antes de domain:
funil mínimo, sem takeover
end note
note right of Assistindo
Cliente pausado
Técnico actua no wizard VM112
Acções API via Desk
end note
```
### Modo Observador (default)
- Técnico vê **funil + sessões activas** (domínio, etapa, `session_id`, stale).
- Até **`onboarding.started`**: visibilidade mínima — processo deve correr sozinho.
- A partir de **`domain.validated`**: ticket pode ser criado/atualizado; técnico **pode** escalar ou ser alertado.
- **Sem acção** no wizard do cliente.
### Modo Assistência activa (ASM)
- Cliente **pausado** — wizard bloqueado com mensagem pt-BR.
- Técnico **substitui** o cliente no wizard (token takeover VM112).
- Ticket: `assisting` + `assigned_to`.
- Desk expõe **Console de assistência**: passo, timeline, acções permitidas, links externos (nova aba).
- **Handoff**: técnico encerra assistência → cliente retoma.
---
## Etapas do funil e regras de visibilidade/intervenção
Alinhado a `FUNNEL_EVENT_RANK` (Spec 001):
| Rank | Evento | Etapa | Observador | Escalar | Takeover ASM |
|------|--------|-------|------------|---------|--------------|
| 1 | `onboarding.started` | started | ✅ mínimo | ❌ | ❌ **+ ticket no «Criar conta»** (Spec 012 — Roger 2026-06-10) |
| 2 | `domain.validated` | domain_validated | ✅ | ✅ | ✅ (técnico puxa) |
| 3 | `dns.applied` | dns_applied | ✅ | ✅ | ✅ |
| 4 | `account.created` | account_created | ✅ + nota no ticket | ✅ | ✅ **principal** |
| 5+ | infra, completed, company, webmail | … | ✅ | ✅ | ✅ |
| 99 | `onboarding.failed` | failed | ✅ + ticket auto | ✅ auto | ✅ |
**Regra PII:** técnico **não fica totalmente cego** — vê domínio e etapa cedo; dados sensíveis (e-mail conta, perfil empresa) **reforçados após `account.created`** no console de assistência.
---
## Quem inicia escalada
| Origem | Actor | Acção |
|--------|-------|-------|
| Wizard VM112 | Cliente | Botão **«Preciso de ajuda técnica»** → webhook `onboarding.escalated` |
| Desk VM122 | Técnico / ops_lead | **«Assumir sessão»** em sessão ≥ `domain.validated` |
| Automático | Sistema | `onboarding.failed` · sessão stale 24h (já detectada) |
| Automático | Sistema | Push Spec 007 — «funil travado» (fase posterior) |
**Ambos** (cliente e técnico) podem iniciar. Primeiro a completar takeover **ganha** a sessão (lock optimista).
---
## Arquitetura
```mermaid
flowchart TB
subgraph VM112["VM112 — Wizard"]
W[Wizard cliente]
WA[Wizard ASM takeover]
API112[Assist API]
end
subgraph VM122["VM122 — Desk"]
DESK[Desk UI Console]
API122[Assist orchestrator]
DB[(SQLite tickets + assist_log)]
end
W -->|webhooks| API122
DESK -->|JWT| API122
API122 -->|service token| API112
API112 --> WA
API112 -->|pause/resume| W
DESK -->|links nova aba| EXT[Proxmox · Carbonio · Traefik · CF]
API122 -->|acções API| API112
```
**Princípio:** VM122 **nunca** embeda Proxmox/Carbonio. Links são referência; **botões de acção** chamam API (VM112 ou integrações futuras 005/006).
---
## Estados de ticket (novo)
| Status | Significado |
|--------|-------------|
| `open` | Ticket criado, ninguém a assistir |
| `escalated` | Cliente ou sistema pediu ajuda |
| `assisting` | Técnico em ASM takeover activo |
| `resolved` | Problema resolvido, aguarda fecho |
| `closed` | Encerrado |
Transições:
```
open → escalated → assisting → resolved → closed
open → assisting (técnico puxa directo, se ≥ domain.validated)
assisting → open (handoff cancelado — raro)
qualquer → closed (ops_lead / super_admin)
```
---
## Data model (VM122)
### `tickets` (alteração)
| Campo | Tipo | Uso |
|-------|------|-----|
| `status` | TEXT | + `escalated`, `assisting`, `resolved` |
| `session_id` | TEXT | FK lógica onboarding VM112 |
| `assist_mode` | TEXT | `null` \| `asm` |
| `assisted_by` | TEXT | username técnico em takeover |
| `assisted_at` | TEXT | ISO timestamp |
| `client_paused` | INTEGER | 1 se wizard pausado |
### `assist_sessions` (nova)
```sql
CREATE TABLE assist_sessions (
id INTEGER PRIMARY KEY,
session_id TEXT NOT NULL,
ticket_id INTEGER,
initiated_by TEXT NOT NULL, -- 'client' | 'technician' | 'system'
initiated_by_user TEXT, -- desk username se técnico
status TEXT NOT NULL, -- 'active' | 'handoff' | 'ended'
funnel_stage TEXT,
domain TEXT,
takeover_token_hash TEXT, -- token VM112 (não plain text)
started_at TEXT NOT NULL,
ended_at TEXT,
audit_summary TEXT
);
```
### `assist_actions` (audit log)
```sql
CREATE TABLE assist_actions (
id INTEGER PRIMARY KEY,
assist_session_id INTEGER NOT NULL,
actor TEXT NOT NULL,
action TEXT NOT NULL, -- 'escalate' | 'takeover' | 'action.dns_retry' | 'handoff'
payload TEXT,
created_at TEXT NOT NULL
);
```
---
## API VM122 (Desk — orchestrator)
| Método | Endpoint | Auth | Descrição |
|--------|----------|------|-----------|
| GET | `/api/v1/assist/sessions` | JWT | Sessões activas + estado assistência |
| GET | `/api/v1/assist/sessions/{session_id}` | JWT | Detalhe + timeline + ticket |
| POST | `/api/v1/assist/sessions/{session_id}/escalate` | JWT | Técnico escala manualmente |
| POST | `/api/v1/assist/sessions/{session_id}/takeover` | JWT | Inicia ASM — chama VM112 |
| POST | `/api/v1/assist/sessions/{session_id}/handoff` | JWT | Devolve controlo ao cliente |
| POST | `/api/v1/assist/sessions/{session_id}/actions/{action}` | JWT | Acção Desk (ver catálogo) |
| GET | `/api/v1/assist/sessions/{session_id}/links` | JWT | Deep links externos (nova aba) |
**Webhook ingress (VM112 → VM122):**
| Evento | Efeito |
|--------|--------|
| `onboarding.escalated` | Ticket `escalated` + notificação |
| `onboarding.assist.started` | Confirma takeover |
| `onboarding.assist.ended` | Handoff confirmado |
---
## API VM112 (wizard — contrato `assist-v1`)
*Implementação no repo VM112 (SUP-4). Desk consome.*
| Método | Endpoint | Auth | Descrição |
|--------|----------|------|-----------|
| POST | `/api/onboarding/sessions/{id}/pause` | service + desk JWT | Pausa wizard cliente |
| POST | `/api/onboarding/sessions/{id}/takeover` | desk JWT | Retorna URL wizard ASM + token |
| POST | `/api/onboarding/sessions/{id}/resume` | desk JWT | Retoma cliente |
| GET | `/api/onboarding/sessions/{id}/state` | service | Etapa, erros, campos permitidos |
| POST | `/api/onboarding/sessions/{id}/actions/{action}` | desk JWT | Executa acção no passo actual |
**Resposta takeover:**
```json
{
"takeover_url": "https://onboard.ligbox.com.br/assist/{session_id}?token=...",
"expires_in": 3600,
"client_paused": true
}
```
---
## Catálogo de acções (só Desk — MVP)
Acções invocam VM112 ou integrações; **nunca** abrem shell Proxmox.
| Acção | Etapa mínima | Efeito |
|-------|--------------|--------|
| `dns.revalidate` | dns_applied | Revalida DNS / Cloudflare via VM112 |
| `dns.reapply` | dns_applied | Re-aplica registos |
| `account.retry_sync` | account_created | Re-sync Carbonio |
| `infra.resync` | infra_synced | Re-sync Proxmox/Traefik via VM112 |
| `onboarding.mark_step_complete` | assisting | Avança passo (com confirmação) |
| `onboarding.abort` | assisting | Encerra sessão com motivo (ops_lead+) |
Links externos (GET `/links`) — **nova aba**, sem acção automática:
| Sistema | URL template |
|---------|--------------|
| Proxmox | `https://proxmox.../?node=...` (contexto tenant) |
| Carbonio | Admin domain |
| Traefik | Dashboard route |
| Cloudflare | Zone DNS |
---
## UI Desk — Console de assistência
### Dashboard / Funil
- Sessões **clicáveis** (hoje read-only).
- Badge: `observando` · `escalado` · `assistindo`.
- Botão **«Assumir sessão»** se etapa ≥ `domain.validated` e não locked.
### Vista ticket / sessão
| Bloco | Conteúdo |
|-------|----------|
| **Cabeçalho** | Domínio · etapa · `session_id` · assignee |
| **Estado** | Observador / Escalado / Assistindo (ASM) |
| **Timeline** | Webhooks existentes |
| **Acções Desk** | Botões catálogo (disabled se não assisting) |
| **Links** | Proxmox, Carbonio, Traefik, CF — `target=_blank` |
| **Takeover** | «Assumir sessão» → abre wizard ASM nova aba |
| **Handoff** | «Devolver ao cliente» |
### Permissões RBAC
| Role | Escalar | Takeover | Acções | Handoff | Ver links |
|------|---------|----------|--------|---------|-----------|
| super_admin | ✅ | ✅ | ✅ todas | ✅ | ✅ |
| ops_lead | ✅ | ✅ | ✅ todas | ✅ | ✅ |
| technician | ✅ | ✅ | ✅ N1/N2 | ✅ própria sessão | ✅ |
| noc | 👁️ | ❌ | ❌ | ❌ | 👁️ |
---
## Fluxo ASM (takeover)
```mermaid
sequenceDiagram
participant C as Cliente VM112
participant D as Desk VM122
participant W as Wizard VM112
participant T as Técnico
alt Cliente pede ajuda
C->>W: Clica ajuda técnica
W->>D: webhook onboarding.escalated
else Técnico puxa
T->>D: POST takeover
end
D->>W: POST pause + takeover
W-->>C: Wizard pausado
W-->>D: takeover_url + token
D-->>T: Console + link ASM
T->>W: Actua no wizard ASM
T->>D: Acções API (dns.reapply, etc.)
T->>D: POST handoff
D->>W: POST resume
W-->>C: Retoma onboarding
```
---
## User stories
### US1 — Cliente pede ajuda (P0)
Como cliente no wizard, quero pedir ajuda técnica para destravar o onboarding.
**Aceite:** botão no VM112 · sessão pausada · ticket escalado no Desk · push/e-mail ops (007).
### US2 — Técnico assume sessão (P0)
Como técnico, quero assumir uma sessão após domínio validado e actuar no wizard em nome do cliente.
**Aceite:** ASM takeover · cliente pausado · audit log · handoff funcional.
### US3 — Acções só no Desk (P0)
Como técnico, quero revalidar DNS ou re-sync infra **pelo Desk**, sem aceder Proxmox directamente.
**Aceite:** botões acção chamam API · links externos só referência nova aba.
### US4 — Observação pré-domínio (P1)
Como ops, quero que etapas antes de domínio corram sem intervenção humana.
**Aceite:** takeover disabled antes de `domain.validated`.
### US5 — Conflito de takeover (P1)
Como ops_lead, quero que apenas um técnico assista por sessão.
**Aceite:** segundo takeover recebe 409 + nome do assignee.
---
## Critérios de aceite MVP
- [ ] Escalada cliente (VM112) + webhook `onboarding.escalated`
- [ ] Escalada técnico no Desk (≥ `domain.validated`)
- [ ] ASM takeover — técnico substitui cliente, cliente pausado
- [ ] Handoff — cliente retoma
- [ ] Estados ticket: escalated, assisting, resolved
- [ ] Console Desk: timeline + acções + links nova aba
- [ ] Catálogo acções MVP (dns.revalidate, account.retry_sync, infra.resync)
- [ ] Audit log `assist_actions`
- [ ] RBAC conforme tabela
- [ ] pt-BR em toda UI/mensagens
- [ ] **Sem** embed Proxmox/Carbonio
- [ ] OTRS **fora** — Spec 011
---
## Fora de escopo (010)
- Co-browse / pointer mode (Roger escolheu ASM puro)
- Embed de consoles externos
- OTRS (→ Spec 011)
- Kanban visual (→ Spec 008, após 010)
- Acções directas Proxmox API no Desk (→ integrações 005/006 encapsuladas depois)
---
## Dependências e ordem
| Spec | Relação |
|------|---------|
| **001** | Webhooks + funil + session_id |
| **003** | RBAC + assigned_to |
| **007** | Push «sessão escalada» (paralelo ok) |
| **008** | Kanban usa estados 010 |
| **011** | OTRS VM112 — futuro, não bloqueia 010 |
**Prioridade backlog:** 010 **antes** de 005/006 para onboarding operacional.
---
## Referências
- SAP Commerce **Assisted Service Mode (ASM)** — emulação sessão agente
- Chatbase **Takeover** — escalada humano assume controlo
- BACKLOG **DESK-3**, **SUP-4.1/4.2**
- `specs/010-desk-assist-takeover/tasks.md`
- `specs/010-desk-assist-takeover/quickstart.md`
- `specs/011-integration-otrs/spec.md` (stub)
---
## Fases de entrega
| Fase | Entrega | Onde |
|------|---------|------|
| **A** | Webhook escalada + estados ticket + UI «Assumir» (sem takeover ainda) | VM122 |
| **B** | VM112 pause/takeover/resume + wizard ASM | VM112 |
| **C** | Console acções Desk + audit | VM122 + VM112 |
| **D** | Push escalada (007) + links contextuais | VM122 |