# Formulários Dinâmicos — Gaps, Dívidas e Riscos

> **Slug:** `infraestrutura-formulariosdinamicos` · **Versão analisada:** `lotep`
> **Raiz de referência:** `/home/one/projects/maxima/v7/lotep`
>
> Este arquivo cobre **dois caminhos**: o motor vivo e o módulo legado morto.

---

## 1. Bugs e erros conhecidos

| ID | Severidade | Descrição | Onde | Impacto |
|---|---|---|---|---|
| FDD-01 | **baixa** | Migration de submissões legadas tem espaçamento degenerado (`Blueprint$t`, `\Exception$e`, `throw$e`) — **sintaxe válida**, confirmada com `php -l` | `V2/Modules/Infraestrutura/FormulariosDinamicos/Migrations/2026_07_17_000004_create_v2_form_submissions_table.php:17,29,31,44,46` | Nenhum impacto de execução; sinal de edição fora do padrão. Reformatar antes de portar |
| FDD-02 | **alta** | `result_token` é UUID v4 sem assinatura/expiração, apesar do comentário "URL assinada" | `SubmissionService.php:103`; `V2/Services/FormularioDinamico/Migrations/2026_09_28_000004_create_v2_formulario_dinamico_submissoes_table.php:27` | Token é credencial perpétua; vazamento em log/referer permite abrir a confirmação de outra pessoa |
| FDD-03 | **alta** | `distribution` e `questionScores` carregam **todas** as `answers` do formulário na memória e percorrem em PHP | `ExportService.php:102-106, 188-232` | Relatório degrada O(N·M) e pode estourar `memory_limit` em formulários populares |
| FDD-04 | **alta** | `consent_text` é impresso sem escape (`{!! $consentLabel !!}`) e o default contém HTML | `FormularioDinamicoPortalController.php:158-164`; `portal/form.blade.php:80` | XSS armazenado por quem tem `write` no CMS; não há sanitizador |
| FDD-05 | **média** | O `submit` legado ainda grava IP cru (a migration correspondente é válida) | `FormPortalController.php:28-32` | Violação de LGPD se a rota for alcançada; inconsistente com o motor |
| FDD-06 | **média** | Nenhum leitor usa o índice GIN `jsonb_path_ops` | `V2/Services/FormularioDinamico/Migrations/2026_09_28_000004_create_v2_formulario_dinamico_submissoes_table.php:41` | Custo de escrita sem ganho; falsa sensação de performance |
| FDD-07 | **média** | IDs de erro duplicados em campos com múltiplas mensagens | `field-text.blade.php`, `field-textarea.blade.php`, `field-select.blade.php`, etc. | `aria-describedby` aponta para o primeiro erro; leitor de tela pode ignorar os demais |
| FDD-08 | **média** | `aria-labelledby="df-{name}-legend"` aponta para `<legend>` sem `id` | `field-satisfaction.blade.php:20` | Grupo de satisfação fica sem nome acessível em alguns leitores |
| FDD-09 | **média** | `questionScores` conta o denominador como **todas** as submissões, inclusive as que não responderam | `ExportService.php:188-232` | Média por pergunta é diluída por não-respostas; não é a média "entre quem respondeu" |
| FDD-10 | **média** | Teste do módulo legado espera views que o controller não renderiza mais | `FormularioCmsControllerTest.php:98-147` | Suíte com falha crônica; esconde regressões reais |
| FDD-11 | **baixa** | `<div>` extra no bloco de filtros de submissões | `submissoes.blade.php:31-45` | HTML desbalanceado |
| FDD-12 | **baixa** | `weight` da pergunta não é editável no CMS | `_campos-editor.blade.php` | Formulários criados pela UI só pontuam pelo peso das opções |
| FDD-13 | **baixa** | `published_at` da versão nunca é escrito | `FormularioCrudService.php:53-56, 176-185` | Coluna morta |

## 2. Funcionalidades incompletas ou mortas

| Item | Evidência de que está morto | Consequência |
|---|---|---|
| Telas do módulo legado | `FormCmsController@index/create/edit` redirecionam; namespace `v2-formularios::` só existe em teste | Nenhum operador usa o builder antigo |
| `V2FormCategory` | Nenhum controller/rota usa categorias | Tabela e model órfãos |
| `V2FormQuestion` | Só o seeder legado cria; nenhuma tela lê | Dados sem manutenção |
| `FormDefaultSeeder` | Não é chamado por `DatabaseSeeder` nem por teste | Nunca roda em produção |
| Tipo `file` | Classe existe, config não registra, editor não oferece, teste afirma remoção | `fieldType('file')` lança exceção |
| `schema` jsonb | Só recebe `{"campos": N}`; nenhum leitor | Estrutura documentada no comentário não existe |
| `expires_at` | Sem campo no CMS; só lido no `scopeActive` | Recurso existe, ninguém configura |
| GIN de `answers` | Nenhuma query usa `@>`/`whereJsonContains` | Índice inútil |
| `FormularioCmsControllerTest` | Espera comportamento anterior ao redirecionamento | Teste morto |
| Stubs de view legados | Só usados pelo teste morto | Manutenção inútil |

## 3. Itens divergentes entre versões

### 3.1 Motor × módulo legado

| Aspecto | Módulo legado (`v2_forms`) | Motor (`v2_formulario_dinamico_*`) | Equivalente? |
|---|---|---|---|
| Estrutura | formulário + perguntas + categorias | formulário + versões + campos | não: versão é nova |
| Resultado | `has_result` + `result_config` | `scoring_config.bands` por versão | parcial: importador copia `result_config` |
| Submissão | `answers`, `ip` cru | `answers`, `ip_hash`, `result_token`, `consent_at` | não: LGPD |
| Portal | redireciona | renderiza | sim, para o usuário |
| CMS | redireciona | CRUD completo | sim, para o usuário |
| Permissão | `formularios` | `formulario-dinamico` | sim: mesmo alias canônico |
| Rotas CMS | `/cms/v2/formularios` | `/cms/v2/formulario-dinamico` | não |
| Seed | `FormDefaultSeeder` | nenhum | não |

### 3.2 V1 (`questionnaires`) × motor V2

| Aspecto | V1 | V2 | Equivalente? |
|---|---|---|---|
| Tabelas de origem | `questionnaires`, `asks`, `ask_types`, `filled_questionnaires` | `v2_formulario_dinamico_*` | — |
| Status | booleano | `draft/active/archived` | `true → active`, `false → draft` |
| Tipos | `TEXT`, `TEXTAREA`, `SELECT`, `MULTI_SELECT`, `RADIO`, `CHECKBOX`, `RANGE`, `SATISFACTION_LEVEL`, `FILE`, `MULTI_FILE` | 8 tipos registrados | arquivos viram `text` (perda declarada) |
| Idempotência | — | `v1_id` + unique parcial | sim |
| IP | cru | só `ip_hash` existente na origem | sim, por decisão LGPD |
| Config de resultado | `result_config` jsonb | `scoring_config` | copiado como está |

O importador (`V2/Modules/Support/MigracaoLegada/Services/FormulariosDinamicosLegacyImporter.php`)
preserva `identifier`, `has_result` e `result_config` em `metadata.legado` e o tipo original em
`rules.legado.ask_type` quando há perda.

### 3.3 Removido na versão atual

| Item | Impacto | Resgate necessário? |
|---|---|---|
| Categorias de formulário | organização interna | não; nenhum dado em uso |
| Tela de builder antiga | operador precisa usar o motor | não |
| Tipo `file` | upload de anexo em formulário | sim, se houver caso de uso — via Media no V7 |

## 4. Riscos de dados na migração

| Risco | Tabela afetada | Como verificar antes de migrar | Plano de rollback |
|---|---|---|---|
| `answers` com PII (nome, e-mail, telefone) | `v2_formulario_dinamico_submissoes` | amostrar `jsonb_object_keys(answers)` e comparar com labels dos campos | anonimizar valores e manter agregados; backup da tabela |
| `scoring_config` em formatos mistos (`max_percent` e `max_points`) | `..._versoes` | contar faixas por estratégia | normalizar por versão, sem reescrever diagnóstico histórico |
| `weight` tudo zero em formulários criados pela UI | `..._campos` | `SELECT count(*) FROM campos WHERE weight = 0` | recalcular a partir dos pesos das opções |
| Slugs duplicados após importação V1 | `..._formularios` | `GROUP BY slug HAVING count(*) > 1` | aplicar sufixo e registrar redirecionamento |
| `result_token` compartilhado/duplicado | `..._submissoes` | unique já impede; conferir nulos | gerar novos tokens na migração |
| Submissões legadas em `v2_form_submissions` | tabela legada | contar linhas e verificar existência da tabela | não migrar; arquivar e registrar |

## 5. Dívida técnica

| Item | Tipo | Esforço | Risco de não corrigir |
|---|---|---|---|
| Relatórios sem SQL agregado | performance | médio | timeout/memória em formulário grande |
| CSV em memória | performance | baixo | pico de memória na exportação |
| Renderer por string | arquitetura | médio | escape inconsistente, dificuldade de compor componentes |
| i18n ausente | produto | médio | portal não multilíngue; strings espalhadas |
| `schema` stub | modelagem | baixo | confusão de contrato |
| Dois caminhos (serviço + módulo) | arquitetura | baixo (remoção) | confusão de permissões e rotas |
| Dependência de AutoTeste nos dados | acoplamento | baixo | fixture quebra a cada mudança |
| Migrations legadas com formatação degenerada | qualidade | baixo | revisão manual; não falha `migrate` |
| `weight` não editável | UX | baixo | instrumento não calibrado pela UI |

## 6. Riscos de segurança

| Risco | Explorável por | Mitigação |
|---|---|---|
| XSS no `consent_text` | usuário com `write` no CMS | escapar texto e permitir link por estrutura (`consent_url` separado), nunca HTML |
| `result_token` perpétuo | quem obtém o token | URL assinada com validade; invalidar em novo resultado |
| CSV injection | operador que abre o CSV | prefixar células que começam com `= + - @` e documentar |
| IP cru no fluxo legado | requisição direta a `/formulario/{slug}/submit` | remover a rota/ação na migração |
| Enumeração de slugs | público | slugs são públicos por design; avaliar rate limit no 404 |
| Formulário como vetor de spam | público | throttle por IP (10/min) + honeypot/time-trap no V7 |
| Substituição de chave `APP_KEY` | operação | documentar que `ip_hash` deixa de ser comparável; usar pepper versionado no V7 |

## 7. Perguntas em aberto

| # | Pergunta | Quem decide | Impacto |
|---|---|---|---|
| 1 | Formulários podem coletar dado pessoal? | DPO / negócio | define cifra, retenção e se `answers` entra no `retention:expunge` |
| 2 | O portal deve permitir rascunho por sessão (salvar e continuar)? | produto | muda modelo de submissão |
| 3 | Haverá upload de arquivo em formulário? | produto | ativa Media/ADR-026 e o tipo `arquivo` |
| 4 | Relatórios precisam ser por versão ou consolidados? | operação | muda a agregação e a tela |
| 5 | Quem pode exportar CSV: `ver` ou `aprovar`? | segurança | define permissão e auditoria de acesso |
| 6 | O link de resultado deve expirar em quantos dias? | DPO | define `expira_em` e retenção |
| 7 | Formulários legados (`v2_forms`/`v2_form_submissions`) têm dados a preservar? | negócio | define se há importação ou descarte |
| 8 | O AutoTeste continua consumindo o motor ou vira módulo próprio? | produto | define acoplamento e fixture de testes |
