# AGENTS.md — Regras de consistência do projeto (Public V6)

## 0. Regras de ouro (valem para tudo)

- **NUNCA** rode `git commit`, `git push`, `git add` de tudo, `sd sync`, `sd` (seeds) ou qualquer
  ação de publicação **sem pedido explícito** do usuário. Crie/ajuste seeds localmente apenas.
- **NÃO** atualize PHP, Laravel, framework, Composer ou dependências para versões mais novas.
  Stack travada: **PHP 7.2.34 / Laravel 5.5.50 / Apache 2.4 (EasyApache4) / cPanel-AlmaLinux**.
- Toda mudança de código PHP deve passar por `php -l` e pelo runner de testes no canário.
- Presuma que o ambiente de produção é **bare-metal cPanel/AlmaLinux**; o Docker serve só para dev/canário.

---

## 1. Stack e fronteiras intransponíveis

| Tópico | Regra |
|---|---|
| PHP | Só recursos de **7.2**. Proibido: arrow fn `fn()=>`, typed properties, `match`, nullsafe `?->`, `??=`, union `int\|string`, `mixed`, constructor promotion, enums, atributos, `static` return type. Use closures `function(){ }`, `?Type`, docblocks. |
| Laravel 5.5 | Sem `php artisan view:cache` (só 5.6+). Use `config:cache`+`route:cache`+`optimize`. Sem `@csrf` (use `{{ csrf_field() }}`). Sem `@error` (use `@if($errors->has('x'))`). Hash = **bcrypt** (sem Argon). |
| `env()` | **Nunca** fora de `config/*`. Leia via `config('x.y')`. Após `config:cache` o `env()` é `null` em runtime. |
| Rotas | **Nunca** Closure como ação de rota (`Route::get('/', function(){})`) nem 3º arg Closure — quebra `route:cache` (all-or-nothing). Use `Route::view`/`Route::redirect`/controller. |
| APP_ENV | Sempre `'production'`. Se vier `'prod'` de DB/`.env`, normalize para `'production'` (ver `config/app.php`). |

---

## 2. Segurança (web_security_guide)

- **CSP:** nunca `script-src 'unsafe-inline'` em produção. Use *nonce* (injeção central em
  `CspNonce`) + *host-allowlist* mínima. `frame-ancestors 'self'`, `object-src 'none'`,
  `base-uri 'none'`, `upgrade-insecure-requests`. **Canário:** hoje está em
  `Content-Security-Policy-Report-Only` + `Reporting-Endpoints`. Só troque para `Content-Security-Policy`
  (enforce) **depois** de revisar `storage/logs/csp-violations.log` e confirmar zero recurso legítimo barrado.
- **Cookies:** `Secure` + `HttpOnly` + `SameSite=Lax` (nativo em `config/session.php`). Nunca
  `SameSite=None` sem `Secure`.
- **Headers (`.htaccess`, crus — sem `<IfModule mod_headers.c>`):** `Cross-Origin-Resource-Policy: same-origin`,
  `Cross-Origin-Opener-Policy: same-origin`, `X-Content-Type-Options: nosniff`, `Referrer-Policy`,
  `Strict-Transport-Security`, `X-Frame-Options: SAMEORIGIN` (legado). Não regredir o template de
  segurança `_pesquisa/security/htaccess.txt`.
- **SRI:** só em assets **self-hosted** e *version-pinned* (ex.: `chart.js`). Loaders externos
  voláteis (`vlibras-plugin.js`, `recaptcha/api.js`, `gstatic charts/loader.js`) ficam na allowlist —
  SRI neles quebraria no update do fornecedor.
- **App:** prepared statements (nunca interpolação em SQL); `filter_var` na entrada; `htmlspecialchars(..., ENT_QUOTES|ENT_HTML5, 'UTF-8')` na saída; tokens com `random_bytes`/`hash_equals`; `password_hash` bcrypt.

---

## 3. Performance & Cache (invariantes do app)

- **Ordem do grupo `web` em `app/Http/Kernel.php`** é cargo-crítica (outbound roda em ordem inversa):
  `PageCache` → `CacheControl` → `HtmlMinify` → `PictureTransform` → **`CspNonce`** (após `PictureTransform`) →
  `EncryptCookies`/`StartSession`/… . Não reordenar sem revalidar HIT/MISS do FPC e headers.
- **`CacheControl`** age só em `GET`, controller `Portal\`, status < 400, sem `Set-Cookie`, sem token CSRF no
  corpo. ETag deve ser **fraco** (`W/"..."`) e normalizar `If-None-Match` (remover `W/`, aspas, sufixo `-gzip`/`-br`).
  `Vary: Accept-Encoding` (nunca `User-Agent`).
- **`PageCache`** (FPC): só `GET` + `Portal\` + 200 + `text/html` + não autenticado + sem `Set-Cookie`.
  Chave `page_cache:v1:` + `md5(fullUrl|locale)`. Invalidação por evento de modelo (`created`/`deleted`,
  não `updated`/`increment('visualizacoes')` — evita thrash). Comando `cache:page:clear`.
- **CSRF:** não remover `Set-Cookie` de páginas de formulário antes do desacoplamento (risco de 419).
  Token CSRF é estabilizado no middleware (strip do valor) + injetado client-side via
  `portal/layouts/partials/csrf-inject.blade.php`. **Não editar os ~60 Blades do portal** para o token.
- **OPcache produção** (`validate_timestamps=0`): toda mudança de código exige reset de OPcache no deploy
  (`php artisan opcache:clear` / `touch public/index.php` / reiniciar php-fpm). Sem isso, código novo não carrega.
- **`.htaccess` da conta:** funciona para `Header` cruas, `mod_expires`, `FilesMatch`, `FileETag`, `Rewrite*`.
  **Não** funciona (e não deve ir) para compressão/OPcache — isso é **WHM (PHP_INI_SYSTEM)** e é ignorado na conta.
- **Nunca** `<IfModule mod_headers.c>` no `.htaccess` → causa HTTP 500 neste servidor.

---

## 4. Frontend / Assets

- **Não** agrupar/bundar CSS/JS em pacotes (decisão do usuário — instável). Mantenha
  `public/assets/vendor/` como **única fonte da verdade** (self-host). Ao trocar lib, namespacear por versão
  (`assets/vendor/select2/4.0.3/`) — a regra `immutable` do `.htaccess` não tem hash no nome.
- `defer`/`async` sem mover posições. jQuery/inline-sync **não** recebem `defer` (init síncrono dependente).
  `gstatic charts/loader.js` não recebe `defer` (quebraria gráficos).
- **Não** editar Blades de módulo para otimização de imagem — use o middleware `PictureTransform`
  (`<img>` → `<picture>` WebP + `loading="lazy"` + `fetchpriority` no LCP). Imagens em `incorporacao_*` (DB)
  não passam pelo middleware (auditar à parte).
- **Não** usar `@import` do Google Fonts; fontes são vendorizadas (`assets/vendor/fonts/google/`). `font-display: swap`.
- **HTML minify** (`HtmlMinify`) preserva `<pre>/<code>/<textarea>/<script>/<style>/<svg>` verbatim.
  Comentários de linha `//` em `<script>` inline devem ser `/* */` em nível de fonte (não via regex no middleware).
- **Build:** CSS do admin é commitado (gerado via `npm run production` / fallback Dart Sass). `mix-manifest.json`
  é só do Mix; `manifest.webmanifest` é o PWA. **Não** rodar `npm` no `deploy.sh` (toolchain `node-sass` quebra em Node moderno).

---

## 5. Testes e Qualidade (foco principal)

### Obrigatório em toda mudança de código
1. **Lint** de cada arquivo PHP alterado (container):
   `docker exec -i public_infra_update-app-1 bash -c 'cd /var/www/html && php -l app/Http/Middleware/X.php'`
   — sem erro de sintaxe e sem token proibido do §1.
2. **Suíte de testes** no canário (roda contra **pgsql** do container):
   `docker exec -i public_infra_update-app-1 php vendor/bin/phpunit`
   - Mantenha a suíte **verde**. Única falha aceita por design: o stub `Tests\Feature\ExampleTest`
     (302 em `/`) — não "corrija" mexendo no app; se tocar, remova o stub, não mude rotas.
3. **`composer validate`** após mexer em `composer.json`.

### Padrões de teste (sigam os existentes)
- Middleware de cache/cabeçalho: `tests/Unit/CacheControlTest.php` (~21 testes), `PageCacheTest.php`,
  `LayoutCacheTest.php`, `CountingCacheRepository.php` (fake contador de closures). Cubra: corpo estável
  após strip de token, `ETag` idêntico para tokens distintos, `/csrf-token` fora do cache, HIT/MISS,
  flush por evento de modelo, `Portal\` only.
- Performance/índices: `tests/Unit/PerformanceIndexTest.php` usa `SET enable_seqscan = off` + `EXPLAIN`
  para afirmar **Index Scan** (robusto em tabela vazia). `EagerLoadMemoizeTest.php` para N+1/memoização.
- Testes **não exigem browser**. Para CSP use o coletor `csp-violations.log`; para cache use `X-Page-Cache`/headers.

### Migrations e DB
- Índices **non-unique** (`->index()`) para não quebrar `slug`/`key` duplicados legados.
- Em tabela grande em produção, `CREATE INDEX CONCURRENTLY` (pgsql) ou janela de manutenção — o app suporta
  pgsql/mysql; use definições portáteis.
- `getTemplateActived()` tem write em GET (side-effect `pagina_em_manutencao`) — memoize a **leitura**, preserve o write.

### Qualidade de código
- Controllers finos; lógica em `app/Services` ou model/trait; nada de regra de negócio em rota/Blade.
- Eager loading (`with`/`withCount`) para N+1; `Cache::remember` para dados de layout quase-estáticos
  (mesmo TTL do FPC). Invalidação coordena FPC + layout em `PageCache::flush()`/`flushLayout()`.
- PSR-12, `declare(strict_types=1)` em libs, um class/file, docblocks onde útil, exceções (nunca `catch` vazio).

---

## 6. Fluxo de validação no canário

- Container: `public_infra_update-app-1` (workdir `/var/www/html`). Ports host: `32768`→80, `32769`→443.
  Apache roda no container → `.htaccess` é aplicado de verdade.
- **CacheControl só age em GET real** — `curl -sI` (HEAD) parece "sem efeito". Teste com
  `curl -s -D - -o /dev/null https://.../`.
- OPcache no container pode não recarregar em bind-mount (`validate_timestamps=0`): **reinicie o php-fpm/container**
  antes de validar mudanças de código.
- Medição opcional: `runners/medir_estrutura.sh` + `comparar.py` contra baseline `2026-09-08-12-10`.
- `https://<host>/` → 200; checar headers (`Content-Security-Policy-Report-Only`, `Reporting-Endpoints`,
  `Cross-Origin-*`, `X-Page-Cache`, `Cache-Control: public … immutable` em estáticos).

---

## 7. Hard "NÃO FAZER" (veto imediato)

- ❌ `git commit` / `git push` / `sd sync` automático.
- ❌ Atualizar PHP/Laravel/dependências ou usar recursos de PHP > 7.2.
- ❌ `<IfModule mod_headers.c>` no `.htaccess` (HTTP 500).
- ❌ `env()` fora de `config/*`; Closure como ação de rota; `@csrf`/`@error` (5.5).
- ❌ `unsafe-inline` em CSP de produção; remover `Set-Cookie` de formulário antes do desacoplamento CSRF.
- ❌ Editar Blades de módulo para nonce/CSRF/imagem (use os middlewares centrais).
- ❌ Agrupar/bundar assets; `@import` Google Fonts; `defer` em jQuery/init-sync/gstatic.
- ❌ Rodar `npm` no `deploy.sh`; adicionar `ext-apcu` ao `require` do `composer.json` (degrada graciosamente).
- ❌ Mexer na ordem do grupo `web` sem revalidar FPC/HIT/MISS e headers.
