# P3 — Área de suporte, ativação de módulos e operação

| Campo | Valor |
|---|---|
| Programa | F7+ — operação, estrutura e conteúdo |
| Status | `revisado` |
| Depende de | P0, P1, P2 |
| Habilita | P4, P13 |
| Tamanho | **G** (4–6 sessões) |
| ADRs | ADR-033, ADR-034, ADR-035, ADR-037, ADR-043, ADR-022/032; emenda à ADR-034 (essenciais) |

## 1. Objetivo

Entregar a **área de suporte** que o dono nomeou: um painel restrito à equipe (`Suporte`),
invisível e 404 para todos os outros, com desativação de módulos em runtime, diagnóstico do
sistema, leitor da trilha de auditoria, modo de manutenção e contato do fornecedor.

**Resultado observável:** a equipe desativa `Noticias` pelo painel, o portal e o CMS respondem 404
para as rotas do módulo no request seguinte, a ação fica auditada com motivo e o módulo não aparece
na navegação; nenhum módulo essencial pode ser desativado.

## 2. Estado atual (reusar)

- `ModuleActivation` (`src/core/Internal/ModuleDiscovery/ModuleActivation.php`): Valkey/TTL + DB,
  fail-open, `setActive()` invalida a chave.
- `CheckModuleActivation` (alias `module.activation`): 404 em portal/CMS/API.
- `core:module:activate {name} [--disable]`: grava o dado e invalida o cache.
- `ModuleDiscoveryService::grouped()`/`config()`: manifesto com grupo, rota, permissões.
- `EnsureSupportGroupAccess`: rotas de `Modules\Suporte\...` → 404 para não-equipe.
- `AccessGate`: grupo `Suporte` só para `SupportAccess::isSupport()`; `Gate::before` dá passe livre
  à equipe.
- `core_audit_logs` + `AuditWriter` + `audit.access` (middleware de leitura sensível).
- **Não existe:** UI, tela de módulos, diagnóstico, leitor de auditoria, manutenção, contato.

## 3. Escopo fechado

### S1 — Módulo `Suporte/Painel` e superfície restrita

- `src/modules/Suporte/Painel/ModuleProvider.php` (grupo `Suporte`, token `painel`,
  permissions `[VIEW, EDIT]`, rota `painel.cms.index`, `position` baixa). O token canônico é o da
  pasta (`painel`, ADR-004) e a URI continua em `/suporte` no domínio do painel — o plano original
  dizia `suporte.cms.*`; a divergência está registrada no `kd` (`decision_0072xd0v`).
- Rotas CMS sem closure, todas sob `/suporte` no domínio do painel:
  `GET /` (visão geral), `GET /modulos`, `POST /modulos/{module}/ativar`,
  `POST /modulos/{module}/desativar`, `GET /diagnostico`, `GET /auditoria`,
  `GET /auditoria/{log}`, `GET /manutencao`, `PUT /manutencao`, `GET /contato`.
- Nenhuma rota do grupo pode ser acessível a não-equipe: `EnsureSupportGroupAccess` cobre pelo
  namespace; teste com perfil máximo e sem `suporte`.
- Navegação: o grupo `Suporte` não aparece no `PanelMenu` para não-equipe; para a equipe, aparece
  com os itens. Nunca link morto; rota inexistente é omitida.
- 4 locales; telas com `<x-layout::cms>` e só o `<main>` (contrato de layout).

### S2 — Ativação/desativação de módulos com proteção

- Extrair a regra do comando para uma Action única `SetModuleActivation` (Core), usada pela UI e
  pelo `core:module:activate`; o comando não pode ter uma segunda implementação.
- Lista do manifesto: grupo, título traduzido, rota, permissões declaradas, estado atual. Ordenação
  determinística (grupo → position → nome).
- **Essenciais:** lista estática em `config/core.php` (`essential_modules`), no mínimo `Acesso`,
  `Configuracoes`; **todo o grupo `Suporte`** é exceção dura e não entra em toggle.
- **Suporte intocável (D3):** nenhum módulo de `src/modules/Suporte/{Modulo}` pode ser desativado,
  por UI, comando ou `--force`. A UI não oferece o toggle; a Action `SetModuleActivation` recusa;
  o comando falha com mensagem. O grupo continua invisível/404 para todos, exceto
  `suporte@maxima.inf.br` (ADR-033).
- **Demais essenciais:** a UI recusa; `--force` existe **apenas no CLI de suporte**, é auditado e
  nunca é oferecido pela UI. Nenhum caminho de UI concede `--force`.
- Toggle: `POST` + CSRF + confirmação nativa; campo **motivo** obrigatório (`max:255`); grava
  `core_module_discovery_settings` via `ModuleActivation::setActive`; invalida cache na hora.
- Auditoria explícita `painel.module_activated` / `painel.module_deactivated` com
  `{module, active, reason, force}`; `log_event` e span com o mesmo nome (`painel.*`). Não usar o
  AuditEntry de mutação de modelo (a tabela de settings não é model auditado).
- Efeito imediato: teste de dois requests (desativa → 404 no portal/CMS; reativa → 200), sem
  `php artisan optimize` e sem reload de FPM.

### S3 — Diagnóstico do sistema

- Tela somente leitura, com cartões:
  - versão (`SystemVersion`), PHP, Laravel, PostgreSQL (`select version()`), Valkey (`PING`),
    driver de fila, cache store;
  - fila (tamanho/atraso), scheduler (última execução), storage (bucket RustFS/S3: head/health),
    disco livre, última migração, último backup (metadados, não conteúdo);
  - contagem de erros recentes? Não: log técnico é efêmero (ADR-024); mostrar apenas o que a
    trilha/health expõe.
- Cada verificação tem timeout curto e falha **exibida** (nunca 500); nada de segredo/token.
- Comando `core:diagnose` reutiliza as mesmas verificações (uma implementação, dois consumidores).
- Teste com doubles/fakes para cada check; nenhuma consulta real em teste unitário.

### S4 — Leitor da trilha de auditoria

- `GET /cms/suporte/auditoria`: filtros por `event`, `module`, `actor` (id/nome/e-mail), intervalo
  de datas; paginação keyset/ordenada por `created_at desc, id desc`; limite máximo por página.
- Detalhe: `before`/`after`, `changes`, `actor`, `request_id`, `trace_id`, `integrity_hash`,
  `created_at`; imutável, sem edição/exclusão.
- Toda leitura passa pela via única `audit.access` (middleware da área CMS): o request de leitura
  vira **um** evento `{module}.view` (`painel.view`) por request, sem duplicar a trilha. O
  vocabulário é o da ADR-035, não um evento literal `audit.access`.
- Permissão: apenas o grupo `Suporte` (e o perfil `suporte`, que tem passe livre). Se um dia outro
  perfil precisar, isso é uma permissão explícita — não um bypass.
- Export CSV opcional (streaming, sem PII além do que já está na trilha); se implementado, auditado.

### S5 — Modo de manutenção

- Settings `system` (ADR-046, campos tipados planos): `maintenance` (bool), `maintenance_message`
  (texto ≤ 500, exibida a todos), `maintenance_retry_after` (segundos, `0..86400`, default `3600`)
  e `maintenance_allow_cms` (default `true`). O objeto aninhado `system.maintenance` do plano
  original não existe: a evolução ficou com prefixo plano (`kd decision_00bgnfi9`).
- Middleware de manutenção: portal → 503 HTML com `Retry-After`; API → 503 RFC 7807; CMS continua
  acessível para a equipe (login/sessão); assets/health/`robots.txt`? Definir allowlist explícita
  (health, assets, login do CMS) e testar.
- Toggle na tela de suporte, com confirmação e auditoria; agendamento de janela fica fora (backlog).
- Página 503 semântica, acessível, sem dependência de JS; `X-Robots-Tag: noindex`.
- Teste: portal 503, CMS 200 para suporte, API 503 JSON, `Retry-After` correto, desligar restaura.

### S6 — Contato do fornecedor, versão e “novidades”

- Seção `support` no `SettingsSchema`: `company`, `site`, `contact_email`, `contact_phone`,
  `whatsapp`, `address`, `hours` (tipo `phone` valida E.164 e vira `tel:`/`wa.me`, nunca HTML).
  Renderização sem iframe de mapa (zero CDN/terceiro); endereço em texto + link externo explícito.
- Tela `Contato` mostra os dados; sem fornecedor configurado, estado vazio explicativo (nunca tela
  em branco, como no legado).
- “Novidades”: versão atual + link para `VERSION`/notas de release; se não houver fonte confiável,
  exibir apenas a versão. Não criar um módulo de changelog sem demanda.
- Manual: link para a documentação interna (`wiki/`) quando o pacote estiver disponível no deploy;
  caso contrário, omitir.

### S7 — Fechamento

- E2E: suporte desativa/ativa módulo; auditoria registra; painel invisível para não-equipe;
  manutenção 503/200; diagnóstico responde.
- `make check` 2× + `E2E=1`; axe desktop/mobile; teste de 404 do grupo.
- Atualizar `INVENTARIO.md` (suporte/ativação/logs) e a emenda à ADR-034.

## 4. Fora de escopo

- Sistema de tickets/chamados (o dossiê mostra que o legado tem um órfão; não é requisito).
- Acesso remoto, execução de comando arbitrário, shell no painel.
- Edição/exclusão de auditoria (imutável por ADR-035).
- Métricas e dashboard analítico (**P4**); aqui só diagnóstico operacional.
- Multi-tenant/regional.

## 5. Fatias de sessão

| Sessão | Entrega | Prova | Depende |
|---|---|---|---|
| **P3.S1** | módulo `Suporte/Painel` + rotas + menu + 404 | grupo invisível; perfil máximo recebe 404; 4 locales | P2 |
| **P3.S2** | Action `SetModuleActivation` + UI + essenciais + auditoria | toggle 404/200 em 2 requests; Suporte recusado; essencial recusado; motivo auditado | S1 |
| **P3.S3** | diagnóstico + `core:diagnose` | checks com timeout; falha exibida; sem segredo | S1 |
| **P3.S4** | leitor de auditoria + filtros + `audit.access` | leitura auditada; before/after; paginação; imutável | S1 |
| **P3.S5** | manutenção (settings + middleware + página) | portal 503, CMS 200, API 503; `Retry-After`; restaura | S1 |
| **P3.S6** | contato do fornecedor + versão/novidades | estado vazio; sem iframe; 4 locales | S1 |
| **P3.S7** | E2E, docs, 2× check | evidência no `kd` | S2–S6 |

## 6. Contratos e dados

- **Módulo:** `Suporte/Painel` (token `painel`, grupo `Suporte`; URI `/suporte` no domínio do painel).
- **Action Core:** `SetModuleActivation` (usada pela UI e pelo comando).
- **Config:** `config/core.php` → `essential_modules`; `config/diagnostics.php` (timeouts).
- **Settings:** `system` (`maintenance*`) e `support` (ADR-046).
- **Rotas:** `painel.cms.*`.
- **Permissões:** `painel.ver`, `painel.editar` (grupo `Suporte` só para a equipe).
- **Eventos:** `painel.module_activated`, `painel.module_deactivated`, `painel.maintenance_changed`,
  `painel.diagnose` (tela) e `core.diagnose` (serviço) — log + span com o mesmo nome e auditoria
  explícita nas mutações. A leitura da trilha gera `painel.view` pela via única `audit.access`.

## 7. DoD

- [x] Painel de suporte acessível só à equipe; 404 para qualquer outro perfil, inclusive máximo.
- [x] Ativação/desativação em runtime pela UI, com motivo, auditoria e invalidação imediata.
- [x] Módulos essenciais protegidos; **grupo `Suporte` nunca desativa** (nem com `--force`); demais não caem por UI.
- [x] Diagnóstico sem segredo, com timeout e falha visível; comando reutiliza a mesma regra.
- [x] Leitor de auditoria imutável, filtrado e com leitura auditada.
- [x] Manutenção com 503/`Retry-After`, CMS acessível e allowlist testada.
- [x] `make check` 2× + `E2E=1`; cobertura ≥90%; PHPStan max 0.

### Evidência de fechamento (S7 — 2026-10-25)

- **E2E** (`[cms-serial] › 23-suporte-painel.spec.ts`, 7 casos): 404 do grupo para o perfil máximo
  em `/suporte`, `/suporte/modulos`, `/suporte/auditoria` e `/suporte/manutencao`; a equipe vê o
  item e a visão geral; desativa `Blueprint` com motivo → portal 404 → reativa → 200; lista mostra
  `Acesso`/`Configuracoes` como **Essencial** e `Painel` como **Protegido**, sem toggle; diagnóstico
  com o `scheduler` **Indisponível** e axe; auditoria filtra `painel.view`, abre o detalhe com hash
  e axe; manutenção liga → portal 503 (`Retry-After`/noindex/axe) + API 503 RFC 7807 + CMS 200 →
  desliga → portal 200; contato vazio → preenchido (links `https/mailto/tel/wa.me`, sem `iframe`) →
  limpo. Ver a ficha `_to_revision/P3-suporte-ativacao.md` para os tempos e a cobertura medidos.
- **Gates (árvore final):** `make check` **2×** = `22 OK · 0 FAIL · 11 SKIP` (**110,14 s** e
  **110,66 s**), PHPUnit **1.163 testes / 19.478 asserções**, cobertura `src/` **93,43 %**
  (piso 90 %), PHPStan max 0 e `a11y` estrita OK; `make check E2E=1` = `24 OK · 0 FAIL · 9 SKIP`
  (**294,29 s**), `e2e` **125 s** com **129 passed / 130** (1 skip condicional) e `perf` 17 passed.
  Sem flaky entre as rodadas; `docs-check` verde.

## Fechamento — ficha de revisão

> **Obrigatório antes de fechar:** criar `_to_revision/P3-suporte-ativacao.md` e seguir
> `_to_revision/README.md` + `_to_revision/PROTOCOLO.md` (v2.0). A ficha não é gate de build; é a
> entrada da fila de revisão profunda.

- Cabeçalho: fase, `kd` épico/tasks/slices, âncoras (este arquivo, ADRs e código) e status
  `⬜ pendente` → `🟡 em revisão` → `✅ revisado` / `🔧 com correções`.
- Conteúdo: o que foi implementado, arquivos/superfícies a revisar, checklist D1–D14 aplicável,
  riscos e observações.
- Fechamento: preencher “Notas da revisão” com data/revisor/resultado e achados (`kd write
  --anchor`), atualizar a fila em `_to_revision/README.md` e `_to_revision/_inventario.md`.
- Achado com correção exige teste vermelho→verde; a fase só é `closed` no `kd` com a ficha
  registrada.

## 8. Riscos

| Risco | Mitigação |
|---|---|
| Desativar um módulo essencial e perder o painel | lista `essential_modules`; grupo `Suporte` recusado sempre; demais essenciais recusados na UI; `--force` só em CLI auditado |
| UI e comando divergirem | uma única Action; o comando só formata a saída |
| Cache não invalidar | `ModuleActivation::setActive` + teste de dois requests |
| Diagnóstico vazar segredo ou pendurar | campos allowlist, timeout por check, sem token/senha |
| Leitura de auditoria poluir a trilha | middleware `audit.access` registra a leitura uma vez, sem recursão |
| Manutenção bloquear o próprio CMS | allowlist de login/CMS/health/assets + teste |
| Contato com iframe de terceiro | link externo, sem embed (ADR-002) |

## 9. Retomada

```bash
kd prime
kd ask "suporte ativação módulos manutenção diagnóstico auditoria"
kd task list --ready
```

Ler: P0/P2 → `src/core/Internal/ModuleDiscovery/**` → `src/core/Internal/Audit/**` →
`src/core/Access/SupportAccess.php` → `src/core/Internal/Http/Middleware/EnsureSupportGroupAccess.php`
→ `wiki/backlog/ui-ativacao-modulos.md` → dossiê `suporte/` (página de contato; tickets são órfãos).

Referências irmãs (UX/desenho, não código): `../cms/core/Area/Super/Pages/ManageModules.php`
(lista/ativação de módulos), `../cms/core/Area/Super/Widgets/SystemInfoWidget.php` (diagnóstico),
`../lotep/V2/Modules/Support/SupportConfigs/**` (manutenção/flags). Rejeitar Filament, painel com
outro modelo de auth e flag fail-open.
