# QA de revisão — Onda 4 (gate G4)

**Data/snapshot:** 2026-10-21. Documentos da amostra e `sucesu-cms/V2/` lidos nesse
estado; nenhum arquivo de unidade, `V2/` ou índice foi alterado. Este arquivo é a única
escrita.

**Escopo coberto (leitura integral dos documentos de contrato/dados/rotas e leitura seção a seção dos anexos de UI):** `Publicacoes/Noticias/{spec,dados,rotas,servicos-e-fluxos,cms,portal}.md`;
`Publicacoes/Agenda/{spec,dados,rotas,cms,portal}.md`;
`Publicacoes/TrabalheConosco/{spec,dados,rotas,cms,portal}.md`;
`Conexoes/Associados/{spec,dados,rotas,cms,portal}.md`;
`Conexoes/ParceirosInstitucionais/{spec,dados,rotas,cms,portal}.md`;
`Conexoes/UnidadeRegional/{spec,dados,rotas,api,cms,portal}.md`;
`Institucional/{Historia,FaleConosco,AssocieSe}/{spec,rotas,cms,portal}.md` (mais
`Historia/dados.md` e `AssocieSe/dados.md`);
`Infraestrutura/{CertificadoDigital,GestaoPermissoes,MailAudit,FeedRss}/spec.md`.

**Método:** leitura integral dos documentos; confronto de cada afirmação material com
`caminho:linha` em `sucesu-cms/V2/`; leitura dos pontos de despacho fora da unidade
(`V2PermissionMiddleware`, `RegionalRestrictMiddleware`, `ModuleAccessMiddleware`,
`PageCacheMiddleware`, `PublicCacheMiddleware`, `FeedRssService`, `PublicIndexerService`,
`ActivityLoggerService`, `SeoPortalController`); conferência dos índices parciais contra
texto e código; `python3 tools/lint_docs.py --strict-citations`; `python3
tools/merge_indices.py --check`.

Ferramentas: `lint_docs.py --strict-citations` não aponta nenhum dos documentos desta
amostra (os 7 arquivos com citação fora do fim são de ondas anteriores: `01_arquitetura`,
`04_ui` e `02_servicos-base`); `merge_indices.py --check` retorna “Índices OK”.

## 1. Verificações do gate

| # | Critério | Resultado |
|---|---|---|
| 1 | Citações, trechos e contratos conferem com o código | **Ressalva.** As citações existem; os erros são semânticos (G4-01, G4-03..G4-06, G4-11, G4-13, G4-14). |
| 2 | Efeitos e estados (publicação, arquivamento, importação, token, permissões) | **Ressalva.** O estado de feed após despublicar/republicar está incorreto em Notícias e TrabalheConosco (G4-03); a invariante de slug de Associados está invertida (G4-01). |
| 3 | Contratos entre módulos e serviços (CRE, Gallery, Search, SEO, FeedRSS, Mail, RegionalIdentity, DigitalSignature) | **Ressalva.** O contrato do FeedRSS é descrito de forma contraditória entre Agenda (correta) e Notícias/TrabalheConosco (G4-03); os demais contratos conferem. |
| 4 | Índices parciais coerentes com texto/código | **Ressalva.** Rotas/tabelas/componentes conferem em contagem, exceto a contagem de rotas nacionais em `permissoes.csv` de Notícias (G4-04) e o inventário textual de TrabalheConosco (G4-02). |
| 5 | Critérios de paridade falsos/ambíguos | **Ressalva.** `PC-ASSOCIADOS-02` (G4-01), `PC-TRABALHECONOSCO-34/37` (G4-02), `PC-NOTICIAS-PORTAL-12` (G4-06) e `PC-TRABALHECONOSCO-66` (G4-13) não podem ser executados como escritos. |
| 6 | Contradições entre unidades (sitemap, tags, TTL, permissões, contagens) | **Ressalva.** FeedRSS (G4-03), slug (G4-01/G4-12), `<title>` (G4-13), flags de layout (G4-14) e contagens de rotas (G4-02/G4-07/G4-08/G4-09). |

## 2. Achados

### G4-01 — A invariante de slug de Associados está invertida na documentação

**Severidade: P2. Confiança: 0.99.**

`AssociadoInstituicao::saving` usa `$baseSlug = $model->slug ?: str_slug($model->name)`
(`V2/Modules/Conexoes/Associados/Models/AssociadoInstituicao.php:186-202`). Como o slug
já existe depois da criação, renomear a instituição **não** altera o slug; o nome só é
usado quando o campo está vazio. O documento afirma o contrário em
`05_modulos/Conexoes/Associados/spec.md:262` (“editar o nome altera a URL pública”),
`:631` (`PC-ASSOCIADOS-02` — “editar o nome altera o slug e a URL pública”) e em
`dados.md:63`, `:138`, `:254` e `:378` (“slug recalculado”/“rename muda URL”). A mesma
frase de `spec.md:262` também diz que `saving` roda antes de `creating`; a ordem real é
`creating` → `saving` (`:177-202`). O padrão correto já está documentado para Parceiros
em `05_modulos/Conexoes/ParceirosInstitucionais/spec.md:290` (RN-04: slug calculado só
quando vazio).

**Impacto:** uma reimplementação que siga o texto muda a URL pública ao renomear e
quebra links; o critério `PC-ASSOCIADOS-02` falha no legado, que preserva o slug.

**Correção por documento:** em `Associados/spec.md`, substituir a frase de `:262` por
“o slug é calculado apenas quando vazio; renomear preserva a URL; colisão recebe `-N`” e
reescrever `PC-ASSOCIADOS-02` para “renomear **não** altera o slug”; em `Associados/dados.md`,
corrigir as linhas `:63`, `:138`, `:254` e `:378` com a mesma redação.

### G4-02 — TrabalheConosco tem 13 endpoints, não 11 (10 no CMS, não 8)

**Severidade: P2. Confiança: 0.99.**

`Routes/cms.php` declara 10 endpoints: 6 de openings (`GET /openings`, `GET/POST
/openings/novo`, `GET/PUT|POST /openings/editar/{id}`, `DELETE /openings/{id}`) e 4 de
applications (`GET /applications`, `GET /applications/{id}`, `PUT|POST
/applications/{id}/status`, `DELETE /applications/{id}`) — `V2/Modules/Publicacoes/TrabalheConosco/Routes/cms.php:9-58`.
`Routes/web.php:7-20` declara 3 rotas públicas. Total: 13 endpoints (3 + 10), e não 11
(3 + 8), como afirmam `spec.md:31`, `spec.md:779`, `rotas.md:17`, `:34`, `:43`, `:121`
e os critérios `PC-TRABALHECONOSCO-34` (`rotas.md:563`) e `:566`. A própria tabela
`rotas.md` §5.2 lista os 10 endpoints CMS; o CSV parcial também tem 13 endpoints + 2
grupos (`_indices/parcial-MOD-PUBLICACOES-TRABALHECONOSCO/rotas.csv`).

**Impacto:** o gate de rotas e os testes “as 11 rotas retornam 404”/“8 de CMS” não
cobrem todas as rotas; `route()` para `applications.show` e as quatro rotas de
candidaturas ficam fora do inventário declarado.

**Correção por documento:** trocar “11 endpoints (3 públicos, 8 no CMS)” por “13
endpoints (3 públicos, 10 no CMS)” em `TrabalheConosco/rotas.md:17`; ajustar `:34`,
`:43`, `:121`, `:563` e `:566`; em `TrabalheConosco/spec.md:31` e `:779`, trocar
“11 endpoints”/“8 endpoints” por “13 endpoints”/“10 endpoints”; conferir o CSV parcial
(que já está correto).

### G4-03 — FeedRSS não recria item após despublicar/desativar, ao contrário do que dizem Notícias e TrabalheConosco

**Severidade: P2. Confiança: 0.98.**

`FeedRssService::removeItem` faz soft delete (`FeedRssItem::…->delete()`,
`V2/Services/FeedRSS/Services/FeedRssService.php:79-90`) e `addItem` usa
`FeedRssItem::updateOrCreate` (`:39-58`). O model tem `SoftDeletes`
(`V2/Services/FeedRSS/Models/FeedRssItem.php:28-35`), então o `updateOrCreate` não
enxerga a linha soft-deletada; o `INSERT` colide com o unique
`(entity_type, entity_id)` (`V2/Services/FeedRSS/Migrations/2026_07_20_000001_create_v2_feed_rss_items_table.php:44`).
O `Throwable` é engolido no hook `saved` de `News` (`Models/News.php:292-319`) e de
`JobOpening` (`Models/JobOpening.php:137-160`). O ciclo publicar → despublicar →
publicar, ou desativar → reativar uma vaga, termina sem item no feed.

Os documentos afirmam o contrário: `Noticias/servicos-e-fluxos.md:560` diz que `saved`
“reindexa/republica feed” e `Noticias/spec.md:623` (`PC-NOTICIAS-06`) exige que
“publicar” crie o item; `TrabalheConosco/spec.md:73` diz que o hook faz “upsert quando
ativa; remoção quando inativa”. A unidade Agenda documenta o problema corretamente em
`Agenda/dados.md:268` (RN-AGENDA-14), o que evidencia a contradição entre unidades.

**Impacto:** uma reimplementação que siga Notícias/TrabalheConosco espera que o feed
volte ao publicar/reativar; o legado perde o item silenciosamente. Um teste de paridade
baseado em `PC-NOTICIAS-06` falha no segundo publish.

**Correção por documento:** em `Noticias/servicos-e-fluxos.md:560` e `Noticias/spec.md:623`,
documentar a limitação (item soft-deletado impede o upsert; seria necessário
`withTrashed()`/restore no `FeedRssService`) e acrescentar o caso “despublicar →
publicar” ao critério; em `TrabalheConosco/spec.md:73`, registrar o mesmo efeito para
“desativar → reativar”; ou corrigir `FeedRssService::addItem` para restaurar a linha
soft-deletada antes do upsert.

### G4-04 — `permissoes.csv` de Notícias conta 12 rotas nacionais, mas são 13

**Severidade: P3. Confiança: 0.99.**

`_indices/parcial-MOD-PUBLICACOES-NOTICIAS/permissoes.csv:5` afirma “bloquear 12 rotas
administrativas nacionais”; `rotas.md:30` e `:122` e o próprio `rotas.csv` R09
(`_indices/parcial-MOD-PUBLICACOES-NOTICIAS/rotas.csv`) dizem 13. O grupo nacional-only
tem 13 endpoints efetivos: toggle, set-super-highlight, arquivadas, restaurar, SEO, tags
(3) e categorias (5) — `Routes/cms.php:52-129`; a duplicata `categories.index` não conta.

**Impacto:** o índice de permissões contradiz o texto e o índice de rotas; a contagem de
cobertura do middleware fica ambígua.

**Correção:** trocar “12” por “13” na linha citada de `permissoes.csv`.

### G4-05 — Portal de Notícias afirma que notícia agendada “não vaza” no detalhe

**Severidade: P3. Confiança: 0.97.**

`Noticias/portal.md:315` (`RN-NOTICIAS-PORTAL-01`) diz “Só aparece na lista/detalhe quem
tem `status=published` e `published_at <= now()` (lista)” e, na coluna de efeito,
“rascunho e agendada não vazam”. O próprio documento, na regra seguinte (`:316`,
RN-02) e em §3.3, afirma que `show` usa apenas `published()` e que a notícia agendada é
acessível por URL direta. O código confirma: `NoticiasPortalController.php:81` não
compara `published_at`.

**Impacto:** o leitor pode implementar uma checagem de data no detalhe — mudando o
contrato legado (200 para agendada) — ou escrever um teste que espera 404 no detalhe.

**Correção:** reescrever RN-01 para circunscrever a regra à **listagem** e declarar
explicitamente que o detalhe de notícia agendada responde `200` por URL direta;
alinhar a coluna “Efeito”.

### G4-06 — `PC-NOTICIAS-PORTAL-12` espera 404 onde o legado responde 500

**Severidade: P3. Confiança: 0.98.**

`Noticias/portal.md:698` (`PC-NOTICIAS-PORTAL-12`) diz “preview por token devolve 404
(reimplementar o método, não o 500 atual)”. O legado chama `News::isAccessible()`, método
inexistente (`NoticiasPortalController.php:156`), e termina em `500`; o `abort(404)` é
inalcançável. O próprio `spec.md:637` (`PC-NOTICIAS-20`) exige 500 no legado e deixa o
contrato da reimplementação para `LG-NOTICIAS-A01`.

**Impacto:** dois critérios de paridade da mesma rota esperam status diferentes; um
teste de paridade escrito a partir do portal falha.

**Correção:** trocar `PC-NOTICIAS-PORTAL-12` para “no legado, `GET
/noticia/{slug}/preview` responde 500; a reimplementação deve definir o contrato em
`LG-NOTICIAS-A01`”, alinhando com `PC-NOTICIAS-20`.

### G4-07 — Notícias: 20 endpoints CMS efetivos, não 21

**Severidade: P3. Confiança: 0.98.**

`Noticias/cms.md:33` afirma “21 endpoints + 2 grupos” em `Routes/cms.php`. O arquivo tem
21 **declarações** de rota, mas `POST /categorias` é declarado duas vezes
(`Routes/cms.php:111-119`); a primeira é sobrescrita pelo `RouteCollection`, deixando 20
endpoints efetivos. É o que dizem `spec.md:519-528` (20 CMS) e `rotas.md:18` (20 CMS +
4 públicos). A própria tabela de `cms.md` §5, ao agrupar tags/categorias, reflete 20.

**Impacto:** a ficha de UI contradiz o inventário de rotas e o CSV; quem contar 21 inclui
uma rota que nunca resolve.

**Correção:** trocar “21 endpoints” por “20 endpoints efetivos (21 declarações; a
duplicata `categories.index` é sobrescrita)” em `cms.md:33`.

### G4-08 — Historia: 11 ações, não 10

**Severidade: P3. Confiança: 0.98.**

`Historia/spec.md:28` diz “11 rotas, 10 ações”. O controller expõe 11 ações públicas:
`index`, `create`, `store`, `edit`, `update`, `destroy`, `reorder`, `toggleActive`,
`preview`, `config` e `updateConfig` (`MilestoneCmsController.php`; as 11 rotas em
`Routes/cms.php:7-60`). A lista da própria “Superfície pública” em `spec.md:45` inclui as
11.

**Impacto:** inventário do CMS subestima uma ação (`updateConfig`), que é justamente a
que termina em 500 após persistir (RN-HISTORIA-12).

**Correção:** trocar “10 ações” por “11 ações” em `spec.md:28`.

### G4-09 — CertificadoDigital: 7 actions e 10 blades, não 8 e 9

**Severidade: P3. Confiança: 0.99.**

`CertificadoDigital/spec.md:20` e `:34` afirmam “9 views”/“9 blades” e “8 actions”. O
controller tem 7 ações públicas — `index`, `create`, `store`, `signForm`,
`signDocument`, `show`, `destroy` (o `__construct` não é action) — e o módulo tem 10
blades, conforme `_indices/parcial-MOD-INFRAESTRUTURA-CERTIFICADODIGITAL/componentes.csv`
(C01–C10). As 7 rotas estão corretas.

**Impacto:** o inventário de telas/ações do gate fica inconsistente com o CSV de
componentes e com a lista de rotas.

**Correção:** trocar “8 actions” por “7 actions” e “9 views”/“9 blades” por “10 blades”
em `spec.md:20` e `:34`.

### G4-10 — UnidadeRegional cita tabela de índice que não existe (`v2_search_index`)

**Severidade: P3. Confiança: 0.99.**

`UnidadeRegional/dados.md:27` diz que a saída inclui “entradas em `v2_search_index`”. A
tabela criada pelo serviço de busca é `v2_public_search_index`
(`V2/Services/Search/Migrations/2026_06_14_000000_create_search_indexes_tables.php:43`);
o índice administrativo é `v2_cms_links_index`. Não há `v2_search_index` no schema.

**Impacto:** uma reimplementação pode criar/consultar a tabela errada ou concluir que a
unidade não indexa.

**Correção:** trocar `v2_search_index` por `v2_public_search_index` em `dados.md:27`.

### G4-11 — MailAudit documenta um 403 que o controller nunca alcança

**Severidade: P3. Confiança: 0.96.**

`MailAudit/spec.md:68` prevê “Sem `MailAudit:read` (mas com acesso ao módulo)” → `403`
do controller. O acesso ao módulo é dado por `V2PermissionMiddleware`: `none` é
bloqueado antes do controller e `write` implica `read` em
`PermissionService::hasPermission` (`V2/Core/Services/PermissionService.php:40-63`;
`V2/Core/Helpers/helpers.php:12-27`). Portanto, toda requisição que chega a
`MailAuditCmsController` já passou por `v2_can('MailAudit','read')`; o `403` de
`MailAuditCmsController.php:40-42` é defesa em profundidade inalcançável por HTTP normal.

**Impacto:** um teste de contrato pode esperar `403` para um perfil que, na prática,
recebe redirect/403 do middleware antes.

**Correção:** marcar a linha como “inalcançável por HTTP normal (defesa em profundidade)”
ou removê-la da tabela de contrato, como já é feito no documento de CertificadoDigital.

### G4-12 — Parceiros: linha de dados diz que o slug é recalculado no update

**Severidade: P3. Confiança: 0.99.**

`ParceirosInstitucionais/dados.md:42` afirma que editar faz “`UPDATE` de todos os campos
do form; slug recalculado”. O hook `Parceiro::saving` preserva o slug existente
(`V2/Modules/Conexoes/ParceirosInstitucionais/Models/Parceiro.php:60-77`) e a RN-04 do
próprio módulo (`spec.md:290`) diz que renomear **não** altera o slug. A linha é uma
contradição interna.

**Impacto:** induz a reimplementação a regenerar o slug no update, quebrando URLs de
busca/widget.

**Correção:** trocar “slug recalculado” por “slug preservado quando já existe” em
`dados.md:42`.

### G4-13 — TrabalheConosco e Agenda descrevem um sufixo de `<title>` que o partial não aplica

**Severidade: P3. Confiança: 0.98.**

`V2/layouts/portal/metadata/seo.blade.php:2` usa
`{{ $seoTitle ?? ($pageTitle ?? '') . (isset($pageTitle) ? " | " : "") . $siteTitle }}`.
Quando `$seoTitle` é não nulo, o `??` curto-circuita e `pageTitle`/`siteTitle` **não** são
concatenados. Em TrabalheConosco o controller chama `setTitle`
(`TrabalheConoscoController.php:41-43,68`), então o `<title>` é
`Trabalhe Conosco — {app.name}`; não há `| {titulo_site}`. Em Agenda o detalhe não chama
`setTitle`, mas o composer preenche `seoTitle` com `titulo_site` quando o serviço está
vazio (`SEOServiceProvider.php:47`), então o `<title>` é apenas `titulo_site`; também não
há duplicação. As afirmações contrárias estão em `TrabalheConosco/portal.md:185-187`,
`:324` e `:648` (`PC-TRABALHECONOSCO-66`) e em `Agenda/portal.md:302` e `:473`. Os
documentos de Notícias (`portal.md:603`) e História (`portal.md:433`) descrevem o
curto-circuito corretamente.

**Impacto:** testes de paridade e reimplementações esperam um `<title>` com sufixo e/ou
duplicação que o legado não produz; a origem do título fica ambígua entre unidades.

**Correção por documento:** em `TrabalheConosco/portal.md`, trocar as três ocorrências por
“`<title>` = `setTitle` (sem sufixo); `pageTitle`/`siteTitle` só entram se `seoTitle` for
nulo”; em `Agenda/portal.md:302`, remover a duplicação e registrar que o título do detalhe
é `titulo_site` (fallback do composer); em `:473`, descrever o curto-circuito.

### G4-14 — TrabalheConosco diz que os flags de layout são inertes; eles chegam ao `portal-content`

**Severidade: P3. Confiança: 0.96.**

`TrabalheConosco/portal.md:210-214` afirma que `app-layout` não repassa
`fullWidthMain`/`showHeader`/`showMainHeader`/`showFooter`/`showMainLeftArea`/
`showMainRightArea` e que “esses flags são inertes”. O `@extends` passa o array de dados
para `app-layout`; o `@include('v2-portal::partials.portal-content', …)` de
`app-layout.blade.php:11-21` compila com `array_except(get_defined_vars(), …)`, que herda
os flags, e `portal-content.blade.php:21,41,117` os consome (`$showHeader ?? true`,
`$showMainHeader ?? true`, `$showFooter ?? true`, `$fullWidthMain ?? false`). O mesmo
mecanismo é usado e documentado como efetivo em Notícias/Agenda/Parceiros.

**Impacto:** uma reimplementação que siga a afirmação renderiza header/footer/áreas
laterais e largura padrão em uma página que o legado renderiza sem eles — diferença
visual e de navegação.

**Correção:** substituir o parágrafo de `TrabalheConosco/portal.md:210-214` por “os flags
são herdados por `app-layout` e consumidos por `portal-content`; o `<head>` recebe apenas
`pageTitle`, `pageDescription`, `coverImage`, `gtag` e `schemas`”.

## 3. O que foi conferido sem achado

- **Notícias:** hooks `creating/saving/saved/deleted` (`News.php:252-339`), slug imutável
  no update, `published_at` definido só na transição, soft delete/restore, exclusividade
  do super-destaque, categorias `is_system`, duplicata `POST /categorias` e a
  inalcançabilidade do nome `.index`, sitemap lendo `DB::table` sem `deleted_at`,
  preview público em 500, importação (dry-run, idempotência por sha256, transação,
  categoria/regional derivadas, relatório JSON) e limites de mídia. O único ponto de
  estado não coberto é o feed após republicação (G4-03).
- **Agenda:** escopos `published`/`active`/`upcoming`/`forDate`/`forWeek`/`forMonth`,
  semana domingo–sábado, evento sem término, filtro `date` incluindo passados, detalhe
  sem `active()`, sitemap sem filtro de publicação, `is_active === '1'`, cache `page:*`
  e trilha de auditoria. A própria unidade documenta corretamente a colisão de feed
  (RN-AGENDA-14).
- **TrabalheConosco:** flag desativada por migration, ordem `module.active` antes do
  `CmsAuthenticate`, prefixo `cms/institucional/trabalhe-conosco` e o ramo de redirect
  `=== 'cms'` que não casa, `scopeOpen`, upload `local`, candidatura `pending`, e-mail
  best-effort, soft delete de vaga/candidatura, permissões e mensagens. O inventário de
  rotas é o achado G4-02.
- **Associados:** escopo regional, catálogo próprio `v2_associado_tags` versus
  `v2_conexao_tags`, `syncTagsFromArray` com `is_primary`, `syncUnidadeRegional`,
  galeria com `gallery_media_id` varchar, `restore` sem reindexar, cache global e
  contrato do portal. O slug é o achado G4-01.
- **Parceiros:** slug estável, `sort_order`/toggle, tags em `v2_conexao_tags`,
  `syncToSearch` com `module`/`entity_type` corretos, sitemap incluindo inativos,
  `findOrFail` sem `active()` no detalhe, cache e permissões.
- **UnidadeRegional:** token `asc_` + sha256/prefixo/TTL 365, `last_used_at` disparando
  hooks, middleware de API por token, throttle 30/min nos POST, categoria
  `regional-<sigla>`, tag da sigla, `withTrashed` no update, vínculo de associados,
  soft delete, portal e OpenAPI. O nome da tabela de índice é G4-10.
- **Historia:** reorder por `ids[]`, toggle `-1`/`max+1`, soft delete, renderização do
  AST, `resolveMilestoneImages` em lote, `updateConfig` que persiste e depois estoura
  `TypeError`, cache `page:*` e sitemap por existência de tabela. A contagem de ações é
  G4-08.
- **FaleConosco:** reCAPTCHA condicional, validação de telefone, e-mails best-effort via
  `MailService`, `v2_mail_logs`, `is_read`/`replied_at`/`is_archived`, inbox 20/página,
  bug `Request::boolean` (`?unread=1` → 500) e exclusão dos caches.
- **AssocieSe:** `metadata` inexistente no lead, `plan_id` aceito sem validar atividade,
  galeria `uuid` × id inteiro, `updateConfig` com `TypeError` pós-commit, sete chaves em
  `v2_portal_configs`, planos `active`/`valid`, e-mail ao suporte e ausência de cache.
- **CertificadoDigital:** escopo por `user_id`, bypass de suporte, importação/validação
  de PKCS#12, cifra por `user_id`, `v2_signature_logs`, soft delete, headers de download
  e defesa em profundidade do controller.
- **GestaoPermissoes:** matriz JSONB sem cache, `PermissionService` relendo por request,
  bloqueio de `super-admin` por slug, remoção de módulos `restricted`, ativação Sentinel,
  `ensureSentinelRole`, N+1 de perfis e efeito imediato das permissões.
- **MailAudit:** universo de 200, corte em 50, filtros em PHP, estatísticas com
  `getHistory(999999)` e o bug de detalhe `getHistory(1)` (só o registro mais recente
  abre; os demais dão 404).
- **FeedRss:** painel restrito por identidade, `toggle` por `'1'`, soft delete,
  estatísticas, paginação e ausência de invalidação de cache (o feed pode ficar 5 min
  atrasado). O comportamento de recriação é G4-03.
- **Índices:** `merge_indices.py --check` OK; contagens de rotas/tabelas/componentes dos
  demais módulos conferem com o código; os parciais de Notícias, Agenda, Associados,
  Parceiros, UnidadeRegional, Historia, FaleConosco, AssocieSe, CertificadoDigital,
  FeedRss, GestaoPermissoes e MailAudit foram confrontados com as tabelas dos docs.

## 4. Correções recomendadas por documento

| Documento | Correções |
|---|---|
| `Conexoes/Associados/spec.md` | G4-01 (`:262`, `PC-ASSOCIADOS-02` `:631`). |
| `Conexoes/Associados/dados.md` | G4-01 (`:63`, `:138`, `:254`, `:378`; também `:30`). |
| `Conexoes/ParceirosInstitucionais/dados.md` | G4-12 (`:42`). |
| `Publicacoes/TrabalheConosco/spec.md` | G4-02 (`:31`, `:779`); G4-03 (`:73`). |
| `Publicacoes/TrabalheConosco/rotas.md` | G4-02 (`:17`, `:34`, `:43`, `:121`, `:563`, `:566`). |
| `Publicacoes/Noticias/servicos-e-fluxos.md` | G4-03 (`:560`). |
| `Publicacoes/Noticias/spec.md` | G4-03 (`PC-NOTICIAS-06`, `:623`). |
| `Publicacoes/Noticias/cms.md` | G4-07 (`:33`). |
| `Publicacoes/Noticias/portal.md` | G4-05 (`:315`); G4-06 (`:698`). |
| `_indices/parcial-MOD-PUBLICACOES-NOTICIAS/permissoes.csv` | G4-04 (`:5`). |
| `Institucional/Historia/spec.md` | G4-08 (`:28`). |
| `Infraestrutura/CertificadoDigital/spec.md` | G4-09 (`:20`, `:34`). |
| `Conexoes/UnidadeRegional/dados.md` | G4-10 (`:27`). |
| `Infraestrutura/MailAudit/spec.md` | G4-11 (`:68`). |
| `Publicacoes/Agenda/portal.md` | G4-13 (`:302`, `:473`). |
| `Publicacoes/TrabalheConosco/portal.md` | G4-13 (`:185-187`, `:324`, `:648`); G4-14 (`:210-214`). |

## 5. Veredito

**Aprovado com ressalvas (correções necessárias antes de fechar o G4).** A amostra cobre
as quatro dimensões e os contratos centrais com profundidade; os trechos são literais e
os índices estão estruturalmente consistentes. Não há P1/P0.

As ressalvas materiais são duas: (a) a invariante de slug de Associados está invertida
em texto e em critério de paridade (G4-01); (b) o contrato de FeedRSS está incompleto em
Notícias/TrabalheConosco, que prometem recriar o item ao publicar/reativar, quando o
`updateOrCreate` colide com a linha soft-deletada (G4-03). Também há um erro de
inventário de rotas em TrabalheConosco (G4-02) e onze P3 (G4-04..G4-14), entre eles um
nome de tabela errado (G4-10) e duas divergências de SEO/layout (G4-13/G4-14). Corrigidos os textos e os critérios de paridade, o gate pode ser fechado
sem nova leitura integral.
