---
id: MATRIZ-FLAGS
tipo: matriz
onda: 5
status: concluido
unidades: []
fontes:
  - V2/Core/Services/ModuleFlagService.php
  - V2/Core/Middleware/ModuleAccessMiddleware.php
  - V2/Core/ViewComposers/SidebarComposer.php
  - V2/layouts/cms/layouts/sidebar.blade.php
  - V2/Modules/
  - config/v2.php
depende_de: [CORE-BOOT, CORE-AUTH, CORE-CMS, MOD-SUPPORT-SUPPORTCONFIGS]
consumidores: [MATRIZ-PERMISSOES, MATRIZ-SINGULARIDADES, REIMPL-ORDEM]
criticidade: alta
dono: Matriz
---

# Flags e navegação: `v2_module_flags`, `ModuleProvider` e a sidebar

Este documento descreve como um módulo aparece (ou não) no menu, como a flag
`v2_module_flags` o desliga e o que acontece com a rota quando ele está
desligado. Também registra o que o `position` faz (nada) e por que a ordem real
do menu é alfabética.

## 1. Efeito

### Ficha de identidade

| Campo | Valor |
|---|---|
| Efeito primário | Monta a sidebar a partir dos 20 `ModuleProvider`, filtra por flag/permissão/grupo e bloqueia as rotas CMS de módulos desativados com 404 (ou redirect em um caso que nunca ocorre). |
| Entrada | 20 `ModuleProvider::getConfig()`; `v2_module_flags` (`module_key`, `active`); perfil V2 do usuário; `config('v2.support.email')`. |
| Saída | `$v2SidebarGroups` (array agrupado) renderizado por `v2-cms::layouts.sidebar`; `404 Modulo desativado.` / JSON `{error, code:404}`; `302 dashboard` em condição não alcançável. |
| Efeitos colaterais | Nenhum em escrita; `ModuleFlagService` mantém cache estático por request; `SystemMaintenanceService::toggleModule` grava a flag e chama `Cache::forget('v2_module_flags')`. |
| Particularidade central | `position` e `subModulesLevel1[].position` existem em todos os providers, mas **não são lidos**: módulos e submenus são ordenados por `title` com `strcasecmp`. |
| Como se integra | `V2ServiceProvider` injeta `group` e publica `v2.modules.configs`; `SidebarComposer` filtra/ordena; `ModuleAccessMiddleware` bloqueia rotas; `SystemMaintenanceService` lista/toggle. |
| Se removido, quebra | O CMS perde o menu dinâmico; os módulos continuam acessíveis por URL (as rotas existem), mas não há como desligá-los nem descobri-los. |
| Superfície pública | `ModuleFlagService::{isActive,filterActive,refresh}`; `ModuleProviderInterface::getConfig`; `SystemMaintenanceService::{getModulesStatus,toggleModule,updateModulesBatch}`. |

### 1.1 Números

| Medida | Valor | Fonte |
|---|---|---|
| `ModuleProvider` descobertos | 20 | `V2/Modules/*/*/ModuleProvider.php` |
| Módulos com `active_pattern` que casa com as rotas | 19 | §3.3 |
| Módulos com pattern que **não** casa | 1 (`SupportConfigs`) | §3.3 |
| Submenus nível 1 declarados | 37 | §3.2 |
| Módulos com 1 submenu (achatado) | 10 | §3.2 |
| Módulos com 2+ submenus | 10 | §3.2 |
| `position` distintos usados | 12 valores para 19 módulos explícitos, com 6 colisões | §3.2 |
| Chaves em `v2_module_flags` (seed) | 8 | §2.1 |
| Chaves que correspondem a um módulo | 4 | §2.1 |
| Módulos sem flag (ativo por ausência) | 16 | §2.1 |
| Endpoints CMS de módulo protegidos por `module.active` | 187 | §3.3 |

## 2. Abordagem

### 2.1 `v2_module_flags`: o que existe no banco

Tabela criada em `SupportConfigs` (`V2/Modules/Support/SupportConfigs/Migrations/2026_06_30_000002_create_v2_module_flags_table.php:33-58`):

| Coluna | Tipo | Regra |
|---|---|---|
| `id` | uuid | PK gerada por `v2_uuid()` |
| `module_key` | varchar(100) | unique |
| `module_title` | varchar(255) | rótulo |
| `active` | boolean | default `true` |
| `module_group` | varchar(50) | default `OUTROS`; usado só na tela de suporte |
| `transaction_snapshot_id` | uuid nullable | não usado no fluxo |
| `created_at/updated_at/deleted_at` | timestamps | soft delete |

Seed (8 chaves, todas `active=true` exceto a última):

| `module_key` | Título | Grupo | Módulo correspondente | Efeito real |
|---|---|---|---|---|
| `noticias` | Módulo de Notícias | CONTEUDO | `Noticias` (match por lowercase) | desliga rotas CMS de Notícias |
| `licitacoes` | Módulo de Licitações | CONTEUDO | **nenhum** | inócuo; aparece na tela de suporte |
| `diario` | Diário Oficial Eletrônico | CONTEUDO | **nenhum** | inócuo |
| `homepage` | Gerenciador da Home Page | CONTEUDO | `Homepage` (lowercase) | desliga rotas CMS da Homepage |
| `layoutbase` | Layout Base (Logos, Contato) | SISTEMA | **nenhum** (o módulo é `StructuralLandingEditor`) | inócuo |
| `galeria` | Galeria de Mídias | CONTEUDO | **nenhum** (`Gallery` é serviço) | inócuo |
| `supportconfigs` | Suporte Técnico e Configs | SISTEMA | `SupportConfigs` (lowercase) | desliga o próprio painel; auto-lockout |
| `trabalheconosco` | Trabalhe Conosco | CONTEUDO | `TrabalheConosco` (lowercase) | **`active=false` desde a migration**; desliga CMS **e** portal |

Evidência: `2026_06_30_000002_create_v2_module_flags_table.php:61-69`;
`V2/Modules/Publicacoes/TrabalheConosco/Migrations/2026_09_03_000001_disable_trabalhe_conosco_module_flag.php:23-47`.

Regra de ausência: `isActive()` tenta match direto, lowercase e `pascalToSnake`;
se não encontra, retorna `true` (`V2/Core/Services/ModuleFlagService.php:72-94`).
Consequência: **16 dos 20 módulos não podem ser desligados pela UI** sem criar
uma chave manual (Associados, Parceiros, Unidade Regional, Associe-se, Fale
Conosco, História, Sobre Nós, BasicPage, Certificado Digital, Feed RSS, Gestão de
Permissões, MailAudit, Notícias Import, SLE, ExemploAPI; além de `Homepage` e
`Notícias` já cobertos).

### 2.2 `ModuleProvider`: o contrato do menu

Campos lidos pelo runtime:

| Campo | Obrigatório? | Uso real | Evidência |
|---|---|---|---|
| `title` | sim | rótulo e **ordenação** | `SidebarComposer.php:85-89` |
| `description` | não | tela de suporte (`getModulesStatus`) | `SystemMaintenanceService.php:103-105` |
| `icon` | não | `v2_cms_icon`; default `bi-box-seam` | `sidebar.blade.php:76`; `SystemMaintenanceService.php:104` |
| `position` | não | **não lido** | `SidebarComposer.php:85-93` |
| `active_pattern` | não | RBAC e destaque do menu | `V2PermissionMiddleware.php:41-46`; `sidebar.blade.php:49-60` |
| `route` | não | link do módulo quando não achatado | `sidebar.blade.php:69` |
| `restricted` | não | oculta no composer e no blade | `SidebarComposer.php:51-57`; `sidebar.blade.php:86` |
| `subModulesLevel1` | não | submenu; se 1 sem sub2, é achatado | `SidebarComposer.php:111-132` |
| `group` | injetado | bloco do menu; `API` oculto; `SUPPORT` restrito | `V2ServiceProvider.php:109`; `SidebarComposer.php:36-49` |

`MailAudit` é o único provider **sem `position` e sem `route`**; o achatamento
copia a rota do único submenu para o módulo (`SidebarComposer.php:121-123`), então
o menu funciona.

### 2.3 `SidebarComposer`: a ordem dos fatores

```text
1. filterActive()                       → remove flag false (ausência = ativo)
2. para cada módulo:
   a. group == API        → unset (nunca aparece)
   b. group == SUPPORT e email != suporte → unset
   c. restricted e email != restricted e !super-admin → unset
   d. email != suporte e !super-admin:
        permissions_v2[módulo] == 'none' → unset
3. agrupa por group
4. usort por title (strcasecmp) dentro do grupo
5. uksort por nome do grupo (strcasecmp)
6. flattenSingleSubmodules(): 1 sub1 sem sub2 → remove o nível
```

Evidência: `V2/Core/ViewComposers/SidebarComposer.php:24-98`.

Consequências:

- A ordem é **alfabética por título**, não por `position`. Exemplo: no grupo
  `Layouts`, “Homepage” vem antes de “Páginas do Portal”; no grupo `Conexoes`,
  “Associados Regionais” antes de “Parceiros Institucionais” e “Unidades
  Regionais”.
- Submenus também são reordenados por título no Blade
  (`sidebar.blade.php:85-87`), anulando `subModulesLevel1[].position`.
- Um módulo com `route` inválida vira `#` (`Route::has`), sem erro.
- O destaque do item usa `explode('|', $active_pattern)`
  (`sidebar.blade.php:49-60`), então `SupportConfigs` **destaca** corretamente
  mesmo tendo um pattern com `|`; o middleware, que não explode, não aplica RBAC.
  Essa é a divergência mais visível entre menu e autorização.

### 2.4 Os 20 módulos no menu

| Módulo | Título | Grupo | `position` | Submenus (L1) | `restricted` | Rota do módulo |
|---|---|---|---|---|---|---|
| `Homepage` | Homepage | Layouts | 0 | 1 (achatado) | — | `v2.homepage.sections` |
| `BasicPage` | Páginas do Portal | Layouts | 1 | 2 | — (sub “Criar Página” é restrito) | `v2.basicpage.index` |
| `Noticias` | Notícias | Publicacoes | 2 | 4 | — | `v2.noticias.cms.index` |
| `Agenda` | Agenda | Publicacoes | 6 | 2 | — | `v2.agenda.cms.index` |
| `TrabalheConosco` | Trabalhe Conosco | Publicacoes | 8 | 2 | — | `v2.trabalhe-conosco.cms.openings` |
| `Associados` | Associados Regionais | Conexoes | 5 | 1 (achatado) | — | `v2.associados.cms.instituicoes.index` |
| `ParceirosInstitucionais` | Parceiros Institucionais | Conexoes | 4 | 1 (achatado) | — | `v2.parceiros-institucionais.cms.index` |
| `UnidadeRegional` | Unidades Regionais | Conexoes | 6 | 1 (achatado) | — | `v2.unidade-regional.cms.index` |
| `AssocieSe` | Associe-se | Institucional | 7 | 3 | — | `v2.associe-se.cms.leads` |
| `FaleConosco` | Fale Conosco | Institucional | 7 | 1 (achatado) | — | `v2.fale-conosco.cms.inbox` |
| `Historia` | História | Institucional | 7 | 3 | — | `v2.historia.cms.milestones.index` |
| `SobreNos` | Sobre Nós | Institucional | 4 | 2 | — | `v2.sobrenos.cms.sections` |
| `CertificadoDigital` | Certificados Digitais | Infraestrutura | 98 | 2 | — | — (sem `route`; submenu) |
| `FeedRss` | Feed RSS | Infraestrutura | 91 | 1 (achatado) | suporte | `v2.feedrss.support.index` |
| `GestaoPermissoes` | Gestão de Permissões | Infraestrutura | 100 | 2 | — | — (sem `route`; submenu) |
| `MailAudit` | Auditoria de E-mails | Infraestrutura | **ausente** | 1 (achatado) | — | **ausente** → copiada do submenu |
| `NoticiasImport` | Importação de Notícias | Support | 90 | 1 (achatado) | — (grupo Support) | `v2.noticiasimport.cms.index` |
| `StructuralLandingEditor` | Editor Estrutural | Support | 91 | 5 | suporte | `v2.structural-editor.editor` |
| `SupportConfigs` | Suporte Técnico | Support | 90 | 1 (achatado) | suporte | `v2.supportconfigs.index` |
| `ExemploAPI` | [DEV] Exemplo API | _Exemplos | 1 | 1 (achatado) | suporte | — (sem `route`; submenu) |

Fonte: cada `ModuleProvider.php` (linhas em §7); contagem de submenus no array.

Colisões de `position`: 4 (`Parceiros`/`SobreNos`), 6 (`UR`/`Agenda`), 7
(`AssocieSe`/`FaleConosco`/`Historia`), 90 (`NoticiasImport`/`SupportConfigs`),
91 (`FeedRss`/`SLE`), 1 (`BasicPage`/`ExemploAPI`). Como o campo não é usado, as
colisões não têm efeito — mas qualquer reimplementação que passe a usá-lo muda a
ordem.

### 2.5 Grupos e o que desaparece

| Grupo | Módulos (ordem alfabética real) | Regra especial |
|---|---|---|
| `Conexoes` | Associados Regionais, Parceiros Institucionais, Unidades Regionais | — |
| `Infraestrutura` | Auditoria de E-mails, Certificados Digitais, Feed RSS, Gestão de Permissões | — |
| `Institucional` | Associe-se, Fale Conosco, História, Sobre Nós | — |
| `Layouts` | Homepage, Páginas do Portal | — |
| `Publicacoes` | Agenda, Notícias, Trabalhe Conosco | — |
| `Support` | Editor Estrutural, Importação de Notícias, Suporte Técnico | só o e-mail de suporte; `super-admin` não vê |
| `_Exemplos` | [DEV] Exemplo API | `restricted` = e-mail de suporte |
| `API` | **nenhum módulo** | se existir, é removido do menu |

Fonte: `V2/Core/ViewComposers/SidebarComposer.php:36-49,72-93`;
`01_arquitetura/10-grupos-e-modulos-vazios.md:34-40`.

## 3. `active_pattern` e permissões

### 3.1 Como o pattern é interpretado

| Consumidor | Interpretação | Consequência |
|---|---|---|
| `V2PermissionMiddleware` | `$request->is($pattern)` com o valor **como está** | `|` é literal; array funciona; string com `|` não casa |
| `SidebarComposer`/Blade (destaque) | `is_array ? array : explode('|', string)` | `SupportConfigs` destaca, embora o middleware não aplique |
| `BasicPage`/flag | prefixo do grupo no provider | não afeta pattern |

Evidência: `V2/Core/Middleware/V2PermissionMiddleware.php:41-46`;
`V2/layouts/cms/layouts/sidebar.blade.php:49-60`.

### 3.2 Cobertura por módulo

| Módulo | Endpoints CMS | Pattern cobre? | Observação |
|---|---|---|---|
| Noticias | 21 | sim | 2 grupos; `RegionalRestrict` em 12 |
| UnidadeRegional | 16 | sim | 3 CSRF/rotas de API |
| BasicPage | 15 | sim | só `cms/paginas-fixas*` |
| SLE | 17 | sim | `cms/infra/structural-editor/*` |
| AssocieSe | 13 | sim | — |
| Associados | 13 | sim | — |
| Historia | 11 | sim | — |
| SupportConfigs | 11 | **não** | string com `|` |
| GestaoPermissoes | 11 | sim | 2 patterns |
| SobreNos | 11 | sim | — |
| TrabalheConosco | 10 | sim | flag vem do provider |
| Parceiros | 9 | sim | — |
| CertificadoDigital | 7 | sim | — |
| Agenda | 6 | sim | — |
| FaleConosco | 4 | sim | — |
| FeedRss | 3 | sim | — |
| Homepage | 3 | sim | — |
| MailAudit | 2 | sim | — |
| NoticiasImport | 2 | sim | — |
| ExemploAPI | 2 | sim | — |

Fonte: `_indices/rotas.csv` + `ModuleProvider.php` de cada unidade.

### 3.3 Flags × rotas: o que é protegido

| Grupo de rota | Recebe `module.active`? | Onde é aplicado | Evidência |
|---|---|---|---|
| CMS de módulo | sim | provider envolve todo `Routes/cms.php` | `V2ServiceProvider.php:303-307` |
| Web de módulo | **não** (exceto Trabalhe Conosco) | provider usa só `web` + cache | `V2ServiceProvider.php:297-301` |
| Web de Trabalhe Conosco | sim | declarado no próprio arquivo | `V2/Modules/Publicacoes/TrabalheConosco/Routes/web.php:4` |
| API de módulo | **não** | provider usa só `api` | `V2ServiceProvider.php:309-313` |
| Rotas de serviço | **não** | provider usa `web`/`api` | `V2ServiceProvider.php:209-230` |

Consequência: desligar `Noticias` **não** desliga `/noticias`; só o CMS. A
exceção é `TrabalheConosco`, cujo portal também responde 404.

### 3.4 O que acontece quando a flag é `false`

`ModuleAccessMiddleware` (`V2/Core/Middleware/ModuleAccessMiddleware.php:40-63`):

| Condição | Resposta |
|---|---|
| `$request->ajax()` ou `wantsJson()` | `404` JSON `{"error":"Modulo desativado: <Nome>","code":404}` |
| `route()->getPrefix() === 'cms'` | `302 v2.cms.dashboard` + flash `Módulo "<Nome>" está desativado. Ative-o em Suporte Técnico > Módulos.` |
| restante | `abort(404, 'Modulo desativado.')` |

**O redirect nunca ocorre para módulos.** O prefixo real das rotas CMS é
`cms/noticias`, `cms/agenda`, `cms/infra/...`; a comparação é estrita a `'cms'`.
Logo, na prática:

- rota CMS de módulo desativado → `404 Modulo desativado.` (HTML) ou JSON 404;
- rota do portal de Trabalhe Conosco → `404`;
- rotas de serviço e API → seguem funcionando (não recebem o middleware).

Evidência: `05_modulos/Support/SupportConfigs/rotas.md:119,289,303`;
`05_modulos/Support/NoticiasImport/spec.md:795-798`.

### 3.5 A tela de suporte e a duplicação de chaves

`SystemMaintenanceService::getModulesStatus()` mescla `array_keys(v2.modules.configs)`
(PascalCase) com `array_keys($flags)` (lowercase):

```php
$allKeys = array_unique(array_merge(array_keys($registeredModules), array_keys($flags)));
```

Evidência: `V2/Modules/Support/SupportConfigs/Services/SystemMaintenanceService.php:78-113`.

Resultado: para `Noticias`/`noticias`, `Homepage`/`homepage`,
`SupportConfigs`/`supportconfigs` e `TrabalheConosco`/`trabalheconosco`, a lista
mostra **duas linhas** (uma para cada grafia). O toggle grava a chave exatamente
como veio; desligar `Noticias` cria `Noticias=false`, que `isActive('Noticias')`
encontra por match direto, mas `getModulesStatus` continua mostrando `noticias`
como ativa. Não há normalização.

Além disso, `toggleModule` chama `Cache::forget('v2_module_flags')`
(`SystemMaintenanceService.php:155-157`), mas `ModuleFlagService` **não usa
`Cache`**: usa `static::$flags` por request (`ModuleFlagService.php:29-66`). Em
processos de longa duração (Octane/queue), a flag fica stale até o processo
reiniciar; em PHP-FPM, cada request relê.

## 4. Particularidades

### 4.1 Contradições medidas

| Contradição | Fato A | Fato B | Efeito |
|---|---|---|---|
| `position` | 20 providers declaram (19 explicitamente) | composer ordena por `title` | campo morto |
| Pattern com `|` | `SupportConfigs` declara `a*|b*` | middleware não explode; sidebar explode | RBAC não aplica; destaque funciona |
| Redirect de flag | comentário promete redirect no CMS | `getPrefix() === 'cms'` nunca é verdadeiro para `cms/<módulo>` | sempre 404 |
| Chaves de flag | 8 seedadas | só 4 casam com módulo | 4 chaves inócuas; 16 módulos sem flag |
| Duplicação de chaves | 4 módulos têm duas grafias | `getModulesStatus` mostra as duas | toggle ambíguo |
| Cache de flag | `toggleModule` esquece `v2_module_flags` | `ModuleFlagService` não usa `Cache` | invalidação sem efeito |
| `restricted` no Blade | composer deixa `super-admin` ver | Blade exige e-mail exato para subitem restrito | `super-admin` vê o módulo, mas não “Criar Página” |
| Grupo API | se existir, some do menu | continua acessível por URL | ocultação não é bloqueio |

### 4.2 Módulos desativados por padrão

| Módulo | Flag | Desde | Efeito |
|---|---|---|---|
| `TrabalheConosco` | `trabalheconosco=false` | migration `2026_09_03_000001` | CMS e portal 404; some do menu; dashboard perde estatísticas |

Todos os outros 19 estão ativos: 4 por flag `true` explícita (Noticias,
Homepage, SupportConfigs e as 4 inócuas) e 15 por ausência de linha.

### 4.3 Anti-uso

- **Não** use `position` para ordenar: o runtime ignora.
- **Não** crie pattern com `|`: use array.
- **Não** espere redirect de módulo desativado: espere 404.
- **Não** desligue `supportconfigs` pela própria tela sem plano de recuperação:
  o painel responde 404 e só um `UPDATE` no banco reativa.
- **Não** desligue `noticias` esperando tirar `/noticias` do ar: o portal não
  recebe `module.active`.
- **Não** confie em `Cache::forget` para propagar flags em workers longos.

## 5. Integrabilidade

### 5.1 Receita: esconder um módulo do menu sem desligar a rota

```php
// ModuleProvider do módulo
'active_pattern' => ['cms/meu-modulo*'],
'restricted' => config('v2.support.email'), // só o suporte vê
```

O composer remove o item; a rota continua acessível para quem souber a URL
(não é autorização).

### 5.2 Receita: desligar um módulo de verdade

```text
1. Escolha a module_key que casa com o nome do módulo (lowercase).
2. POST /cms/infra/support-configs/modules/toggle {module_key: "noticias", active: false}
3. Efeito: 404 nas rotas CMS de Notícias; item some do menu no próximo request.
4. Para o portal, adicione module.active:<Modulo> também no Routes/web.php do módulo.
```

### 5.3 Receita: adicionar um módulo ao menu

```php
// V2/Modules/<Grupo>/<Modulo>/ModuleProvider.php
public function getConfig(): array {
    return [
        'title' => 'Meu Módulo',
        'icon' => 'bi bi-box',
        'position' => 42,                 // decorativo
        'active_pattern' => ['cms/meu-modulo*'],
        'route' => 'v2.meu-modulo.index',
        'subModulesLevel1' => [],         // 0 ou 1 → item direto
    ];
}
```

O grupo vem do diretório; não declare `group` no array (seria sobrescrito em
`V2ServiceProvider.php:109`).

## 6. Lacunas

- `LG-FLAG-01` — não há teste que prove o efeito de cada uma das 8 chaves; a
  análise é por leitura de código.
- `LG-FLAG-02` — não há normalização de `module_key`; a duplicação PascalCase ×
  lowercase na tela de suporte não está documentada no doc de SupportConfigs.
- `LG-FLAG-03` — `ModuleFlagService` não expõe invalidação; não há como forçar
  recarga em runtime sem novo request/processo.
- `LG-FLAG-04` — a ordem visual desejada (posições) não está registrada em
  nenhum documento; o campo `position` sugere uma intenção que o código não
  implementa.
- `LG-FLAG-05` — não há verificação de que todos os 20 módulos tenham pattern
  válido; `SupportConfigs` é a exceção conhecida.

## 7. Evidências

- `V2/Core/Services/ModuleFlagService.php:29-125`;
  `V2/Core/Middleware/ModuleAccessMiddleware.php:40-63`;
  `V2/Core/ViewComposers/SidebarComposer.php:24-132`;
  `V2/layouts/cms/layouts/sidebar.blade.php:1-140`;
  `V2/Core/Providers/V2ServiceProvider.php:85-137,290-341`.
- `V2/Modules/Support/SupportConfigs/Migrations/2026_06_30_000002_create_v2_module_flags_table.php:33-84`;
  `V2/Modules/Publicacoes/TrabalheConosco/Migrations/2026_09_03_000001_disable_trabalhe_conosco_module_flag.php:23-47`.
- 20 `V2/Modules/*/*/ModuleProvider.php`; linhas de configuração:
  `Homepage:40-51`, `BasicPage:24-35`, `Noticias:36-52`, `Agenda:19-31`,
  `TrabalheConosco:12-24`, `Associados:17-29`, `Parceiros:22-34`,
  `UnidadeRegional:17-29`, `AssocieSe:12-24`, `FaleConosco:19-31`,
  `Historia:20-32`, `SobreNos:33-45`, `CertificadoDigital:21-35`,
  `FeedRss:21-33`, `GestaoPermissoes:17-30`, `MailAudit:17-26`,
  `NoticiasImport:22-37`, `SLE:31-44`, `SupportConfigs:83-94`,
  `ExemploAPI:23-35`.
- `_indices/rotas.csv`; `01_arquitetura/08-helpers-composers.md:27-33`;
  `05_modulos/Support/SupportConfigs/spec.md:582-609`.
- Snapshot: 2026-10-21; `git -C sucesu-cms status --porcelain V2` vazio.
