# 33 — Fonte do dataset de dados abertos no CMS

**Fase:** P5.5.S6.1 · **Área:** CMS (domínio do painel) · **Spec:** `tests/33-cms-dados-abertos-fonte.spec.ts`

## História

A redação abre **Dados Abertos → Novo conjunto** no painel. O campo **Fonte de dados** lista o
catálogo **publicável** do motor de dados abertos (ADR-049): só fonte de módulo ativo e com PII
resolvida (base legal + allowlist) aparece — a mesma lista que o export público usa. Ao escolher
uma fonte, o dataset passa a ser **gerado**: as linhas vêm da fonte e o formulário não guarda
colunas/linhas próprias (sem upload de CSV). Sem fonte, o dataset continua **curado** pelas áreas
de texto de colunas/linhas — o caminho sem JavaScript.

Quando **nenhum** módulo publica dados (estado antes do P6), o formulário explica isso no próprio
campo e mantém o dataset curado disponível; não há opção “fantasma” nem erro silencioso. Uma fonte
que saiu do catálogo entre a abertura e o envio do formulário (módulo desativado, PII bloqueada) é
recusada pela validação de servidor com mensagem que diz o que fazer, resumo `role="alert"` e foco
no campo.

## Critérios de aceite

1. A lista do painel e o formulário de criação passam no axe A/AA (WCAG 2.2) sem violações.
2. **Com fonte registrada** (o `P6.S6` registra `noticias.publicadas`): o select oferece a fonte; o
   dataset criado pela UI é salvo e, na edição, o aviso de linhas geradas aparece e as áreas de
   colunas/linhas **não** existem (`source` é a única verdade) — sem cópia de linhas.
3. **Sem fonte registrada** (estado atual): o select tem apenas “Nenhuma (dataset curado)”, o CMS
   exibe o aviso de que nenhum módulo publica dados agora e as áreas de colunas/linhas continuam
   visíveis e utilizáveis.
4. **Sem JavaScript**, o dataset curado é criado pelo formulário (`columns_text`/`rows_text`,
   células separadas por `|`) e a edição preserva colunas e linhas gravadas.
5. Uma fonte fora do catálogo vivo enviada ao servidor é recusada (nada é gravado), o resumo
   `role="alert"` (`data-form-errors`) aparece, o select recebe `aria-invalid="true"` e a mensagem
   é associada por `aria-describedby`; com JS o foco vai para o campo inválido.
6. Ao final o dataset criado é **arquivado** pela UI (o banco de dev não acumula `e2e-*`).

## Dados e isolamento

- Título e slug carregam projeto + timestamp; o dataset nasce **rascunho** (não aparece no
  catálogo público enquanto a spec roda em paralelo com a história 12) e é marcado como criado
  logo após o clique de salvar, para o `finally` arquivá-lo mesmo se a asserção do flash falhar.
- Nenhum estado global (tema, home, módulos) é mutado — o arquivo roda no projeto `cms-desktop`,
  em paralelo com as demais specs, e não precisa do lote `cms-serial`.
- O ramo “com fonte” é **condicional de propósito**: hoje o registry de produção nasce vazio
  (as fontes existentes são fixtures de `Test/`); a partir do `P6.S6` o mesmo teste percorre o
  caminho gerado sem alteração — preferimos o ramo declarado ao `test.skip`, que esconderia a
  cobertura quando a fonte chegar. O valor padrão é `noticias.publicadas` quando presente.

## Fora de escopo

- Export público do dataset gerado (CSV/JSON/XML, `ETag`): história 12 + testes de contrato do
  `OpenData`.
- Criar/editar `source_options` (ainda sem UI) e o snapshot/materialização da fonte (P12/P22).
- Reordenar/ocultar o campo por JS: o formulário é server-rendered e o caminho sem JS é o contrato.
