# Registro de Bugs — Detalhamento item a item (continuação)

> Continuação de `17b-DETALHAMENTO-DE-BUGS.md`. Mesma estrutura: severidade,
> onde, o que acontece, impacto, correção e implicação na v7.

---

### BUG-FUNC-022 — `updateSuperBanner` assume exatamente dois super banners
- **Severidade:** **Alta** — a terceira posição sobrescreve a segunda.  |  **Onde:** `v6/app/Http/Controllers/Prefecture/ContentController.php:1431`
- **O que acontece:** `updateSuperBanner` lê e grava sempre as posições 1 e 2 do conjunto de super banners; um terceiro banner sobrescreve o segundo.  |  **Impacto:** O terceiro super banner do portal nunca aparece — ou apaga o segundo.  |  **Correção:** Iterar sobre o conjunto por chave/posição, sem número fixo de posições.  |  **Impacto na v7:** Tabela de banners com `position` livre e ordenação por `position, id`.
### BUG-FUNC-023 — Caminho de upload do super banner divergente
- **Severidade:** **Alta** — o arquivo é gravado em um caminho na criação e lido de outro no update.  |  **Onde:** `v6/app/Http/Controllers/Prefecture/ContentController.php:1460` (update) vs. `:1150` (create)
- **O que acontece:** Na criação o upload vai para `content/super-banner` (`ContentController:1150`); no update o `path` é montado como `storage/content/super-banner/{name}` (`ContentController:1460`) — o mesmo arquivo gravado e lido em caminhos diferentes.  |  **Impacto:** O super banner não é encontrado no update (imagem some do portal) ou é gravado em disco duplicado.  |  **Correção:** Usar um único serviço de upload com o diretório como parâmetro.  |  **Impacto na v7:** `Storage::disk('public')` com um único método de gravação; o `path` gravado é sempre o retorno do storage.
### BUG-FUNC-024 — `update` do e-vacina quebra sem nenhum registro
- **Severidade:** **Alta** — erro fatal (acesso a método de `null`).  |  **Onde:** `v6/app/Http/Controllers/Prefecture/EvacinaController.php:68`
- **O que acontece:** `EvacinaController@update` acessa o resultado de `latest()->first()` sem verificar se há registro; com a base vazia, o acesso a propriedade de `null` derruba a requisição.  |  **Impacto:** Erro fatal ao editar o e-Vacina em uma instalação sem registro — o operador não consegue configurar o módulo.  |  **Correção:** Verificar o `first()` antes de usar, ou `findOrFail()`.  |  **Impacto na v7:** `firstOrFail()` com 404 tratado; nenhum acesso a propriedade de `null`.
### BUG-FUNC-025 — `destroyAll` sempre truthy
- **Severidade:** **Alta** — exclusão em lote responde "sucesso" sem apagar nada.  |  **Onde:** `v6/app/Http/Controllers/Prefecture/FeriadosMunicipaisController.php:149` · `v6/app/Repositories/Repository.php:105`
- **O que acontece:** `Repository::destroyAll()` retorna sempre um valor truthy, mesmo quando não apaga nada; o controller testa `if ($this->repository->destroyAll($request->toDelete))` e emite a mensagem de sucesso.  |  **Impacto:** O operador recebe apagado com sucesso e os registros permanecem — a tela mostra um estado que não corresponde ao banco.  |  **Correção:** `destroyAll` deve retornar a contagem de registros afetados (ou `false` quando 0).  |  **Impacto na v7:** Comando de soft delete em lote transacional, com retorno do número de linhas afetadas.
### BUG-FUNC-026 — `SurveyRepository::upInsert()` não retorna o model
- **Severidade:** **Alta** — o chamador recebe `null` e opera sobre objeto inexistente.  |  **Onde:** `v6/app/Repositories/Eloquent/Prefecture/Survey/SurveyRepository.php:41`
- **O que acontece:** `SurveyRepository::upInsert()` grava a linha mas não retorna o model criado, ao contrário do contrato dos demais repositórios.  |  **Impacto:** O chamador recebe `null` e qualquer acesso a propriedade do model derivado quebra em tempo de execução.  |  **Correção:** Retornar o model criado, como os demais repositórios.  |  **Impacto na v7:** Repositórios com contrato único (`insert(): Model`), testado.
### BUG-FUNC-027 — `destroy()` ignora o `DeleteRequest` e devolve 500
- **Severidade:** **Alta** — id inválido causa erro fatal em vez de 404.  |  **Onde:** `v5/app/Http/Controllers/Prefecture/Works/WorkController.php:237`
- **O que acontece:** `WorkController@destroy()` não instancia o `DeleteRequest` (que existe e valida o id e a justificativa); com id inválido, o controller desreferencia um model inexistente.  |  **Impacto:** HTTP 500 ao excluir com id inválido, e a validação de exclusão nunca é exercitada.  |  **Correção:** Instanciar o `DeleteRequest` no controller e usar `findOrFail()`.  |  **Impacto na v7:** Form Request por ação (`DestroyXRequest`) com `findOrFail()`.
### BUG-FUNC-028 — `DeleteRequest` reprovaria sempre (código morto)
- **Severidade:** **Alta** — a validação de exclusão nunca é acionada; a exclusão não exige justificativa.  |  **Onde:** `v5/app/Http/Requests/Prefecture/Works/DeleteRequest.php:37` (`'id' => $this->route('obra')` — o parâmetro de rota é `{id}`)
- **O que acontece:** O `DeleteRequest` faz `'id' => $this->route('obra')`, mas o parâmetro de rota é `{id}` — a validação é código morto; e como o controller não o instancia (BUG-FUNC-027), a exclusão nunca exige justificativa.  |  **Impacto:** A exclusão de uma obra ocorre sem a justificativa obrigatória definida pelo próprio pedido de validação.  |  **Correção:** Corrigir o parâmetro de rota e instanciar o request no controller.  |  **Impacto na v7:** Justificativa como campo obrigatório do próprio registro, validada por Form Request.
### BUG-FUNC-029 — `Route::resource('licitacao')` gera rota `show` inexistente
- **Severidade:** **Alta** — `GET /cms/licitacao/{id}` responde 500.  |  **Onde:** `v6/routes/basepro.php:184`
- **O que acontece:** `Route::resource('licitacao')` gera a rota `show` para `BiddingController@show`, que não existe no controller.  |  **Impacto:** HTTP 500 ao abrir a tela de detalhe de uma licitação no CMS.  |  **Correção:** Implementar `show()` ou usar `Route::resource(..., ['except' => ['show']])`.  |  **Impacto na v7:** Rotas explícitas; nenhuma rota gerada sem método correspondente.
### BUG-FUNC-030 — Filtro por órgão ignorado
- **Severidade:** **Alta** — o formulário mostra o filtro, a query não aplica.  |  **Onde:** o select existe em `v6/resources/views/portal/layouts/modelo1/licitacao/filtro.blade.php:131` (`Form::select('contents_id', $secretarias, ...)`), mas `FilterBiddings::filter()` — `v6/app/Http/Filter/FilterBiddings.php:22` — tem 12 blocos `when()` e **nenhum** para `contents_id`.
- **O que acontece:** o formulário de Licitações do portal oferece o select de órgão/secretaria, mas o filtro correspondente não existe no `FilterBiddings` — o parâmetro viaja e é ignorado.
- **Impacto:** o cidadão escolhe um órgão e recebe o resultado de **todos** os órgãos, sem qualquer aviso na tela.  |  **Correção:** incluir o bloco `when(($request->contents_id), ...)` em `FilterBiddings::filter()` e validar que a coluna existe no escopo da query.  |  **Impacto na v7:** filtros de formulário e parâmetros da query com o mesmo nome, cobertos por teste de feature que compara o resultado filtrado com o não filtrado.

### BUG-FUNC-031 — Listagem de licitações antigas quebra (500)
- **Severidade:** **Alta** — a rota principal do módulo retorna 500.  |  **Onde:** `v6/app/Http/Controllers/Portal/BiddingOldController.php:54` (`ModalitiesBiddingsLegacys::pluck(...)` sobre `biddings_legacys_modalities`, tabela que nenhuma migration cria)
- **O que acontece:** `BiddingOldController` executa `ModalitiesBiddingsLegacys::pluck('name','id')` e `JurisdictionalBiddingsLegacys::pluck('title','id')` sobre `biddings_legacys_modalities` e `biddings_legacys_jurisdictional` — tabelas que nenhuma migration cria e que não existem na base.  |  **Impacto:** A rota principal do módulo de licitações antigas retorna 500 — o acervo público fica fora do ar.  |  **Correção:** Criar as tabelas de catálogo (ou removê-las do controller e usar valores literais).  |  **Impacto na v7:** Tabelas de catálogo como migrations reais; o controller nunca consulta tabela inexistente.
### BUG-FUNC-032 — Captcha renderizado mas nunca validado
- **Severidade:** **Alta** — proteção contra spam inativa na Ouvidoria.  |  **Onde:** `v6/app/Http/Controllers/Portal/OmbudsmanController.php:114` (a regra `'g-recaptcha-response' => 'required|recaptcha'` está **comentada**)
- **O que acontece:** O formulário renderiza o widget do reCAPTCHA, mas a regra de validação está comentada no controller (a linha `'g-recaptcha-response' => 'required|recaptcha'` está precedida de `//`).
- **Impacto:** A Ouvidoria aceita envios automatizados — volume de manifestações falsas e base poluída.  |  **Impacto na v7:** Validação de captcha no servidor, com limite de taxa por IP.  |  **Correção:** reativar a regra ou remover o widget do formulário (não há os dois).
### BUG-FUNC-033 — Comparação de modalidade com acento que nunca casa
- **Severidade:** **Alta** — o registro cai em modality errada.  |  **Onde:** `v6/app/Http/Controllers/Portal/OmbudsmanController.php:176`
- **O que acontece:** O controller compara `$ombudsman->modality == 'Anonimo'` (com acento) enquanto o formulário grava a modalidade conforme o valor enviado, sem acento.  |  **Impacto:** A manifestação não é classificada como anônima — a conferência pública de identidade é quebrada.  |  **Correção:** Comparar com o valor canônico gravado, ou usar um enum de modalidade.  |  **Impacto na v7:** Enum `Modality` com valor persistido estável, sem acento no valor do banco.
### BUG-FUNC-034 — Consulta por protocolo expõe dados pessoais em claro
- **Severidade:** **Alta** — o protocolo é curto e o endpoint devolve nome, CPF e descrição do pedido a quem o chute.  |  **Onde:** `v6/app/Http/Controllers/Portal/OmbudsmanController.php:154`
- **O que acontece:** A rota de consulta por protocolo devolve o modelo completo da manifestação (nome, CPF, descrição, anexos) a quem apresenta o número de protocolo.
- **Impacto:** O protocolo tem espaço de busca pequeno; com Knowledge Based Authentication o cidadão pode ser autentificado por outros dados, expondo dado pessoal de terceiro.  |  **Impacto na v7:** Endpoint de consulta com KBA (CPF + protocolo) e resposta mascarada por padrão.  |  **Correção:** exigir o CPF junto do protocolo ou mascarar os dados na resposta.
### BUG-FUNC-035 — Resposta AJAX do Comitê renderiza o partial de Publicações
- **Severidade:** **Alta** — o HTML retornado é de outro módulo.  |  **Onde:** `v6/app/Http/Controllers/Portal/ComiteDeInvestimentosController.php:91`
- **O que acontece:** A resposta AJAX do Comitê de Investimentos renderiza o partial de Publicações — o HTML devolvido não corresponde ao módulo solicitado.  |  **Impacto:** A listagem do Comitê exibe cards com estrutura e classes de Publicações — quebra de layout e conteúdo errado.  |  **Correção:** Renderizar o partial do próprio módulo.  |  **Impacto na v7:** Um partial por módulo; resposta JSON com Resource, sem HTML montado à mão.
### BUG-FUNC-036 — Slug inexistente na URL do portal gera erro 500
- **Severidade:** **Alta** — idem BUG-FUNC-013, no módulo de Publicações.  |  **Onde:** `v6/app/Http/Controllers/Portal/PublicationController.php:157`
- **O que acontece:** `PublicationController` acessa o model sem verificar se ele existe — o mesmo padrão do BUG-FUNC-013.  |  **Impacto:** HTTP 500 para slug inexistente no portal de Publicações.  |  **Correção:** `firstOrFail()` antes de qualquer acesso.  |  **Impacto na v7:** `firstOrFail()` uniforme.
### BUG-FUNC-037 — Filtros da API de órgãos descartados
- **Severidade:** **Alta** — `whereRaw` sem reatribuição: o filtro é aplicado e jogado fora.  |  **Onde:** `v6/app/Http/Controllers/Swagger/V1/Portal/OrgaoController.php:91`
- **O que acontece:** `OrgaoController` (Swagger) monta o filtro com `whereRaw(...)` sem reatribuir o resultado à query — o filtro é aplicado e jogado fora.  |  **Impacto:** A API de órgãos devolve o conjunto completo, ignorando os parâmetros de filtro enviados pelo integrador.  |  **Correção:** Reatribuir o resultado (`$query = $query->whereRaw(...)`) ou usar `when()`.  |  **Impacto na v7:** Query builder com `when()` por parâmetro; API Resource com filtros documentados.
### BUG-FUNC-038 — Precedência AND/OR na view `atas_e_pontos`
- **Severidade:** **Alta** — a view devolve linhas que não deveriam constar.  |  **Onde:** `v6/database/migrations/2025_01_20_122421_create_view_atas_e_pontos_table.php:30`
- **O que acontece:** A view `atas_e_pontos` combina condições com `AND` e `OR` sem parênteses; a precedência do SQL faz a condição de módulo se aplicar apenas à primeira alternativa.
- **Impacto:** A listagem de Atas e Pontos devolve linhas que não pertencem ao filtro pedido.  |  **Impacto na v7:** Evitar views SQL para regras de negócio; se mantidas, com parênteses explícitos e teste.  |  **Correção:** parenthesizar as condições OR na definição da view.
### BUG-FUNC-039 — `ManualController@update` sem validação
- **Severidade:** **Alta** — escrita irrestrita na tabela do manual.  |  **Onde:** `v6/app/Http/Controllers/Prefecture/ManualController.php:110`
- **O que acontece:** `ManualController@update` recebe o `Request` cru e grava sem nenhum Form Request — não há validação de campos, tipos ou tamanho.
- **Impacto:** Gravação irrestrita no conteúdo do manual do sistema, que é servido ao usuário final.  |  **Impacto na v7:** Form Request com regras declarativas e `authorize()`.  |  **Correção:** Form Request com regras por tipo de manual.
### BUG-FUNC-040 — Rota pública de ativação/desativação de itens de menu
- **Severidade:** **Alta** — o menu do portal é alterável por anônimo.  |  **Onde:** `v6/routes/web.php:232`
- **O que acontece:** A rota de ativação/desativação de itens de menu está em `v6/routes/web.php:232` — no grupo público do portal, sem `check.logged` nem `check.route.permission`.
- **Impacto:** O menu do portal pode ser alterado por qualquer visitante anônimo.  |  **Impacto na v7:** Escrita apenas por rota autenticada; nenhum método de escrita exposto no portal.  |  **Correção:** mover para o grupo do CMS com `auth`.
### BUG-FUNC-041 — Layout `default` quebra a página `/glossario`
- **Severidade:** **Alta** — HTTP 500 no glossário quando `configurations.layout = default`.  |  **Onde:** `v6/app/Http/Controllers/Portal/GlossaryController.php:23` (renderiza `portal.layouts.{template}.transparencia.glossario`, que não existe no template `default`) · `v6/app/Http/Controllers/Portal/PortalController.php:391` e `:400` (busca global → `glossario.show`, inexistente em `default` e `newsbit`)
- **O que acontece:** `GlossaryController@index` renderiza `portal.layouts.{template}.transparencia.glossario`, mas o template `default` só tem `pages/contents/glossario/show.blade.php` — não existe a pasta `transparencia`. O mesmo vale para a busca global, que mapeia a categoria 11 para `glossario.show`, inexistente em `default` e `newsbit`.
- **Impacto:** HTTP 500 no glossário e na busca global em 2 dos 8 templates disponíveis.  |  **Impacto na v7:** Componente de glossário fora do eixo de template, com um único template.  |  **Correção:** views iguais em todos os templates, ou componente único fora do eixo de template.
### BUG-FUNC-042 — View SQL de notícias exige categoria pai `id = 1`
- **Severidade:** **Alta** — o módulo fica inoperante sem criar subcategorias com pai 1.  |  **Onde:** `v6/database/migrations/2024_07_01_094338_udate_noticias_view.php:37` · `v6/app/Http/Model/Prefecture/Content.php:216`
- **O que acontece:** A view SQL `noticias` e o escopo do portal filtram `cc.category_contents_id = 1` — um id interno fixo. A sequência `category_contents_id_seq` é compartilhada por todos os módulos de conteúdo, então o id 1 não é garantido em bases novas.
- **Impacto:** Sem criar manualmente subcategorias de Notícias com pai 1, a listagem pública de notícias fica vazia.  |  **Impacto na v7:** Filtros por slug ou por FK; nenhum id interno fixo em view SQL.  |  **Correção:** filtrar por `category_contents.slug = 'noticias'` em vez do id fixo.
### BUG-FUNC-043 — Itens desativados são publicados no portal
- **Severidade:** **Alta** — conteúdo desativado aparece para o cidadão.  |  **Onde:** `v6/app/Http/Traits/GetInformationsPrefectureTrait.php:28`
- **O que acontece:** `GetInformationsPrefectureTrait::getContentByCategory()` busca o conteúdo da categoria sem filtrar `status` — perguntas desativadas continuam publicadas.  |  **Impacto:** Conteúdo desativado pelo operador continua visível ao cidadão.  |  **Correção:** Filtrar `status = true`.  |  **Impacto na v7:** Escopo de publicação único na camada de consulta.
### BUG-FUNC-044 — Pauta não publicada aparece no portal
- **Severidade:** **Alta** — idem, no módulo Pauta do Dia.  |  **Onde:** `v6/app/Http/Controllers/Portal/PautaDoDiaController.php:15`
- **O que acontece:** `PautaDoDiaController` busca o conteúdo por `slug = 'pauta-do-dia'` sem filtrar `status` nem `deleted_at`.  |  **Impacto:** Pauta não publicada aparece no portal.  |  **Correção:** Filtrar `status = true` e `deleted_at IS NULL`.  |  **Impacto na v7:** Escopo de publicação único.
### BUG-FUNC-045 — `config.json` obrigatório no boot
- **Severidade:** **Alta** — sem o arquivo, a aplicação não sobe (falha no `config/app.php`).  |  **Onde:** `v6/config/app.php:6` (`file_get_contents(base_path()."/config.json")` sem checagem) · `v6/config/mail.php:3` · `v6/config/recaptcha.php:3`
- **O que acontece:** `config/app.php` faz `file_get_contents(base_path()."/config.json")` e `json_decode()` sem verificar a existência do arquivo; `config/mail.php` e `config/recaptcha.php` fazem o mesmo.
- **Impacto:** Sem o `config.json` (não versionado, gerado pelo CMS) a aplicação não sobe — erro fatal no bootstrap, sem mensagem clara.  |  **Impacto na v7:** Zero arquivo de configuração gerado em runtime; tudo em `.env` e `config/` versionado.  |  **Correção:** mover para `.env` + `config/*.php`; ou falhar com mensagem clara e checagem de existência.
### BUG-FUNC-046 — Sobreposição literal entre dois módulos de configuração
- **Severidade:** **Alta** — `configuracoes-da-instituicao` e `outras-configuracoes` implementam a mesma tela sobre a mesma tabela.  |  **Onde:** `v6/app/Http/Controllers/Prefecture/ConfigurationController.php:59` e `:228` e `:423`
- **O que acontece:** `configuracoes-da-instituicao` e `outras-configuracoes` implementam a mesma tela sobre a mesma tabela `configurations`, com os mesmos métodos (`index`, `update`, `editByCategory`) — a distinção é só o slug da categoria.  |  **Impacto:** Duas telas idênticas com nomes diferentes; correção aplicada em uma não aparece na outra.  |  **Correção:** Unificar em um único controller com o slug da categoria como parâmetro de rota.  |  **Impacto na v7:** Uma tela de configuração por categoria, dirigida por rota.
### BUG-FUNC-047 — Composer `$modulos` ausente nas telas do CMS
- **Severidade:** **Alta** — a sidebar do AdminLTE recebe `$modulos` apenas no dashboard.  |  **Onde:** `v6/app/Providers/ModulosServiceProvider.php:26` (registro) e `:47` (escopo restrito) · `v6/resources/views/vendor/adminlte/page.blade.php:225` (uso)
- **O que acontece:** `ModulosServiceProvider` registra o composer de `$modulos` restrito ao dashboard; a view `vendor/adminlte/page.blade.php:225` usa `$modulos` em todas as telas do CMS.
- **Impacto:** telas do CMS renderizam a sidebar sem o menu de módulos.  |  **Correção:** Registrar o composer para todas as views do CMS.  |  **Impacto na v7:** Composable de layout do CMS com o menu derivado do registry de módulos.
### BUG-FUNC-048 — `Menu::menuItems()` declarado como `belongsTo`
- **Severidade:** **Alta** — relação invertida; devolve o pai no lugar dos filhos.  |  **Onde:** `v6/app/Http/Model/Prefecture/Menu.php:30`
- **O que acontece:** `Menu::menuItems()` está declarado como `belongsTo(MenuItem::class, 'menus_id', 'id')` — uma relação `belongsTo` retorna um model, mas o nome e o uso exigem a coleção de itens.  |  **Impacto:** O menu exibe um único item (ou o item errado) em vez da lista de itens.  |  **Correção:** `hasMany(MenuItem::class, 'menus_id')`.  |  **Impacto na v7:** Relações Eloquent com semântica correta, cobertas por teste.
### BUG-FUNC-049 — `MenuItem` sem `SoftDeletes`
- **Severidade:** **Alta** — a coluna `deleted_at` existe, mas o model não filtra; exclusões lógicas não funcionam.  |  **Onde:** `v6/app/Http/Model/Prefecture/MenuItem.php:18`
- **O que acontece:** O model `MenuItem` tem o `use SoftDeletes` comentado (`v6/app/Http/Model/Prefecture/MenuItem.php:18`), embora a tabela tenha a coluna `deleted_at`.  |  **Impacto:** Itens de menu excluídos continuam no banco e continuam aparecendo no portal; o soft delete nunca é exercitado.  |  **Correção:** Ativar `SoftDeletes` e preencher `deleted_at` na exclusão.  |  **Impacto na v7:** Soft delete como padrão em toda entidade de conteúdo.
### BUG-FUNC-050 — Slug duplicado em `modulos`
- **Severidade:** **Alta** — `pluck('status','slug')` resolve a chave duplicada em favor da última linha (`status = false`), desabilitando o módulo Logs do Sistema no menu.  |  **Onde:** `v6/database/migrations/2024_11_01_150206_create_modulos_table.php:65` e `:217` (mesmo slug `logs-do-sistema`) · `:121` (`diario-ofifical`) e `:247` (`diario-oficial`)
- **O que acontece:** A migration de módulos insere `logs-do-sistema` duas vezes (bloco Configurações com `status = 1`; bloco Sidebar com `status = 0`) e `diario-ofifical`/`diario-oficial` em pares; não há `UNIQUE` em `modulos.slug`. O `pluck('status','slug')` do `ModulosServiceProvider` resolve a chave duplicada em favor da última linha.
- **Impacto:** 58 linhas produzem 57 chaves: o módulo Logs do Sistema fica efetivamente desabilitado no menu, apesar de a linha id 7 estar com `status = true`.  |  **Impacto na v7:** `modulos.slug` com `UNIQUE`; migration idempotente que reconcilie o registry.  |  **Correção:** consolidar as linhas, repontar `resources.modulo_id` e criar `UNIQUE (slug)`.
### BUG-FUNC-051 — Slug do módulo gravado como `detaques` (typo)
- **Severidade:** **Alta** — consumidores de `modulos.slug` (permissões, URLs, relatórios) não encontram o módulo.  |  **Onde:** `v6/database/migrations/2024_11_01_150206_create_modulos_table.php:355`
- **O que acontece:** O registro id 55 é inserido com `name = 'Destaques'` e `slug = 'detaques'` (typo), enquanto o recurso e o menu usam `destaques`.
- **Impacto:** Consumidores de `modulos.slug` (permissões, URLs, relatórios) não encontram o módulo.  |  **Impacto na v7:** Slug único e validado, com `UNIQUE` em `modulos.slug`.  |  **Correção:** migration idempotente corrigindo para `destaques`, avaliando as permissões já concedidas.
### BUG-FUNC-052 — `modulos.status = 0` desabilita módulos no menu
- **Severidade:** **Alta** — o módulo só é acessível por URL direta (regressão frente à v5, onde bastava `has_access`).  |  **Onde:** `v6/database/migrations/2024_11_01_150206_create_modulos_table.php:356` (destaques) e `:41` (lgpd) · `v6/app/Providers/MenuServiceProvider.php:299`
- **O que acontece:** O `MenuServiceProvider` só adiciona o item de menu se `has_access(...)` e `$menu->modulo->status` forem verdadeiros; vários módulos nascem com `status = 0` (destaques id 55, lgpd id 3, comitê de investimentos). Na v5, o item aparecia apenas por `has_access`.
- **Impacto:** Módulos existentes e operáveis somem do menu do CMS — o operador só os alcança digitando a URL.  |  **Impacto na v7:** Registry de módulos como feature flag explícito, documentado.  |  **Correção:** decisão de negócio sobre quais módulos ficam operáveis; na v7, o registry é feature flag explícito com padrão ligado.
### BUG-FUNC-053 — Cache de módulos sem invalidação
- **Severidade:** **Alta** — alterar `modulos.status` só surte efeito após `php artisan cache:clear`.  |  **Onde:** `v6/app/Providers/ModulosServiceProvider.php:22` (`Cache::rememberForever`)
- **O que acontece:** `ModulosServiceProvider` usa `Cache::rememberForever('modulos', ...)` — sem TTL; e o `ModulosController@update` não invalida o cache.
- **Impacto:** Alterar `modulos.status` só surte efeito após `php artisan cache:clear` — o operador muda o status e nada acontece.  |  **Impacto na v7:** Cache com invalidação por evento; nada de `rememberForever` sem gatilho.  |  **Correção:** `Cache::forget('modulos')` no `ModulosController@update`, ou cache com TTL curto.
### BUG-FUNC-054 — `Layout` model com `'kay'` no `$fillable`
- **Severidade:** **Alta** — o campo real é `key`, que **não** é mass-assignable; por isso todo o código usa `Layout::where('key', …)->update([...])` em vez de `update()`.  |  **Onde:** `v6/app/Http/Model/Prefecture/Layout.php:18`
- **O que acontece:** O `$fillable` do model `Layout` traz `'kay'` — o campo real é `key`. O `key` não é mass-assignable, e é por isso que todo o código usa `Layout::where('key', ...)->update([...])` em vez de `update()`.
- **Impacto:** `Layout::create([...])` ignora silenciosamente a chave; o padrão de uso anômalo se espalha pelo código.  |  **Impacto na v7:** `$fillable` revisado e testado — nenhum typo sobrevive a um teste de criação.  |  **Correção:** corrigir para `'key'`.
### BUG-FUNC-055 — `Route::getCurrentRoute()` pode retornar `null`
- **Severidade:** **Alta** — `explode('.', null)` é erro em PHP 8.1+ e comportamento indefinido em 7.2.  |  **Onde:** `v6/app/Http/Middleware/CheckRoutePermission.php:59`
- **O que acontece:** `CheckRoutePermission` faz `Route::getCurrentRoute()->getName()`; rotas sem nome (ex.: `POST password/reset`, `GET /logout`) fazem `getName()` retornar `null`, e `explode('.', null)` é erro em PHP 8.1+.
- **Impacto:** Rota sem nome quebra o middleware de autorização — no Laravel moderno, erro fatal em produção.  |  **Impacto na v7:** Middleware tipado, com guarda para nome de rota ausente.  |  **Correção:** guarda `if (!$access) return false;` antes do `explode`.
### BUG-FUNC-056 — `array_where()` removido do core do Laravel
- **Severidade:** **Alta** — ruptura obrigatória na migração do middleware de autorização.  |  **Onde:** `v6/app/Http/Middleware/CheckRoutePermission.php:75` · helper em `v6/vendor/laravel/framework/src/Illuminate/Support/helpers.php:315`
- **O que acontece:** O helper `array_where()` existe no Laravel 5.5 (`v6/vendor/laravel/framework/src/Illuminate/Support/helpers.php:315`) mas foi removido do core a partir do Laravel 6 (migrado para o pacote `laravel/helpers` e removido do core no Laravel 11).
- **Impacto:** A chamada quebra em qualquer Laravel moderno — ponto de ruptura obrigatória na migração do middleware de autorização, justamente o componente mais sensível do CMS.  |  **Impacto na v7:** Reimplementação no middleware novo, que já não depende de comparação por nome de rota (Policies).  |  **Correção:** `array_filter($accessArr, fn($v,$k) => …, ARRAY_FILTER_USE_BOTH)`.  |  **v7:** reimplementar a semântica (trivial) no middleware novo, baseado em Policies.
### BUG-FUNC-057 — Funções globais só em `autoload-dev`
- **Severidade:** **Alta** — bug latente que só aparece em deploy com `--no-dev`: as 27 funções globais (`mask()`, `getActualLayout()`, `slug_fix()`, `file_exist()`, `getSizeImage()`…) **não existem** em produção.  |  **Onde:** `v6/composer.json:62` (`autoload-dev.files`) · consumo em `v6/resources/views/components/weather-box.blade.php:111` (`getActualLayout()`)
- **O que acontece:** `v6/composer.json` registra `app/Helpers/functions.php` em `autoload-dev.files` — em produção (`composer install --no-dev`) as 27 funções globais (`mask()`, `getActualLayout()`, `slug_fix()`, `file_exist()`, `getSizeImage()`) não existem. `v6/resources/views/components/weather-box.blade.php:111` chama `getActualLayout()`.
- **Impacto:** Bug latente que só aparece em deploy com `--no-dev`: undefined function em produção, em uma view do portal.  |  **Impacto na v7:** Zero função global; helpers como classes estáticas ou invólucros de serviço.  |  **Correção:** mover para `autoload.files` (ou para classes com DI).
### BUG-FUNC-058 — `AuthServiceProvider` referencia policy inexistente
- **Severidade:** **Alta** — placeholder do skeleton.  |  **Onde:** `v6/app/Providers/AuthServiceProvider.php:16` (`'App\Model' => 'App\Policies\ModelPolicy'`); não existe `v6/app/Policies/`.
- **O que acontece:** `AuthServiceProvider::$policies` mapeia `'App\Model' => 'App\Policies\ModelPolicy'`; não existe `v6/app/Policies/` nem a classe referenciada.
- **Impacto:** Placeholder do skeleton — passa despercebido e dá falsa sensação de que a autorização está configurada.  |  **Impacto na v7:** Policies reais, uma por recurso, com testes.  |  **Correção:** remover o placeholder ou mapear as policies reais.
### BUG-FUNC-059 — `Exception Handler` engole a exceção
- **Severidade:** **Alta** — a exceção é anexada à resposta JSON enviada ao navegador: vaza stack trace, caminho de arquivo e credencial de conexão do banco.  |  **Onde:** `v6/app/Exceptions/Handler.php:51`
- **O que acontece:** O handler responde JSON com `'message' => '...' . $exception` — a exceção (stack trace, caminho de arquivo, credencial de conexão) é concatenada na mensagem enviada ao navegador.  |  **Impacto:** Vazamento de stack trace e de DSN do banco para qualquer visitante.  |  **Correção:** Mensagem genérica ao cliente; `report($e)` para o log.  |  **Impacto na v7:** Handler único com `ProblemDetails` e sem vazamento de detalhe interno.
### BUG-FUNC-060 — `TokenMismatchException` vira redirect de logout
- **Severidade:** **Alta** — um POST sem CSRF válido **desloga** o operador em vez de mostrar erro.  |  **Onde:** `v6/app/Exceptions/Handler.php:75` · `v5/app/Exceptions/Handler.php:77`
- **O que acontece:** `Handler` converte `TokenMismatchException` (CSRF) em redirect para `logout`.
- **Impacto:** Um POST sem token CSRF válido desloga o operador em vez de mostrar sessão expirada — perda de trabalho em formulários longos.  |  **Impacto na v7:** Página de expiração de sessão; o token CSRF rotaciona por requisição.  |  **Correção:** responder 419 com página de "sessão expirada" e reexibir o formulário.
### BUG-FUNC-061 — Recurso `manual-usuario` só é criado por seeder opcional
- **Severidade:** **Alta** — em instalações que não rodam `ExtrasSeeder`, o módulo fica inacessível a qualquer perfil que não seja `super-admin`.  |  **Onde:** `v6/database/seeds/ManualDoSistemaResourceSeeder.php:16` · `v6/database/seeds/ExtrasSeeder.php:37` · `v6/database/seeds/DatabaseSeeder.php:27`
- **O que acontece:** O slug `manual-usuario` aparece apenas em `ManualDoSistemaResourceSeeder` (chamado só por `ExtrasSeeder`); o `DatabaseSeeder` chama `ResourcesTableSeeder`, que não inclui esse slug.  |  **Impacto:** Em instalações que não rodam `ExtrasSeeder`, o módulo fica inacessível a qualquer perfil que não seja `super-admin`.  |  **Correção:** Mover o registro para `ResourcesTableSeeder` (idempotente) ou rodar o seeder no fluxo padrão.  |  **Impacto na v7:** Recursos e permissões criados por migration, não por seeder opcional.
### BUG-FUNC-062 — Módulo Novidades sem resource/permissão concedível
- **Severidade:** **Alta** — só `super-admin` opera.  |  **Onde:** `v6/app/Http/Controllers/Admin/ChangeLogController.php:16`
- **O que acontece:** `ChangeLogController` (módulo Novidades) não tem resource correspondente em `resources` nem permissão concedível; só o papel `super-admin` opera.  |  **Impacto:** O módulo é inoperável para os demais perfis, mesmo com rota e tela existentes.  |  **Correção:** Cadastrar o resource e as permissões `novidades.*`.  |  **Impacto na v7:** Todo módulo do CMS tem resource e 4 verbos de permissão, criados por migration.
### BUG-FUNC-063 — `catch` vazio na conversão da data de versão
- **Severidade:** **Alta** — data inválida vira data de hoje silenciosamente.  |  **Onde:** `v6/app/Http/Controllers/Admin/ChangeLogController.php:46`
- **O que acontece:** A conversão da data de versão tem `catch (\Exception $e) {}` — o corpo do catch está vazio.  |  **Impacto:** Uma data de versão inválida vira silenciosamente a data de hoje, sem qualquer registro.  |  **Correção:** `catch` com `report($e)` e valor de fallback explícito.  |  **Impacto na v7:** Parsing de data com exceção explícita; nada de catch vazio.
### BUG-FUNC-064 — Reordenação por drag-and-drop morta
- **Severidade:** **Alta** — a ordenação das redes sociais no CMS é inoperante.  |  **Onde:** `v6/resources/views/prefecture/contents/rede-social/index.blade.php:58` (coleta `.item-list`) · o datatable do módulo não existe em `v6/public/assets/js/custom/contents/`
- **O que acontece:** O JS da listagem coleta os elementos `.item-list`, mas apenas banners, acesso-rápido, super-banners e transparência-fiscal aplicam essa classe via `createDragglabeButton(..., 'item-list')`; o datatable do módulo de redes sociais não existe em `v6/public/assets/js/custom/contents/`.
- **Impacto:** A ordenação das redes sociais no CMS é inoperante — o operador arrasta e nada acontece.  |  **Impacto na v7:** Componente único de ordenação por drag-and-drop, reaproveitado por todos os módulos de conteúdo.  |  **Correção:** componente único de ordenação reaproveitado por todos os módulos de conteúdo.
### BUG-FUNC-065 — Item de menu de logs nunca exibido
- **Severidade:** **Alta** — consequência direta do slug duplicado (BUG-FUNC-050).  |  **Onde:** `v6/resources/views/vendor/adminlte/page.blade.php:284`
- **O que acontece:** `v6/resources/views/vendor/adminlte/page.blade.php:284` exibe o item de menu de logs condicionado ao valor de `$modulos['logs-do-sistema']`, que resolve para `false` por causa do slug duplicado.  |  **Impacto:** O módulo de logs do sistema nunca aparece no menu do CMS.  |  **Correção:** Corrigir o slug duplicado (BUG-FUNC-050).  |  **Impacto na v7:** Menu derivado de uma fonte única (registry e permissão).
### BUG-FUNC-066 — `Route::resource('log')` gera 6 ações inexistentes
- **Severidade:** **Alta** — rotas que respondem 500.  |  **Onde:** `v6/routes/basepro.php:71`
- **O que acontece:** `Route::resource('log')` em `v6/routes/basepro.php:71` gera 6 ações; o `ActivityLogController` não implementa nenhuma delas.  |  **Impacto:** As rotas do resource respondem 500 ao serem acessadas.  |  **Correção:** Implementar as ações ou remover o resource.  |  **Impacto na v7:** Rotas explícitas, uma a uma.
### BUG-FUNC-067 — Rotas de `categoria-conteudo` sem `check.logged`
- **Severidade:** **Alta** — escrita de categorias de conteúdo sem login.  |  **Onde:** `v6/routes/basepro.php:173` e `:175`
- **O que acontece:** As rotas de `categoria-conteudo` (`v6/routes/basepro.php:173` e `:175`) estão no grupo `/cms` sem `check.logged`, e o controller é um dos que sobrescrevem `__construct()` sem `parent::__construct()`.  |  **Impacto:** Criação, alteração e exclusão de categorias de conteúdo por visitante anônimo.  |  **Correção:** `check.logged` e Policy; e corrigir o construtor (BUG-SEC-003).  |  **Impacto na v7:** Grupo do CMS com `auth` e `can:`.
### BUG-FUNC-068 — Exclusão em lote apaga linhas da tabela errada
- **Severidade:** **Alta** — exclusão em lote de newsletters não apaga newsletters.  |  **Onde:** `v6/resources/views/prefecture/contents/newsletter/index.blade.php:22`
- **O que acontece:** O botão de exclusão em lote da listagem de newsletters aponta para a rota genérica que opera sobre `contents`, não sobre `newsletter`.  |  **Impacto:** A exclusão em lote de newsletters não apaga newsletters — ou apaga conteúdo de outro módulo.  |  **Correção:** Criar rota dedicada `newsletter/delete-por-lote` com método próprio.  |  **Impacto na v7:** Uma rota por recurso; nenhum reaproveitamento acidental de handler de outra entidade.
### BUG-FUNC-069 — `changelog.show` aponta para método inexistente
- **Severidade:** **Alta** — HTTP 500 em `/cms/novidades/{id}`.  |  **Onde:** `v6/routes/basepro.php:76`
- **O que acontece:** `Route::get('novidades/{id}', 'Admin\ChangeLogController@show')->name('changelog.show')` referencia um método `show()` que não existe no controller (só há index/create/store/edit/update/destroy).  |  **Impacto:** HTTP 500 ao acessar `/cms/novidades/{id}`.  |  **Correção:** Implementar `show()` ou remover a rota.  |  **Impacto na v7:** Nenhuma rota aponta para método inexistente — verificado por teste.
### BUG-FUNC-070 — Segredos gravados em claro no log de atividades
- **Severidade:** **Alta** — `activity_log.properties` vira cofre de segredos.  |  **Onde:** `v6/app/Http/Controllers/Prefecture/ConfigurationController.php:130`
- **O que acontece:** `ConfigurationController` grava o valor integral da configuração em `activity_log.properties` via `withProperty('Detalhe', ...)`.  |  **Impacto:** O log de atividades vira cofre de segredos — qualquer operador com acesso ao log lê credenciais.  |  **Correção:** Filtrar campos sensíveis antes de gravar no log; gravar apenas a chave e o id.  |  **Impacto na v7:** Allowlist de campos auditáveis; segredos nunca entram no log.
### BUG-FUNC-071 — `ajaxUploadFiles` engole exceções e não devolve resposta
- **Severidade:** **Alta** — o JS fica travado em "enviando".  |  **Onde:** `v6/app/Http/Controllers/Prefecture/CalendarEvent/CalendarEventController.php:352` e `:388`
- **O que acontece:** O `catch` do `ajaxUploadFiles` engole a exceção e o método não devolve resposta ao JS.  |  **Impacto:** O botão fica travado em enviando e o operador não sabe que o upload falhou.  |  **Correção:** Devolver JSON de erro no `catch` (e `report($e)`).  |  **Impacto na v7:** Endpoint de upload com resposta JSON padronizada em sucesso e em erro.
### BUG-FUNC-072 — Endpoints de selos institucionais sem rota
- **Severidade:** **Alta** — controllers com ação sem rota (e duplicados em `:597` e `:641`).  |  **Onde:** `v6/app/Http/Controllers/Prefecture/ConfigurationController.php:563`
- **O que acontece:** `ConfigurationController` tem três ações de selos institucionais (`updateSelosInstitucionais`, `createSelosInstitucionais`, `deleteSelosInstitucionais`) sem rota registrada em `v6/routes/`.  |  **Impacto:** A funcionalidade é inalcançável pelo CMS; a única rota de selos (`/selos`) está fora do grupo de manutenção.  |  **Correção:** Registrar as rotas ou remover as ações.  |  **Impacto na v7:** Toda ação de controller tem rota; verificado por teste de superfície.
### BUG-PERF-001 — Paginação em memória no portal
- **Severidade:** **Alta** — o conjunto inteiro é carregado em memória para depois fatiar; o custo cresce sem limite com o histórico.  |  **Onde:** `v5/app/Http/Controllers/Portal/ConcursosPssController.php:45` e `:114` · `v6/app/Http/Controllers/Portal/LegislationController.php:197`
- **O que acontece:** O controller carrega o conjunto inteiro com `->get()` e fatia em memória, em vez de `paginate()` no servidor.
- **Impacto:** O custo de memória e tempo cresce sem limite com o histórico; o portal de concursos e o de legislação são os mais expostos.  |  **Impacto na v7:** Paginação server-side em todas as listagens públicas.  |  **Correção:** `paginate()` no servidor com `perPage` e índice em `(category_contents_id, publication_date)`.
### BUG-PERF-002 — Endpoint de filtro carrega a subcategoria duas vezes
- **Severidade:** **Alta** — duas queries sem `LIMIT`, e a primeira carrega a coluna `text`, que pode ser grande. Principal risco de indisponibilidade do portal de legislação.  |  **Onde:** `v6/app/Http/Controllers/Portal/LegislationController.php:197`
- **O que acontece:** `filtroGET` carrega a subcategoria duas vezes sem `LIMIT`; a primeira query inclui a coluna `text`, que pode ser grande.  |  **Impacto:** Principal risco de indisponibilidade do portal de legislação em um município com milhares de decretos.  |  **Correção:** Uma query só, com `select` das colunas necessárias e paginação.  |  **Impacto na v7:** Uma query paginada por padrão; projeções de coluna explícitas.
### BUG-PERF-003 — N+1 no `ConfigurationRequest`
- **Severidade:** **Alta** — uma query por campo enviado; a categoria `site` tem 57 configurações ativas.  |  **Onde:** `v6/app/Http/Requests/ConfigurationRequest.php:53` e `:136`
- **O que acontece:** `ConfigurationRequest::rules()` e `::messages()` executam uma query por campo enviado; a categoria `site` tem 57 configurações ativas.  |  **Impacto:** 4+ queries por submit de configuração, crescendo com o tamanho da categoria.  |  **Correção:** Carregar as configurações da categoria uma vez e indexar por chave.  |  **Impacto na v7:** Form Request sem I/O; validação contra um array carregado em uma única query.
### BUG-PERF-004 — `head.blade.php` executa 5 queries por renderização
- **Severidade:** **Alta** — e sem guarda de nulo: se `cor_1`..`cor_4` não existirem, erro fatal em **todas** as páginas do portal.  |  **Onde:** `v6/resources/views/portal/layouts/barra-privacidade/head.blade.php:3`
- **O que acontece:** `head.blade.php` faz 5 queries (`cor_1` a `cor_4` e mais) por renderização, sem guarda de nulo no `first()->value`.  |  **Impacto:** 5 queries em toda página do portal; e erro fatal em todas as páginas se `cor_1` a `cor_4` não existirem.  |  **Correção:** Uma query com `whereIn('key', [...])` e `keyBy('key')`; usar `?->value` com valor padrão.  |  **Impacto na v7:** Configurações do tema carregadas uma vez por requisição, em cache.
### BUG-UX-001 — Layouts só têm `path` apontando para `modelo5`
- **Severidade:** **Alta** — das 41 linhas da tabela `layouts`, 39 com `path` apontam para `portal.layouts.modelo5.*`. O editor de layout do CMS **só sabe editar o modelo5**: se o município trocar `layout` para `modelo1`, os widgets customizados não têm efeito.  |  **Onde:** `v6/app/Http/Model/Prefecture/Layout.php:25` (coluna `path`) · `v6/app/Http/Controllers/Prefecture/LayoutController.php`
- **O que acontece:** Das 41 linhas da tabela `layouts`, 39 com `path` apontam para `portal.layouts.modelo5.*` — o editor de layout do CMS só sabe editar o modelo5.  |  **Impacto:** Se o município trocar `layout` para `modelo1`, os widgets customizados (títulos, `columns`, `style`, ordem) não têm efeito: os `path` não batem.  |  **Correção:** Parametrizar o `path` pelo template ativo, ou duplicar as linhas por template.  |  **Impacto na v7:** Layout como dado por template, com componente único que resolve o `path` em runtime.
### BUG-UX-002 — Erro 500 na home quando o aviso ativo não tem imagem de destaque
- **Severidade:** **Alta** — o aviso ativo é lido sem verificar se tem imagem.  |  **Onde:** `v6/resources/views/portal/layouts/modelo1/home/aviso.blade.php:13` · `v6/resources/views/portal/layouts/modelo6/home/index.blade.php:28`
- **O que acontece:** A view `home/aviso.blade.php` (modelo1) e `modelo6/home/index.blade.php:28` acessam a imagem de destaque do aviso ativo sem verificar se ela existe.  |  **Impacto:** HTTP 500 na home do portal — a página mais visitada do site.  |  **Correção:** Verificar a existência do arquivo de imagem antes de renderizar o `img`.  |  **Impacto na v7:** Componente de imagem com fallback; nenhum acesso a arquivo sem guarda.
### BUG-UX-003 — Destinos trocados no menu "Acesso Rápido" (menu 3)
- **Severidade:** **Alta** — "Manual de Navegação" aponta para "Relatório Estatístico", "Solicitar Informação" para "Ouvidoria" e "Acompanhe sua solicitação" para "Solicitar Informação".  |  **Onde:** `v6/database/seeds/MenuItemsTableSeeder.php:1049` · `v6/app/Http/Presenters/MenuItemPresenter.php:16`
- **O que acontece:** O seeder `MenuItemsTableSeeder` (linhas 1049-1081) grava destinos trocados: Manual de Navegação aponta para `static_pages_id = 5` (Relatório Estatístico), Solicitar Informação para `2` (Ouvidoria) e Acompanhe sua solicitação para `3` (Solicitar Informação).  |  **Impacto:** O cidadão sai do menu e chega à página errada — e a página de LAI perde a entrada Acompanhe sua solicitação.  |  **Correção:** Corrigir os `static_pages_id` do seeder, revisando a semântica de cada `static_pages`.  |  **Impacto na v7:** Vínculo menu para conteúdo por chave natural (slug), não por id numérico de seeder.
### BUG-UX-004 — Permissões do seed divergem das permissões avaliadas no código
- **Severidade:** **Alta** — o seed grava `redes-sociais.*`, mas as views avaliam `rede-social.create`; só `super-admin` vê o botão.  |  **Onde:** `v6/database/seeds/RolesTableSeeder.php:84` · `v6/resources/views/prefecture/contents/rede-social/index.blade.php:15`
- **O que acontece:** `RolesTableSeeder:84` grava as permissões `redes-sociais.*`, mas as views do módulo avaliam `has_access(['rede-social.create'])` — slug que não existe na tabela de permissões.  |  **Impacto:** Somente `super-admin` vê o botão Cadastrar Rede Social; os demais operadores não conseguem criar redes.  |  **Correção:** Padronizar o slug (criar `rede-social.*` no seed ou alterar as views para `redes-sociais.*`).  |  **Impacto na v7:** Tabela de permissões única por módulo, com slugs gerados a partir de `resources.slug`.
### BUG-MIG-001 — 8 módulos inteiros removidos na v6 sem justificativa
- **Severidade:** **Crítica** — perda funcional não documentada; ver §4.  |  **Onde:** `v6/app/Http/Controllers/Prefecture/` (nenhum dos controllers correspondentes existe)
- **O que acontece:** Detalhado na §4.1 — 8 módulos (obras, obras-paralisadas, concursos-pss, estagiarios, fila-espera-sus, licitantes-sancionados, terceirizados, relatorio-estatistico-ouvidoria) e o fluxo de geração do Gemini sumiram da v6 sem ADR, ticket ou changelog que justifique.  |  **Impacto:** O município perde a funcionalidade; a v7 precisa reconstruir, não migrar.  |  **Correção:** Reconstruir cada módulo a partir do dossiê da v5, que é a especificação funcional disponível.  |  **Impacto na v7:** Definir o escopo da v7 com o negócio antes de iniciar o Sprint 5.
### BUG-MIG-003 — Pacote `davejamesmiller/laravel-breadcrumbs` removido do GitHub
- **Severidade:** **Alta** — o `composer install` da v5 **abortava** (404 no zipball e no clone), deixando o `vendor/` incompleto e a aplicação sem boot. Corrigido na v5 durante a investigação trocando para o fork `diglactic/laravel-breadcrumbs`.  |  **Onde:** `v5/composer.json:20` · `v6/composer.json:20` · `v5/routes/breadcrumbs.php` (importa `DaveJamesMiller\Breadcrumbs\Facades\Breadcrumbs`)
- **O que acontece:** O `v5/composer.json` fixava `davejamesmiller/laravel-breadcrumbs 4.x`, pacote cujo repositório GitHub foi removido (404 no zipball e no clone). O `composer install` da v5 abortava, o `vendor/` ficava incompleto e a aplicação não bootava. O `v5/routes/breadcrumbs.php` importa `DaveJamesMiller\Breadcrumbs\Facades\Breadcrumbs` — a dependência não é opcional.
- **Impacto:** Deploy quebrado; a v5 não iniciava. Corrigido durante a investigação trocando para o fork `diglactic/laravel-breadcrumbs`.  |  **Impacto na v7:** Componente Blade de breadcrumb alimentado por uma view-object, sem pacote externo.  |  **Correção:** não replicar a dependência; na v7, componente Blade `<x-breadcrumbs>` alimentado por view-object, sem pacote.  |  **v7:** o padrão `Breadcrumbs::view()` está acoplado ao pacote em todas as views de portal (`v5/resources/views/portal/layouts/modelo*/layout/breadcrumb.blade.php`).
### BUG-MIG-004 — `DB::raw` com `concat`/`CASE`/`to_char` montados em PHP
- **Severidade:** **Alta** — SQL não portável e não protegido por prepared statement.  |  **Onde:** `v6/app/Http/Controllers/Prefecture/ContractController.php:67` e `:190` e `:196` · `v6/app/Http/Controllers/Prefecture/ProposalController.php:68` e `:104`
- **O que acontece:** `ContractController` (linhas 67, 190, 196) e `ProposalController` (linhas 68, 104) montam SQL com concatenação de string PHP dentro do `DB::raw`.  |  **Impacto:** SQL não portável (CASE, `to_char`, `concat`), sem prepared statement — superfície de injeção e de quebra em qualquer banco diferente do PostgreSQL.  |  **Correção:** Substituir por expressões do Query Builder ou por colunas derivadas.  |  **Impacto na v7:** Nenhum `DB::raw` com interpolação; colunas calculadas em migration.
### BUG-MIG-005 — `orderByRaw` com regex PostgreSQL
- **Severidade:** **Alta** — o resultado da ordenação depende do formato textual do número e é específico do PostgreSQL.  |  **Onde:** `v6/app/Http/Controllers/Prefecture/ContractController.php:190`
- **O que acontece:** A ordenação de `biddings.number` usa `orderByRaw` com um `CASE` que tenta extrair o primeiro número da string, usando regex (`number ~ '/[0-9]/`) — específico do PostgreSQL e dependente do formato textual do número.
- **Impacto:** A ordenação varia conforme a convenção de numeração do município e quebra em qualquer outro banco.  |  **Impacto na v7:** Coluna numérica preenchida na aplicação, com índice.  |  **Correção:** campo numérico derivado (`biddings.numeric_number`), ordenado por ele.
### BUG-MIG-006 — Views SQL com `category_contents_id` fixo
- **Severidade:** **Alta** — se o id for reatribuído, a view lista outro módulo.  |  **Onde:** `v6/database/migrations/2024_11_29_103842_update_view_comite_de_investimentos.php:20` · `v6/database/migrations/2024_07_01_094338_udate_noticias_view.php:37`
- **O que acontece:** As 75 views SQL da v6 fixam `category_contents_id` numérico (ex.: `cc.category_contents_id = 2` na view `legislacao`, `= 1` na de notícias). A sequência `category_contents_id_seq` é compartilhada por todos os módulos de conteúdo, então o id 2 não é garantido em bases novas.
- **Impacto:** Recriar o banco do zero pode fazer a view listar outro módulo — sem erro, apenas conteúdo errado.  |  **Impacto na v7:** Regras de negócio em PHP; views SQL apenas para leitura simples e com filtro por slug.  |  **Correção:** parametrizar por `slug`; ou migrar as views para consultas Eloquent.
### BUG-MIG-007 — Coluna `"order"` (palavra reservada)
- **Severidade:** **Alta** — exige aspas em todo o SQL que a referencia; a ordenação quebra silenciosamente quando falta.  |  **Onde:** `v6/database/migrations/2018_04_02_181807_create_archives_table.php:22` · `v6/app/Http/Model/Prefecture/Content.php:26`
- **O que acontece:** a coluna se chama `order`, que é palavra reservada do SQL: todo acesso exige aspas (`ORDER BY "order"`), e a ordenação quebra silenciosamente quando o valor é `NULL` (o `null == null` dispara a rotina de correção de ordem duplicada e todos os registros sem ordem caem na posição 1).
