# 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), **[Spec 044](../044-foss-desk-ticket-sync/spec.md)** (sync FOSS)  
**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 |
