ligbox-ops-platform/specs/004-onboard-funnel-events/spec.md
Ligbox Spec Hub 3a2c64834b Initial import: ligbox-ops-platform + specs + LAPTOP + obsidian merge (CT130)
Source: VM122 /opt + obsidian-infra + LAPTOP
Hub: CT130 spec-hub 10.10.10.130
2026-06-19 17:26:41 +00:00

135 lines
7.1 KiB
Markdown

# Feature Specification: Funil de Onboarding Completo (004)
**Feature Branch**: `004-onboard-funnel-events`
**Created**: 2026-06-08
**Status**: Draft
**Input**: Completar a integração VM112 → VM122 com todos os marcos do wizard de onboarding, timeline por sessão e widget de funil no Dashboard Ops — continuando a feature 001 (Phase D).
**Depends on**: `001-webhook-vm112-integration` (MVP `account.created` entregue)
## User Scenarios & Testing *(mandatory)*
### User Story 1 — Visibilidade do funil activo (Priority: P1)
A equipa ops vê no Dashboard quantos onboardings estão em cada fase (domínio validado, DNS aplicado, conta criada, concluído) sem abrir o portal VM112.
**Why this priority**: Responde directamente à expectativa de um painel com informação útil — mostra onde clientes travam.
**Independent Test**: Emitir eventos simulados para 3 sessões em fases diferentes e confirmar contadores correctos no widget funil.
**Acceptance Scenarios**:
1. **Given** 2 sessões com `domain.validated` e 1 com `onboarding.completed`, **When** o dashboard carrega, **Then** o widget mostra contagens por fase actual.
2. **Given** sessão sem eventos há > 24h, **When** listada no funil, **Then** aparece marcada como `stale` (inactiva).
---
### User Story 2 — Timeline por sessão no ticket (Priority: P1)
Ao abrir um ticket de onboarding, a equipa vê a sequência cronológica de eventos da mesma `session_id` (validação, DNS, conta, infra, conclusão).
**Why this priority**: Diagnóstico rápido quando onboarding falha a meio — sem cruzar logs manualmente.
**Independent Test**: Enviar 5 eventos com mesmo `session_id` e confirmar timeline ordenada no detalhe do ticket.
**Acceptance Scenarios**:
1. **Given** ticket criado por `account.created`, **When** chegam `domain.validated` e `dns.applied` com mesma sessão, **Then** aparecem na timeline do ticket (notas/eventos).
2. **Given** `onboarding.completed` para sessão existente, **When** processado, **Then** actualiza ticket (nota ou tag `ready`) sem criar ticket duplicado.
---
### User Story 3 — Portal emite marcos do wizard (Priority: P1)
O portal VM112 emite webhooks para cada marco importante do funil, de forma não-bloqueante (mesmo padrão `ops_webhook.py`).
**Why this priority**: Sem emissor completo, o Ops não tem dados para funil nem timeline.
**Independent Test**: Percorrer wizard em staging e confirmar eventos no registo Ops por ordem.
**Acceptance Scenarios**:
1. **Given** `POST /validate-domain` com sucesso, **When** resposta 200, **Then** emite `domain.validated`.
2. **Given** `POST /dns/cloudflare/apply` com registos aplicados, **When** sucesso, **Then** emite `dns.applied`.
3. **Given** provision infra concluído após criar conta, **When** sucesso, **Then** emite `infra.synced`.
4. **Given** fluxo completo sem erro, **When** resposta final ao cliente, **Then** emite `onboarding.completed`.
5. **Given** falha crítica (ex. CarbonioError em create account), **When** HTTP 400, **Then** emite `onboarding.failed` com motivo em `data.error`.
---
### User Story 4 — Sessão iniciada (Priority: P2)
Quando o cliente escolhe/valida domínio pela primeira vez na sessão, o Ops regista `onboarding.started` para contagem de funil desde o início.
**Why this priority**: Permite taxa de conversão início → conclusão; não bloqueia MVP do funil.
**Independent Test**: Primeira validação de domínio numa sessão nova gera evento `onboarding.started` uma única vez.
**Acceptance Scenarios**:
1. **Given** nova `session_id`, **When** primeiro `validate-domain` OK, **Then** emite `onboarding.started` (idempotente — não repete na mesma sessão).
---
### Edge Cases
- Eventos fora de ordem (ex. `onboarding.completed` antes de `dns.applied`): Ops aceita e ordena por timestamp na timeline.
- Mesmo evento repetido (retry portal): idempotência `event + session_id + domain` — sem duplicar timeline.
- Sessão sem `session_id`: evento aceite, funil agrupa em `unknown`.
- Domínio com múltiplas sessões: funil mostra sessões separadas; tickets ligados por `session_id`.
- Ops offline: portal continua; activity log regista falha webhook.
## Requirements *(mandatory)*
### Functional Requirements
- **FR-001**: Portal DEVE emitir: `onboarding.started`, `domain.validated`, `dns.applied`, `infra.synced`, `onboarding.completed`, `onboarding.failed` via `ops_webhook.emit_event`.
- **FR-002**: Emissão DEVE ser non-blocking (`BackgroundTasks` ou equivalente) — nunca alterar resposta HTTP ao cliente.
- **FR-003**: Ops DEVE persistir todos os eventos em `webhook_events` (já existente).
- **FR-004**: Ops DEVE expor `GET /api/v1/onboard/funnel` com contagens por fase e lista de sessões activas (últimas 48h).
- **FR-005**: Ops DEVE expor `GET /api/v1/onboard/sessions/{session_id}/timeline` com eventos ordenados.
- **FR-006**: Ticket existente (mesma `session_id`) DEVE enriquecer-se com novos eventos — notas internas ou campo `timeline` no detalhe.
- **FR-007**: Apenas `account.created` e `onboarding.failed` criam ticket novo; restantes eventos actualizam contexto.
- **FR-008**: Idempotência OBRIGATÓRIA: `(event_type, session_id, domain)` — já implementada em 001, manter.
- **FR-009**: UI Dashboard DEVE incluir widget **Funil Onboarding** (contadores + link sessões).
- **FR-010**: UI Ticket DEVE mostrar timeline de eventos da sessão.
- **FR-011**: Comunicação LAN-only (`10.10.10.112` → `10.10.10.122`).
### Key Entities
- **Funnel Session**: `session_id`, `domain`, `current_stage`, `last_event_at`, `events[]`.
- **Funnel Stage**: ordered enum — `started``domain_validated``dns_applied``account_created``infra_synced``completed` | `failed`.
- **Timeline Entry**: event_type, timestamp, data snapshot, source `vm112-onboard`.
## Success Criteria *(mandatory)*
- **SC-001**: 100% dos marcos do wizard (6 tipos) geram evento Ops quando portal e Ops disponíveis.
- **SC-002**: Equipa identifica fase actual de um onboarding em < 10s via Dashboard (sem SSH no portal).
- **SC-003**: Timeline completa visível no ticket para qualquer sessão com 2 eventos.
- **SC-004**: Zero tickets duplicados por sessão além de `account.created` + opcional `onboarding.failed`.
- **SC-005**: Falha Ops não afecta taxa de sucesso do wizard (0 regressões em testes E2E portal).
## Assumptions
- Receptor `/api/v1/webhooks/onboard` e `ops_webhook.py` funcionais (001).
- `session_id` disponível via `bind_onboarding_session` em todos os routers de onboarding.
- Fases do funil mapeiam 1:1 aos passos actuais do wizard ibytera-mail-portal.
- `onboarding.completed` dispara no fim de `create_account` após infra (mesmo request).
- UI Desk v2 existente extensão de dashboard e ticket detail, não rebuild.
## Dependencies
- Constitution v1.0.0 (separação VM112/122, LAN-only).
- Feature 001 deployada em VM112 + VM122.
- Feature 002 não conflita (origens diferentes).
## Out of Scope
- Notificações push (ntfy) em mudança de fase.
- Auth/RBAC no Desk (feature 003 futura).
- Sincronização Ops Portal.
- Expor webhook onboard na internet pública.
- Kanban / SLA (feature desk futura).