---
id: REIMPL-PARIDADE
tipo: reimplementacao
onda: 5
status: concluido
unidades: []
fontes:
  - V2/
  - app/
  - config/
  - tests/
depende_de: [REIMPL-ORDEM, ARCH-OVERVIEW, CORE-BOOT, CORE-AUTH, CORE-DATA, CORE-RUNTIME]
consumidores: [equipe-de-reconstrucao, qa, agente-reconstrucao-cega]
criticidade: alta
dono: agente-reimplementacao
---

## Efeito

Converte os `PC-*` espalhados pelos documentos de unidade em um critério de aceite
executável por unidade. O efeito prático é duplo:

1. Nenhuma unidade é declarada “pronta” por semelhança visual; ela passa por camadas
   explícitas de contrato (rota, schema, serviço, UI, integração).
2. Os testes de contrato transversais (rotas, tokens, importação, uploads, assinatura,
   permissões, cache) são escritos **antes** dos testes de unidade, porque uma falha neles
   invalida dezenas de PCs de uma vez.

No snapshot desta síntese, o corpus tem 1070 identificadores `PC-*` únicos (contando
referências cruzadas) e 56 unidades; a matriz §3 mapeia quem tem checklist próprio e quem depende de PCs de outra
unidade.

## Abordagem

### 1. Modelo de aceite em cinco camadas

| Camada | Nome | O que prova | Falha típica que ela pega |
|---|---|---|---|
| A1 | Contrato HTTP | método, URI, middleware, status, headers, corpo literal | rota sem auth, 302 onde o doc promete 403, cache em POST |
| A2 | Contrato de dados | tabela, colunas, tipos, PK/FK/unique, defaults, soft delete | coluna faltante, `uuid` vs `int`, duplicidade de metadata |
| A3 | Contrato de serviço | assinatura pública, efeitos, erros, idempotência | exceção onde o contrato promete `MailResult(false)` |
| A4 | Contrato de UI/JS | DOM ids, eventos, props, estado, teardown | picker sem evento, modal que não fecha, nonce ausente |
| A5 | Integração | indexação, feed, cache, e-mail, storage, auditoria | salvar não invalida cache, remover não tira do índice |

Uma unidade só fecha quando **todos os PCs do seu checklist** têm teste em pelo menos uma
camada, e os PCs transversais (A1/A2/A5) estão verdes. PCs sem teste observável são
convertidos em lacuna explícita, nunca em “ok” tácito.

### 2. Testes de contrato a escrever primeiro

Estes testes são pré-requisito de todas as unidades. Cada um nasce de um contrato citado e
deve rodar na etapa E1–E5 de `ordem-de-construcao.md`.

#### CT-ROUTES — tabela de rotas e pilha

| ID | Caso | Evidência |
|---|---|---|
| CT-ROUTES-01 | Snapshot da tabela de rotas: 243 endpoints, 30 grupos, 2 redirects e 2 rotas, com método+URI+middleware+controller; qualquer rota nova aparece no diff | `_indices/rotas.csv`; `01_arquitetura/02-roteamento.md:350-357` |
| CT-ROUTES-02 | `GET /login` → 301 `/cms/login`; `GET /cms/dashboard` autenticado → 301 `/cms` | `01_arquitetura/02-roteamento.md:350-351` |
| CT-ROUTES-03 | Ordem da pilha: `module.active` antes de `CmsAuthenticate`; `CmsAiToken` no grupo `web`; RBAC depois da sessão | `01_arquitetura/04-middlewares-seguranca.md:71-109` |
| CT-ROUTES-04 | Rota de serviço (`cms/v2/gallery`, `api/v2/mail`) não herda a pilha de módulo; o teste deve **falhar** se a rota ficar anônima | `01_arquitetura/04-middlewares-seguranca.md:537`; `_qa/security-g1.md:20-46` |
| CT-ROUTES-05 | Fallback `/{slug}` responde só depois de todas as rotas de módulo; `/Busca` (maiúscula) → 404 | `PC-CORE-BOOT-05,12` |

#### CT-TOKENS — sessão, CSRF e canais de API

| ID | Caso | Evidência |
|---|---|---|
| CT-TOKENS-01 | Login válido regenera sessão; logout invalida; segunda chamada protegida volta ao login | `01_arquitetura/03-auth-rbac-sentinel.md:787` |
| CT-TOKENS-02 | AJAX sem sessão → 401 `Unauthorized.`; com sessão e sem role → 403 `Forbidden. No administrative roles assigned.` | `01_arquitetura/03-auth-rbac-sentinel.md:778` |
| CT-TOKENS-03 | POST sem `_token` em rota isenta (Gallery) deve **ser negado** na reimplementação: 419 | `PC-GALLERY-10`; `_qa/security-g1.md:20-46` |
| CT-TOKENS-04 | Chave interna: `missing_key` 401, `invalid_key` 401, `key_revoked` 401, `key_expired` 401, `ip_not_allowed` 403, `scope_not_allowed` 403, nesta ordem | `01_arquitetura/04-middlewares-seguranca.md:134-135,230`; `PC-NOTICIASIMPORT-16,19,20` |
| CT-TOKENS-05 | `--ttl=0` grava `expires_at=null`; `--ttl=90` grava ~90 dias; segredo só existe em claro na emissão | `PC-NOTICIASIMPORT-21`; `01_arquitetura/04-middlewares-seguranca.md:438-439` |
| CT-TOKENS-06 | Bearer regional `asc_`: 60 caracteres, apenas sha256 e prefixo persistidos, TTL de 365 dias, revogação → `401 invalid_token` | `05_modulos/Conexoes/UnidadeRegional/spec.md:61,761,763` |
| CT-TOKENS-07 | Canal IA: token fixo, sem TTL; com `APP_ENV=prod` **não** é bloqueado; a reimplementação deve falhar fechado (`enabled=false`/token ausente) | `_qa/security-g1.md:48-62,177-190` |
| CT-TOKENS-08 | Token de IA não aparece em log de RBAC quando `allow_query=false` | `_qa/security-g1.md:191-201` |

#### CT-IMPORT — lote de notícias

| ID | Caso | Evidência |
|---|---|---|
| CT-IMPORT-01 | `POST /run` com diretório fora de `storage/app/imports` e `TMP` → 422 `invalid_dir` | `PC-NOTICIASIMPORT-18`; `05_modulos/Support/NoticiasImport/spec.md:400` |
| CT-IMPORT-02 | `consumer` divergente → 403 `consumer_mismatch`, sem efeito de domínio | `PC-NOTICIASIMPORT-17` |
| CT-IMPORT-03 | Dry-run não grava; apply duas vezes no mesmo lote devolve `skipped` e mantém a contagem | `PC-NOTICIASIMPORT-22` |
| CT-IMPORT-04 | Relatório de aborto tem `context.abort_code` e não tem `error`; 200/429 têm `X-RateLimit-*` | `PC-NOTICIASIMPORT-23,24` |
| CT-IMPORT-05 | Notícia importada por regional nasce `pending_approval` e recebe a categoria da unidade | `PC-UNIDADEREGIONAL-05,06` |

#### CT-UPLOAD — mídia, galeria e editor

| ID | Caso | Evidência |
|---|---|---|
| CT-UPLOAD-01 | Upload válido grava original e variantes `360/720/1200/1920`; abaixo de 360 px gera só `original` | `PC-MEDIA-01,02`; `03_servicos-ui/media/spec.md:590-591` |
| CT-UPLOAD-02 | Extensão/MIME fora da whitelist → 422; nenhuma linha criada; `max:10240` | `PC-GALLERY-02`; `03_servicos-ui/gallery/spec.md:88,396` |
| CT-UPLOAD-03 | Escopo `owner_module` derivado do servidor, nunca do request; escrita em mídia alheia → 404 e nenhuma alteração | `PC-GALLERY-04,10` |
| CT-UPLOAD-04 | SVG: sanitizar ou forçar download com `Content-Disposition`/CSP | `PC-GALLERY-15` |
| CT-UPLOAD-05 | Rotação só em imagem; GIF/PDF → 400 com mensagem literal; arquivar preserva arquivo, excluir apaga original e variantes | `PC-GALLERY-06,07` |
| CT-UPLOAD-06 | Upload do editor usa a mesma whitelist de 21 extensões e 10 MB | `03_servicos-ui/content-rich-editor/spec.md:308,617` |

#### CT-SIGN — certificado e PDF

| ID | Caso | Evidência |
|---|---|---|
| CT-SIGN-01 | PFX válido cria 1 linha com PEM e chave cifrada; senha errada → `CERT_INVALID_PASSWORD` com mensagem literal | `PC-SVC-DIGITALSIGNATURE-01,02` |
| CT-SIGN-02 | Chave só decifra com o mesmo `APP_KEY` **e** o mesmo `user_id`; outro usuário → `CERT_DECRYPTION_FAILED` | `PC-SVC-DIGITALSIGNATURE-04`; `02_servicos-base/digital-signature/spec.md:25,203-216` |
| CT-SIGN-03 | PDF assinado contém `/SubFilter /adbe.pkcs7.detached`; hash de 64 hexadecimais maiúsculos; log `success` | `PC-SVC-DIGITALSIGNATURE-06,08` |
| CT-SIGN-04 | `add_visual_stamp=false` não injeta HTML e produz assinatura invisível | `PC-SVC-DIGITALSIGNATURE-09` |
| CT-SIGN-05 | Suporte lista certificado alheio mas não assina com ele; histórico global só para suporte | `PC-SVC-DIGITALSIGNATURE-11,13` |

#### CT-PERM — RBAC V2 e escopo

| ID | Caso | Evidência |
|---|---|---|
| CT-PERM-01 | `{Noticias: read}` → 200 em GET e 403 em POST; `write` passa nos dois | `PC-CORE-AUTH-05` |
| CT-PERM-02 | Chave ausente na matriz → 302 ao dashboard com mensagem literal; sidebar oculta | `PC-CORE-AUTH-06,09` |
| CT-PERM-03 | `config('v2.support.email')` atravessa qualquer módulo, inclusive Support, mesmo com `v2_role_id=null`; `super-admin` **não** atravessa Support | `PC-CORE-AUTH-07,08`; `05_modulos/Support/NoticiasImport/spec.md:393` |
| CT-PERM-04 | `v2_role_id=null` → 200 em `/cms`, `/cms/search`, `/cms/conexao-tags`; 302 nas rotas de módulo | `PC-CORE-AUTH-28`; `_qa/security-g1.md:123-134` |
| CT-PERM-05 | `last_login` é escrito no login, antes dos gates de role/ativação | `PC-CORE-AUTH-31`; `_qa/security-g1.md:135-146` |
| CT-PERM-06 | Operador regional não acessa rota nacional (`regional.restrict`): 403 JSON ou 302 dashboard | `01_arquitetura/04-middlewares-seguranca.md:154` |
| CT-PERM-07 | `SupportConfigs` só é protegido por action (`authorizeSupportAccess`), exceto o reset de tema; o teste deve cobrir o reset e esperar guarda | `_qa/security-g1.md:79-95,110-122` |

#### CT-CACHE — página, CSP e invalidação

| ID | Caso | Evidência |
|---|---|---|
| CT-CACHE-01 | Segunda requisição pública → `X-Cache-Status: HIT`; nonce novo casa com o header CSP | `PC-CORE-RUNTIME-01,02` |
| CT-CACHE-02 | AJAX não recebe `X-Cache-Status` nem é gravado; POST idem | `PC-CORE-RUNTIME-03,06` |
| CT-CACHE-03 | `/cms/login` não recebe `Cache-Control: public`; `/robots.txt` recebe `public, max-age=60, must-revalidate` + `Vary` | `PC-CORE-RUNTIME-04,05` |
| CT-CACHE-04 | TTL efetivo de 300 s na pilha-alvo (contrato: 5 minutos, não 5 segundos) | `PC-CORE-RUNTIME-09`; `01_arquitetura/09-cache-csp-minify-maintenance.md:372-374` |
| CT-CACHE-05 | Salvar tema/configuração limpa `page:*`; upload/exclusão de asset **não** limpa | `PC-SLE-12`; `_qa/review-g3.md:157-172` |
| CT-CACHE-06 | `v2:clear-page-cache` remove as chaves e retorna 1 com mensagem literal quando o store falha | `PC-CORE-BOOT-07`; `PC-CORE-RUNTIME-10` |

### 3. Matriz de cobertura (unidade → PCs → verificação)

Legenda de verificação: **A1** HTTP, **A2** dados, **A3** serviço, **A4** UI/JS, **A5**
integração. A coluna “PCs” é o total de identificadores únicos do prefixo; a coluna
“Checklist” aponta o documento e as linhas onde os itens estão escritos.

#### 3.1 Fundação

| Unidade | Checklist (doc:linhas) | PCs | Verificação |
|---|---|---|---|
| `ARCH-OVERVIEW` | `01_arquitetura/00-visao-geral.md:303-308` | `PC-ARCH` 6 | A1, A2, A5 |
| `CORE-BOOT` | `01_arquitetura/01-boot-autodiscovery.md:392-399`; `02-roteamento.md:350-357` | `PC-CORE-BOOT` 16 | A1, A4, A5 |
| `CORE-AUTH` | `01_arquitetura/03-auth-rbac-sentinel.md:775-791`; `04-middlewares-seguranca.md:800-818` | `PC-CORE-AUTH` 36 | A1, A3, A5 |
| `CORE-DATA` | `01_arquitetura/05-persistencia-uuid-migracoes.md:412-419`; `06-tenancy-regional.md:451-460`; `12-migracoes-inventario.md:463-469` | `PC-CORE-DATA` 21 | A2, A3 |
| `CORE-CMS` | `01_arquitetura/07-controllers-core.md:497-508`; `08-helpers-composers.md:539-614` | `PC-CORE-CMS` 23 | A1, A3, A4 |
| `CORE-RUNTIME` | `01_arquitetura/09-cache-csp-minify-maintenance.md:540-551`; `11-satelites-build-config.md:534-543` | `PC-CORE-RUNTIME` 25 | A1, A2, A5 |
| `GRP-*` (9 grupos) | `01_arquitetura/10-grupos-e-modulos-vazios.md:315-320` | `PC-GRP` 6 | A1, A4 |
| `MOD-INDEX` | `05_modulos/README.md:358-362` | `PC-MOD-INDEX` 5 | A3, A4 |

#### 3.2 Serviços

| Unidade | Checklist (doc:linhas) | PCs | Verificação |
|---|---|---|---|
| `SVC-ACTIVITYLOGGER` | `02_servicos-base/activity-logger.md:569-583` | `PC-ACTIVITYLOGGER` 15 | A2, A3, A4 |
| `SVC-DASHBOARD` | `02_servicos-base/dashboard.md:479-487` | `PC-DASHBOARD` 9 | A1, A3 |
| `SVC-DIGITALSIGNATURE` | `02_servicos-base/digital-signature/spec.md:668-682`; `assinatura-pdf.md:549-559` | `PC-SVC-DIGITALSIGNATURE` 26 | A2, A3, A5 |
| `SVC-FEEDRSS` | `02_servicos-base/feed-rss.md:546-558` | `PC-FEEDRSS` 13 | A1, A3, A5 |
| `SVC-MAILSERVICE` | `02_servicos-base/mail-service.md:548-558` | `PC-MAILSERVICE` 11 | A1, A3, A5 |
| `SVC-STORAGE` | `02_servicos-base/storage.md:468-475` | `PC-STORAGE` 8 | A3, A5 |
| `SVC-CONTENTRICHEDITOR` | `03_servicos-ui/content-rich-editor/spec.md:847-995`; `ast-e-renderers.md:494-504`; `ui-contrato.md:515-531` | `PC-CONTENTRICHEDITOR` 48 | A2, A3, A4, A5 |
| `SVC-EXCELIMPORTER` | `03_servicos-ui/excel-importer/spec.md:626-639`; `ui-contrato.md:580-591` | `PC-EXCELIMPORTER` 26 | A1, A3, A4 |
| `SVC-GALLERY` | `03_servicos-ui/gallery/spec.md:872-886`; `ui-contrato.md:592-602` | `PC-GALLERY` 15 + `PC-GALLERY-UI` 11 | A1, A2, A3, A4 |
| `SVC-LANDINGSECTIONSEDITOR` | `03_servicos-ui/landing-sections-editor/spec.md:553-629`; `ui-contrato.md:582-592`; `widgets-e-schemas.md:552-560` | `PC-LSE` 35 | A1, A3, A4 |
| `SVC-MEDIA` | `03_servicos-ui/media/spec.md:590-603`; `ui-contrato.md:551-563` | `PC-MEDIA` 14 + `PC-MEDIA-UI` 13 | A2, A3, A4 |
| `SVC-SEARCH` | `03_servicos-ui/search/spec.md:540-549` | `PC-SEARCH` 10 | A1, A2, A5 |
| `SVC-SEO` | `03_servicos-ui/seo/spec.md:688-702`; `contrato.md:442-450` | `PC-SEO` 24 | A1, A3, A4, A5 |
| `SVC-INDEX` | sem PCs (apenas README) | 0 | não implementar |

#### 3.3 UI global

| Unidade | Checklist (doc:linhas) | PCs | Verificação |
|---|---|---|---|
| `UI-CMS` | `04_ui/01-cms-design-system.md:728-742`; `02-cms-componentes.md:721-733` | `PC-UI-CMS` 28 | A4 |
| `UI-PORTAL` | `04_ui/03-portal-design-system.md:620-708`; `04-acessibilidade.md:473-554`; `05-portal-componentes.md:476-565` | `PC-UI-PORTAL` 10 + `A11Y` 12 + `COMP` 10 | A4 |

#### 3.4 Módulos

| Unidade | Checklist (doc:linhas) | PCs | Verificação |
|---|---|---|---|
| `MOD-CONEXOES-ASSOCIADOS` | `05_modulos/Conexoes/Associados/spec.md:630-643` (+ docs cms/dados/portal/rotas) | `PC-ASSOCIADOS` 14 | A1, A2, A3, A4 |
| `MOD-CONEXOES-PARCEIROSINSTITUCIONAIS` | `spec.md:549-564`; `cms.md:490-501`; `dados.md:467-474`; `portal.md:485-500`; `rotas.md:481-493` | `PC-PARCEIROSINSTITUCIONAIS` 65 | A1–A5 |
| `MOD-CONEXOES-UNIDADEREGIONAL` | `spec.md:760-777` (+ api/cms/dados/portal/rotas) | `PC-UNIDADEREGIONAL` 18 | A1, A2, A3, A5 |
| `MOD-INFRAESTRUTURA-CERTIFICADODIGITAL` | `05_modulos/Infraestrutura/CertificadoDigital/spec.md:808-819` | `PC-CERTIFICADODIGITAL` 12 | A1, A2, A4 |
| `MOD-INFRAESTRUTURA-FEEDRSS` | `05_modulos/Infraestrutura/FeedRss/spec.md:677-687` | `PC-FEEDRSS` 11 (prefixo compartilhado com o serviço) | A1, A4 |
| `MOD-INFRAESTRUTURA-GESTAOPERMISSOES` | `05_modulos/Infraestrutura/GestaoPermissoes/spec.md:774-785` | `PC-GESTAOPERMISSOES` 12 | A1, A3, A4 |
| `MOD-INFRAESTRUTURA-MAILAUDIT` | `05_modulos/Infraestrutura/MailAudit/spec.md:639-649` | `PC-MAILAUDIT` 11 | A1, A4 |
| `MOD-INSTITUCIONAL-ASSOCIESE` | `spec.md:792-832` (+ docs) | `PC-ASSOCIESE` 15 | A1–A5 |
| `MOD-INSTITUCIONAL-FALECONOSCO` | `spec.md:695-723`; `cms.md:491-509`; `portal.md:506-521`; `rotas.md:398-407` | `PC-FALECONOSCO` 15 + `CMS` 11 + `PORTAL` 10 + `ROTAS` 8 | A1–A5 |
| `MOD-INSTITUCIONAL-HISTORIA` | `spec.md:785-809`; `cms.md:309-555`; `dados.md:435-446`; `portal.md:511-525`; `rotas.md:395-406` | `PC-HISTORIA` 15 + `CMS` 11 + `DADOS` 8 + `PORTAL` 10 + `ROTAS` 7 | A1–A5 |
| `MOD-LAYOUTS-BASICPAGE` | `spec.md:826-845`; `runtime-e-seo.md:651-667` | `PC-BASICPAGE` 20 + `PC-BASICPAGE-RT` 17 | A1–A5 |
| `MOD-PUBLICACOES-AGENDA` | `spec.md:700-719`; `cms.md:533-542`; `dados.md:522-529`; `portal.md:541-554`; `rotas.md:466-475` | `PC-AGENDA` 42 | A1–A5 |
| `MOD-PUBLICACOES-NOTICIAS` | `spec.md:618-691`; `cms.md:890-901`; `portal.md:687-698` | `PC-NOTICIAS` 20 + `CMS` 12 + `PORTAL` 12 | A1–A5 |
| `MOD-PUBLICACOES-TRABALHECONOSCO` | `spec.md:951-970`; `cms.md:656-668`; `dados.md:674-686`; `portal.md:629-641`; `rotas.md:563-573` | `PC-TRABALHECONOSCO` 70 | A1–A5 |
| `MOD-SUPPORT-NOTICIASIMPORT` | `spec.md:849-862`; `contrato-api.md:662-671` | `PC-NOTICIASIMPORT` 24 | A1, A3, A5 |
| `MOD-SUPPORT-STRUCTURALLANDINGEDITOR` | `spec.md:1006-1022`; `editores-temas-e-assets.md:612-620`; `ui.md:598-611` | `PC-SLE` 40 | A1–A5 |
| `MOD-SUPPORT-SUPPORTCONFIGS` | `spec.md:607-622`; `cms.md:657-671`; `dados.md:640-651`; `rotas.md:533-544` | `PC-SUPPORTCONFIGS` 55 | A1–A5 |
| `MOD-_EXEMPLOS-EXEMPLOAPI` | `05_modulos/Exemplos/ExemploAPI/spec.md:427-436` | `PC-EXEMPLOAPI` 10 | A1, A2 |

#### 3.5 Composições e dados

| Unidade | Checklist | PCs | Verificação |
|---|---|---|---|
| `MOD-LAYOUTS-HOMEPAGE` | `05_modulos/Layouts/Homepage/spec.md:622-644` | `PC-HOMEPAGE` 20 | A1, A3, A4, A5 |
| `MOD-INSTITUCIONAL-SOBRENOS` | documento da Onda 5 (em produção no snapshot) | a definir | A1, A3, A4, A5 |
| `DATA-SEEDS-BASICPAGES` | `07_dados-iniciais/basic-pages.md` (tipo `dados`, sem PCs) | 0 | A2, A5 |
| `DATA-SEEDS-CONTENT` | idem | a definir | A2, A5 |
| `DATA-SEEDS-SLE` | idem | a definir | A2, A5 |

### 4. PCs que não podem virar teste como estão

Os PCs abaixo foram corrigidos em `_qa/review-g2.md` e `_qa/review-g3.md` porque descrevem
resultado que o código não produz. O teste de paridade deve usar a versão corrigida; a
condição antiga **não** é critério de aceite.

| PC | Problema | Versão correta a testar |
|---|---|---|
| `PC-BASICPAGE-13` (versão anterior a G3-01) | prometia 403 do controller; no HTML o middleware responde 302 antes | 302 com `Operação negada...` (HTML) ou 403 genérico (AJAX); o 403 do controller é inalcançável (`_qa/review-g3.md:49-71`) |
| `PC-SLE-08` (versão anterior a G3-08) | espera `compile error` para erro de sintaxe Blade, que só ocorre no `eval` | distinguir `<!-- SLE compile error -->` de `<!-- SLE eval error -->` (`_qa/review-g3.md:173-189`) |
| `PC-SLE-12` (versão anterior a G3-07) | exige limpeza de cache em qualquer submit de escrita | só os cinco submits de configuração limpam; assets não (`_qa/review-g3.md:157-172`) |
| `PC-LSE-16` (versão anterior a G2-04) | exige 21 slugs, mas o boot normal tem 36 (21 padrão + 15 de módulo) | 21 padrões + 15 módulos, ordem de registro (`_qa/review-g2.md:134-153`) |
| `R-GALLERY-05` (índice de rotas) | download documentado como 404 | no legado responde 500 JSON por `abort` capturado; a reimplementação deve responder 404/403 corretos (`_qa/review-g2.md:76-96`) |
| `PC-CORE-AUTH-24` | promete 302 para módulo desativado em qualquer rota CMS | 302 só no prefixo `cms`; prefixos compostos retornam 404 (`_qa/security-g1.md:147-157`) |
| `PC-CORE-AUTH-29` | descreve Gallery anônima como comportamento | deve virar teste de regressão negativa: 401/403/419 (`_qa/security-g1.md:20-46`) |

Além disso, os PCs de segurança `PC-CORE-AUTH-30` e `PC-GALLERY-10` descrevem o legado; na
reimplementação eles são **testes de não-regressão invertidos** (o comportamento inseguro
deve falhar).

### 5. Critério de saída da paridade

Uma unidade fecha quando:

1. Todos os PCs do seu prefixo têm teste mapeado (nome do teste contém o ID do PC).
2. Os testes transversais CT-ROUTES, CT-TOKENS, CT-PERM e CT-CACHE estão verdes.
3. Os PCs de §4 foram substituídos pela versão corrigida.
4. O índice canônico da unidade está consistente: rotas em `rotas.csv`, tabelas em
   `tabelas.csv`, blades em `componentes.csv`, configs em `config.csv`.
5. Não há `[L]` de segurança sem decisão registrada (ver `riscos.md`).

## Particularidades

1. **PCs são por documento, não por unidade.** `PC-CORE-BOOT` aparece em dois documentos
   (boot e roteamento), `PC-CORE-AUTH` em dois, `PC-NOTICIAS` em três. A matriz usa o
   prefixo, mas o teste deve citar o documento que originou o item.
2. **Alguns prefixos são de documento, não de unidade** (`PC-GALLERY-UI`, `PC-MEDIA-UI`,
   `PC-BASICPAGE-RT`, `PC-UI-PORTAL-A11Y`). Eles continuam sendo aceite da unidade dona;
   não criar unidades novas por causa do prefixo.
3. **Há PCs que descrevem o legado inseguro.** `PC-CORE-AUTH-29`, `PC-CORE-AUTH-30`,
   `PC-CORE-AUTH-31` e `PC-GALLERY-10` são registros de comportamento, não metas. A
   reimplementação deve inverter a expectativa e manter o PC como teste negativo.
4. **Os números de rota não fecham.** `_indices/rotas.csv` tem 277 linhas (243 endpoints),
   enquanto `PLANO.md:83,321` fala em 308 chamadas `Route::*`. O aceite usa a tabela de
   rotas, não a contagem bruta; a diferença está registrada em `LG-REIMPL-03`.
5. **Testes do legado não são confiáveis como oráculo.** Há testes fora das suítes e
   PHPUnit 6 sem `assertStringContainsString`
   (`01_arquitetura/11-satelites-build-config.md:226,333`). O oráculo é o contrato
   documentado + código citado, não a suíte antiga.
6. **A matriz de permissões é assimétrica.** `super-admin` ignora o RBAC V2 mas **não**
   entra no grupo Support; o e-mail de suporte ignora tudo, inclusive Support
   (`_indices/permissoes.csv`; `05_modulos/Support/NoticiasImport/spec.md:393`). Um teste
   que trate os dois como equivalentes falha.
7. **PCs de UI não provam acessibilidade.** Os 22 itens de `PC-UI-PORTAL-A11Y`/`COMP`
   cobrem DOM e teclado; contraste e leitor de tela exigem verificação manual
   (`04_ui/04-acessibilidade.md:473-554`).

## Integrabilidade

### Receitas de uso

**1. Derivar um teste de um PC.**

```text
PC-XXX-NN (doc:linha)
  → camada (A1..A5)
  → arrange: fixture mínima + estado de config/flag
  → act: chamada HTTP/serviço/JS
  → assert: status, corpo literal, linha de tabela, arquivo, header, evento DOM
  → nome do teste: test_PC_XXX_NN_resumo
```

**2. Rodar a matriz por unidade.**

```bash
# extrair PCs de um documento e conferir se cada um tem teste
grep -o 'PC-[A-Z0-9-]*-[0-9][0-9]*' 05_modulos/Publicacoes/Agenda/spec.md | sort -u
# no repositório da reconstrução:
grep -rn 'PC-AGENDA-' tests/ | wc -l
```

**3. Priorizar quando o tempo aperta.** Ordem: CT-ROUTES → CT-PERM → CT-TOKENS →
CT-CACHE → CT-UPLOAD → CT-IMPORT → CT-SIGN → PCs de unidade. Os quatro primeiros
invalidam mais PCs por falha.

**4. Aceitar uma unidade com PCs de legado defeituoso.** Marcar o PC como `legado` no
teste, apontar o achado de `_qa/` e testar a versão corrigida; nunca apagar o ID.

## Anti-uso

1. **Contar PCs em vez de testá-los.** 1070 IDs únicos não exigem um teste por ID; muitos
   se repetem entre documentos e vários são afirmações sobre o legado, não metas.
2. **Usar `PC-GALLERY-10` como especificação.** Ele descreve a ausência de auth; copiá-lo
   reproduz a vulnerabilidade SEC-G1-01.
3. **Testar UI por screenshot.** Os contratos de UI são DOM ids, eventos e estados
   (`03_servicos-ui/gallery/ui-contrato.md`); screenshot não prova `pickerId`, callback nem
   teardown.
4. **Tratar 404/500 do legado como contrato.** Gallery download é o caso documentado: o
   código responde 500 onde o doc dizia 404 (`_qa/review-g2.md:76-96`). O contrato correto
   é o status semanticamente correto, não o bug.
5. **Declarar paridade sem os testes transversais.** Um módulo pode passar em todos os PCs
   e ainda assim vazar CSRF, cachear login ou ignorar `owner_module`.

## Lacunas

| ID | Lacuna | Impacto |
|---|---|---|
| `LG-REIMPL-06` | SobreNos e `DATA-SEEDS-CONTENT`/`DATA-SEEDS-SLE` ainda não têm documento no snapshot; Homepage já tem 20 PCs | a matriz §3.5 fica parcialmente sem critério até a Onda 5 fechar |
| `LG-REIMPL-07` | Não há contagem reconciliada de `Route::*` (308) vs rotas indexadas (277) | aceite de rotas usa o CSV; a diferença pode esconder rota não documentada |
| `LG-REIMPL-08` | PCs de acessibilidade não têm método de verificação automática definido | 22 itens dependem de auditoria manual |
| `LG-REIMPL-09` | `_indices/dados-iniciais.csv` vazio | não há PCs de seed deriváveis do índice |
| `LG-REIMPL-10` | `08_matriz/permissoes-e-papeis.md` ainda não publicado | a matriz de CT-PERM usa `_indices/permissoes.csv`, que tem 63 linhas, como fonte provisória |

## Evidências

- PCs: seções “Checklist de paridade” de cada documento, linhas listadas na §3.
- Correções de PCs: `_qa/review-g2.md:76-153`; `_qa/review-g3.md:49-189`.
- Segurança: `_qa/security-g1.md:20-214`.
- Índices: `_indices/rotas.csv`, `tabelas.csv`, `componentes.csv`, `permissoes.csv`,
  `config.csv`, `servicos.csv`.
- Contagem de unidades e arquivos: `00_inventario/manifest.json`; `PLANO.md:83,321`.
- Gate: `PLANO.md:315-325` (G5 exige três reconstruções cegas; ver
  `reconstrucoes-cegas.md`).
