# P15 — Publicações, atos oficiais e arquivo público

| Campo | Valor |
|---|---|
| Programa | F7+ — operação, estrutura e conteúdo |
| Status | `planejado` |
| Depende de | P14, P9 |
| Habilita | P16, P17, P18, P19 |
| Tamanho | **G** (5–7 sessões) |
| ADRs | ADR-053 (PDF pesquisável), ADR-012, ADR-017, ADR-018, ADR-026, ADR-027, ADR-028, ADR-033, ADR-035, ADR-042 |

## 1. Objetivo

Implementar o **repositório documental** do município com donos distintos — Publicações
(editorial), Diário Oficial (edição oficial), Carta de Serviços (catálogo) e, se houver demanda,
Licitações Antigas e Arquivo — sem duplicar dataset, anexo ou PDF e sem ETL de legado.

**Resultado observável:** o cidadão encontra um edital, uma edição do Diário Oficial e a carta de
um serviço; o operador publica com anexo PDF, categoria e data; tudo entra na busca e no sitemap; o
anexo é servido same-origin pelo Media, nunca por URL S3 exposta.

## 2. Escopo fechado

### S1 — Publicações

- Módulo `Publicacoes/Publicacoes` (token `publicacoes`, grupo `Publicacoes`, permissions
  `[VIEW, EDIT, APPROVE, ARCHIVE]`).
- Tabelas:
  - `publicacoes`: `id`, `slug` único case-insensitive, `titulo` jsonb, `resumo` text,
    `conteudo` AST jsonb, `categoria_id` FK, `publicado_em` timestamptz, `destaque` bool,
    `arquivado_em` timestamptz null, `metadata` jsonb, timestamps.
  - `publicacao_categorias`: `id`, `slug` único, `nome` jsonb, `position`, `active`.
  - `publicacao_anexos`: `id`, `publicacao_id` FK cascade, `media_id`, `titulo` jsonb, `position`,
    `publico` bool.
- Portal `/publicacoes` (filtros por categoria/ano/texto, paginação server-side) e
  `/publicacoes/{slug}`; CMS CRUD + publicar/despublicar/arquivar; anexos via Media (magic bytes,
  PDF prioritário); busca/sitemap.
- Regras: slug determinístico; arquivar não apaga anexo; publicar exige título/conteúdo; datas com
  `LocalizedDate`; 4 locales.

### S2 — Diário Oficial

- Módulo `Publicacoes/DiarioOficial` (token `diario_oficial`), separado de Publicações (C13).
- Tabela `diario_edicoes`: `id`, `data` (date), `numero`, `ano`, `extra` bool, `titulo` jsonb,
  `resumo` text, `conteudo` jsonb (AST/matérias, nullable), `pdf_media_id` (Media; nullable quando
  gerado), `publicado_em`, `arquivado_em`, timestamps; `UNIQUE (numero, ano, extra)`.
- **D20:** a edição pode ser **gerada pelo sistema** a partir do `conteudo`/matérias (PDF com camada
  de texto real); nesse caso a busca indexa o texto estruturado e o PDF é pesquisável. Se for PDF
  anexado externo, **não há OCR**: Media atesta `pdf_pesquisavel` e o CMS avisa.
- Portal `/diario-oficial` (lista por ano/mês, busca) e `/diario-oficial/{ano}/{numero}` (ou slug
  estável); PDF via `media.stream` com `Content-Disposition: inline`/`attachment` conforme o tipo;
  CMS upload/publicação/geração; sitemap; busca indexa título/resumo e, quando gerado, o texto.
- Sem extração de texto/OCR de PDF externo no MVP; se necessário, conversor entra em P9 (RichText)
  com justificativa.
- Regras: edição publicada é imutável (correção = nova edição/errata); arquivamento reversível;
  `extra` não colide com a edição normal.

### S3 — Carta de Serviços

- Módulo `Servicos/CartaServicos` (token `carta_servicos`).
- Tabelas `servicos_publicos` + `servico_categorias`: `nome` jsonb, `slug`, `descricao` AST,
  `requisitos` (lista), `etapas` (lista ordenada), `prazo`, `custo` (string), `responsavel`,
  `canal_online` (URL validada), `digital` bool, `form_schema` jsonb (campos fechados quando a
  solicitação é interna), `publico_alvo`, `palavras_chave`, `position`, `active`.
- **PNTP 15.3:** `digital=true` exige uma solicitação/acompanhamento interno (P25
  `Servicos/Solicitacoes`) ou um canal oficial real; link “em breve” não conta. Pelo menos um
  serviço digital real no demo.
- Portal `/carta-de-servicos` (A–Z, busca, filtro por categoria/público) e detalhe; CMS CRUD;
  busca/sitemap; links para Unidades de Atendimento (P14) por id/slug.
- Regras: prazo/custo como texto estruturado (não HTML); etapas em lista ordenada; nenhum iframe de
  serviço externo (link).
- 4 locales.

### S4 — Licitações Antigas (arquivo curado)

- `PODE` — só se houver acervo real e contrato. Não é ETL (ADR-042).
- Módulo `Publicacoes/LicitacoesAntigas` (token `licitacoes_antigas`), **read-only** no portal e no
  CMS (somente importação/curadoria administrativa).
- Tabela `licitacoes_antigas` com os campos públicos mínimos (numero, ano, objeto, modalidade,
  data, situação, anexos via Media); sem edição editorial no CMS; carga por CSV do importador (P9)
  com dry-run e relatório, ou digitação manual.
- Portal lista/detalhe; `show_value` se aplicável; sitemap; busca.
- Se não houver acervo, **não criar** o módulo (decisão no `kd`).

### S5 — Arquivo público (biblioteca de documentos)

- `PODE` — só com demanda real; `Drive` interno já é Media/Gallery.
- Módulo `Portal/Arquivo` (token `arquivo`): `arquivo_categorias`, `arquivo_documentos`
  (`media_id`, `titulo`, `descricao`, `categoria_id`, `publico` bool, `data`, `retention_until`).
- Portal busca/filtros por categoria/ano; CMS upload; download same-origin; documento privado só no
  CMS; retenção/expurgo via `RetentionRegistry`.
- Regras: não duplicar Publicações/Diário; se o documento tem dono editorial, ele pertence ao dono.
- Se não houver demanda, **não criar** (decisão no `kd`).

### S6 — Mais Conteúdos (coleções editoriais) — condicional

- `PODE` — só se o cliente precisar de coleções fora da árvore padrão.
- Implementação mínima: `colecoes` + `colecao_paginas` + `colecao_publicacoes` (duas pivots
  explícitas, sem polimorfismo solto), ordem e título; portal listagem; CMS.
- Não criar um motor genérico de conteúdo nem um “CMS dentro do CMS”.
- Sem demanda, fica fora da fase.

### S7 — Integração e fechamento

- Cada módulo registra `SearchSource` (P9) e `SitemapSource`; links internos com locale.
- Cross-links: Publicação ↔ Diário Oficial (edição), Carta ↔ Unidades (P14), Licitações Antigas ↔
  Licitações (P8) quando o mesmo processo existir (sem duplicar dados).
- E2E: publicar cada tipo, anexo PDF, busca, sitemap, 4 locales, axe.
- `make check` 2× + `E2E=1`; cobertura ≥90%; PHPStan max 0.

## 3. Fora de escopo

- ETL de publicações/PDFs do legado (ADR-042).
- OCR/extração de texto de PDF; conversão DOCX (P9, se demandado).
- Assinatura digital de PDF (P13).
- Diário Oficial como jornal completo com múltiplas matérias por edição — começar pela edição + PDF;
  matérias separadas só com demanda.
- Motor genérico de coleções (Mais Conteúdos).

## 4. Fatias de sessão

| Sessão | Entrega | Prova | Depende |
|---|---|---|---|
| **P15.S1** | Publicações + categorias + anexos | portal/CMS; PDF same-origin; busca/sitemap | P14/P9 |
| **P15.S2** | Diário Oficial | número/ano/extra único; PDF gerado pesquisável; externo atestado; imutável; sitemap | S1 |
| **P15.S3** | Carta de Serviços | A–Z; etapas/requisitos; link unidades; busca | S1 |
| **P15.S4** | Licitações Antigas (se aprovado) | CSV dry-run; read-only; sem duplicar P8 | S1 |
| **P15.S5** | Arquivo público (se aprovado) | Media; privado; retenção; busca | S1 |
| **P15.S6** | Mais Conteúdos (se aprovado) | coleções explícitas; sem polimorfismo solto | S1 |
| **P15.S7** | E2E, docs, 2× check | evidência no `kd` | S1–S6 |

## 5. DoD

- [ ] Publicações, Diário Oficial e Carta de Serviços funcionam ponta a ponta (portal/CMS).
- [ ] Anexos sempre via Media; PDF same-origin; nenhuma URL S3 exposta.
- [ ] Diário gerado pelo sistema tem texto pesquisável; anexo externo é atestado sem OCR.
- [ ] Um dono por domínio; sem duplicar Publicações × Diário × Arquivo.
- [ ] Busca/sitemap integrados; 4 locales; axe zero A/AA.
- [ ] Módulos `PODE` só entram com decisão registrada; sem ETL.
- [ ] `make check` 2× + `E2E=1`; cobertura ≥90%; PHPStan max 0.
- [ ] PNTP 14.3/15.3: Carta com requisitos/etapas/prazos; ao menos um serviço digital real.
- [ ] PNTP 2.6: atos normativos via P10; P15 não duplica o repositório.

## Fechamento — ficha de revisão

> **Obrigatório antes de fechar:** criar `_to_revision/P15-publicacoes-arquivo.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.

## 6. Riscos

| Risco | Mitigação |
|---|---|
| Domínios se sobrepõem | dono definido (Publicações/Diário/Arquivo); teste de não duplicação |
| PDF servido de URL S3 | `media.stream` same-origin; teste de rota |
| Edição do DO alterada | imutável após publicar; errata como nova edição |
| Anexo grande/consome storage | limites do Media; retenção; auditoria |
| Busca indexar PDF externo | gerado indexa texto estruturado; anexo externo só metadados/atestado, sem OCR |
| Licitações antigas virarem ETL | carga curada/CSV com dry-run; ADR-042 |

## 7. Retomada

```bash
kd prime
kd ask "publicações diário oficial carta de serviços licitações antigas arquivo"
kd task list --ready
```

Ler: `dossie_modules/{publicacoes,diario-oficial,carta-de-servicos,licitacoes-antigas,arquivo,mais-conteudos}/**`
→ `MODULOS.md` §1/P15 → P14/P9 → `src/services/Media/**` → `src/services/Seo/**`.
