# 07 — Oportunidades a partir das skills (`.agents/skills/`)

Revisão das skills que tocam o runtime do container: `caddy`, `php-84-correct`, `laravel-12`,
`postgresql`, `web-performance`, `web-security-review` e `vite`. O objetivo é **só** o que muda
configuração de container (Caddy/php.ini/FPM/Postgres/Compose) — não estilo de código.

Método: cada achado confronta a recomendação da skill (`arquivo:linha`) com a configuração atual
(`arquivo:linha`). Onde a skill é genérica ou está desatualizada, a verificação usa a documentação
oficial da versão em uso. Nada foi aplicado.

As melhorias `SK-xx` complementam o catálogo `03-melhorias.md`; quando coincidem, o campo
**Relação** aponta o ID existente em vez de duplicar.

## Resumo

| ID | Eixo | Ajuste | Prioridade | Relação |
|---|---|---|---|---|
| SK-01 | Estabilidade | Caddy: `read_body`/`write` compatíveis com upload/download de 110 MiB | P0 | novo |
| SK-02 | Estabilidade | Caddy: `max_header_size` explícito (skill diz 1 MB; default real é 16 KiB) | P2 | novo |
| SK-03 | Estabilidade | Caddy: `grace_period`, `stop_grace_period` do `web`, `ulimits.nofile` | P1 | P-07/P-08 |
| SK-04 | Estabilidade | Caddy: timeouts de upstream + retry em GET durante reload do FPM | P1 | P-07 |
| SK-05 | Logs/Segurança | Access log com filtro de `Authorization`/`Cookie` e `log_skip` | P0 | O-01 |
| SK-06 | Segurança | PHP: `allow_url_fopen=Off`, `allow_url_include=Off` | P1 | novo |
| SK-07 | Segurança | PHP: `disable_functions` **por pool**, preservando `proc_open` | P1 | novo |
| SK-08 | Segurança | PHP: `open_basedir` por pool (condicionado a teste) | P2 | novo |
| SK-09 | Estabilidade | PHP: `max_input_time` e `request_terminate_timeout` coerentes com 110 MiB | P1 | P-02 |
| SK-10 | Estabilidade | Queue: `--timeout`, `--backoff`, `stop_grace_period`, `prune-failed` | P1 | R-02 |
| SK-11 | Performance | Postgres: `wal_compression=lz4` + checkpoints dimensionados | P1 | P-05/R-05 |
| SK-12 | Performance | Postgres: autovacuum mais agressivo + `maintenance_work_mem` | P2 | P-05 |
| SK-13 | Estabilidade | Backup diário verificado em serviço/profile próprio | P2 | R-05 |

## Itens

### SK-01 — Caddy: timeouts de corpo incompatíveis com o limite de upload

- **Skill:** `caddy/05-performance.md:97-101` recomenda `read_body 10s`, `read_header 5s`,
  `write 30s`; a própria nota em `:107-108` diz que `write` deve ser alto para transferências
  grandes. A documentação oficial da Caddy é mais precisa: `read_body` é um teto absoluto e
  `read_body_idle` (default `1m`) é o que protege contra slowloris; o exemplo oficial usa
  `read_body 5m`, `read_body_idle 30s`, `write 10m`, `write_idle 30s`.
- **Atual:** `docker/web/Caddyfile:12-15` fixa `read_body 10s` e `write 30s`; o mesmo arquivo aceita
  `request_body max_size 110MiB` (`:26-28`), o Apache aceita `LimitRequestBody 115343360`
  (`docker/web/app.conf:63`) e o PHP aceita `upload_max_filesize=110M` /
  `post_max_size=114M` (`docker/php-fpm/php.ini:42-43`).
- **Ajuste:** `read_body 5m`, `read_body_idle 30s`, `read_header 5s`, `write 10m`,
  `write_idle 30s`, `idle 5m`.
- **Ganho:** um PDF de 100 MiB a 2 MB/s (50 s) deixa de ser cortado em 10 s; um download grande
  deixa de ser cortado em 30 s. O slowloris continua contido por `read_body_idle` + Apache
  `RequestReadTimeout ... MinRate=500` (`app.conf:68-70`).
- **Risco:** `read_body` absoluto maior permite um cliente que envia devagar mas sempre envia
  ocupar um worker; `read_body_idle` e o `MinRate` do Apache são a defesa. Não remover ambos.
- **Verificação:** `curl --limit-rate 2M -F file=@arquivo-100mib ...` (upload) e
  `curl --limit-rate 2M -o /dev/null .../arquivo.pdf` (download) sem 408/502; `caddy validate`.
- **P/E:** P0 / B.

### SK-02 — Caddy: `max_header_size` e o default real

- **Skill:** `caddy/05-performance.md:221-231` afirma `max_header_size 1MB` como default e sugere
  `64KB` para APIs internas. A documentação atual da Caddy 2.11 diz default **16 KiB**.
- **Atual:** não há `max_header_size` no `docker/web/Caddyfile`; vale o default de 16 KiB.
- **Ajuste:** medir o tamanho de `Cookie`/headers reais (sessão do CMS + preferências de
  acessibilidade + CSRF). Se passar de ~8 KiB, fixar `max_header_size 64KiB`; caso contrário,
  apenas documentar o default. Não copiar o “1MB” da skill.
- **Ganho:** evita `431 Request Header Fields Too Large` em sessões com muitos cookies sem abrir
  1 MB de memória por conexão.
- **Risco:** elevar demais amplia memória por conexão; 64 KiB é o teto razoável.
- **Verificação:** request com `Cookie` grande (`curl -H "Cookie: x=$(head -c 12000 /dev/zero | tr '\0' a)"`)
  e `docker compose logs web`.
- **P/E:** P2 / B.

### SK-03 — Caddy: encerramento gracioso e limites do processo

- **Skill:** `caddy/05-performance.md:233-242` (`grace_period 10s`), `caddy/09-deployment.md:258-270`
  (`grace_period`/`shutdown_delay`), `caddy/09-deployment.md:337-350` (`nofile`).
- **Atual:** `docker/web/Caddyfile` não define `grace_period`; o serviço `web` não tem
  `stop_grace_period` nem `ulimits` (`docker-compose.yml:13-33`).
- **Ajuste:** `grace_period 10s` no bloco global; `stop_grace_period: 15s` no `web`;
  `ulimits.nofile: 65536`. `shutdown_delay` **não** é útil aqui: o Apache usa `retry=0`
  (`docker/web/app.conf:25`), então um 503 de shutdown não é re-tentado.
- **Ganho:** requisições em andamento terminam no restart do deploy; o Caddy não é morto por
  `SIGKILL` em 10 s; conexões concorrentes não esbarram no `nofile` baixo do Docker.
- **Risco:** deploy alguns segundos mais lento; `nofile` alto consome apenas tabela de FDs.
- **Verificação:** `docker compose restart web` sob carga; `docker inspect -f '{{.HostConfig.Ulimits}}'`.
- **P/E:** P1 / B.

### SK-04 — Caddy: timeouts de upstream e retry durante o reload do FPM

- **Skill:** `caddy/05-performance.md:111-126` (`dial_timeout`, `read_timeout`, `write_timeout`,
  `lb_try_duration`, `lb_try_interval`) e `caddy/07-reverse-proxy.md:136-145` (retries só em GET).
- **Atual:** `docker/web/Caddyfile:35` usa `php_fastcgi php-fpm:9000` sem nenhum subdiretivo; não há
  `read_timeout`, `write_timeout` nem retry.
- **Ajuste:** no bloco `php_fastcgi`:
  `dial_timeout 3s`, `write_timeout 60s`, `lb_try_duration 5s`,
  `lb_try_interval 250ms`, `lb_retries 2`. `read_timeout` segue o orçamento de SK-09:
  **130s** se o FPM usar `request_terminate_timeout=120s` (upload de 110 MiB),
  ou **40s** se o FPM ficar em 35s e o upload for limitado. Invariante:
  `read_timeout` > `request_terminate_timeout` > `max_input_time`.
- **Ganho:** FPM ocupado/travado gera erro em 40 s em vez de pendurar; durante o restart do
  `php-fpm` no deploy, GETs idempotentes esperam até 5 s por um upstream saudável; POST não é
  re-tentado (default `lb_retry_match method GET`).
- **Risco:** retry em GET pode duplicar efeitos colaterais de rotas GET mal projetadas; o projeto
  já trata GET como leitura. `write_timeout` alto afeta apenas envio ao FPM.
- **Verificação:** `docker compose restart php-fpm` com loop de `curl`; `docker compose logs web`.
- **P/E:** P1 / B.

### SK-05 — Caddy: access log com redação e sem ruído

- **Skill:** `caddy/08-observability.md:43-53` (log JSON), `:76-89` (filter deletando
  `Authorization`/`Cookie` e mascarando IP), `:91-100` (`log_skip` para health/estáticos).
- **Atual:** o `docker/web/Caddyfile` não tem bloco `log` — nenhum acesso é registrado.
- **Ajuste:** `log { output stdout; format filter { wrap json; fields { request>headers>Authorization delete; request>headers>Cookie delete; request>remote_ip replace ".*" "REDACTED" } }; level INFO }`;
  `log_skip /health` e `log_skip @build` (`path /build/*`). Registrar `X-Request-ID` para
  correlação com o log JSON do Laravel.
- **Ganho:** status/duração/bytes por request sem vazar cookie/token; volume menor; correlação
  edge↔app.
- **Risco:** mascarar IP remove a capacidade de investigar abuso no log; se necessário, manter o
  IP completo em retenção curta e pseudonimizado no coletor (LGPD).
- **Verificação:** `docker compose logs web --tail=1 | jq .`; request com `Cookie` e conferir
  ausência do valor; `/health` não aparece.
- **P/E:** P0 / B (refina O-01).

### SK-06 — PHP: desligar `allow_url_fopen`

- **Skill:** `php-84-correct/25-config-web-phpini.md:23-24,40-41`.
- **Atual:** `docker/php-fpm/php.ini` não define `allow_url_fopen` (default `On`) nem
  `allow_url_include` (default `Off`, mas implícito).
- **Ajuste:** `allow_url_fopen = Off`, `allow_url_include = Off`.
- **Ganho:** um include/fopen remoto deixa de ser possível mesmo com falha de validação; reduz a
  superfície de SSRF local.
- **Risco:** o app usa `fopen` apenas em caminhos locais/`php://temp`
  (`src/core/Internal/System/Storage/Console/*`, `src/services/Media/Storage/SecureMediaStorage.php:143-202`).
  Dependências que abrem URL por stream deixariam de funcionar; a varredura não encontrou uso em
  `src/` e o README do Guzzle é apenas exemplo. Validar com `make check` + upload/backup.
- **Verificação:** `php -i | grep allow_url_fopen`; suíte; upload de mídia; push/pull de backup.
- **P/E:** P1 / B.

### SK-07 — PHP: `disable_functions` por pool, sem quebrar `proc_open`

- **Skill:** `php-84-correct/25-config-web-phpini.md:26,48` recomenda desabilitar
  `exec,shell_exec,system,passthru,proc_open,popen,pcntl_exec`.
- **Atual:** não há `disable_functions` em `docker/php-fpm/php.ini` nem no pool
  (`docker/php-fpm/zz-custom.conf`).
- **Ajuste:** aplicar **só no pool web** (`php_admin_value[disable_functions]` em `zz-custom.conf`)
  com `exec,shell_exec,system,passthru,popen,pcntl_exec` — **sem `proc_open`**. O `queue` usa a
  mesma configuração de pool; se jobs precisarem de `exec`, criar um pool/worker dedicado ou
  sobrescrever no `queue`.
- **Ganho:** um RCE no request web perde os atalhos de execução; `proc_open` continua disponível
  para o fluxo de mídia.
- **Risco:** o Media usa `Symfony\Component\Process\Process` para `vips`/`qpdf`
  (`src/services/Media/Optimizers/ImageOptimizer.php:64,90`,
  `src/services/Media/Optimizers/PDFOptimizer.php:36`), que depende de `proc_open`. Desabilitar
  `proc_open` quebraria imagem/PDF. `MakeModuleCommand` também usa Process, mas é CLI.
- **Verificação:** upload de imagem + PDF via CMS; `php -r 'var_dump(function_exists("exec"));'`
  dentro do FPM (deve ser `false`); `make check`.
- **P/E:** P1 / M.

### SK-08 — PHP: `open_basedir` por pool (condicionado)

- **Skill:** `php-84-correct/25-config-web-phpini.md:25,42`.
- **Atual:** ausente.
- **Ajuste:** `php_admin_value[open_basedir] = /var/www/html:/tmp` no pool (incluir o caminho real
  de `STORAGE_PATH` se ele não estiver sob o docroot). Não aplicar no CLI — `composer`/`artisan`
  do deploy continuam lendo `~/.composer` e o próprio repo.
- **Ganho:** um LFI/leitura arbitrária no request web não alcança `/etc`, `/proc` ou o home do
  host.
- **Risco:** `proc_open` de `vips`/`qpdf` e leitura de `/proc/self/fd/2` para logs podem ser
  afetados; o `error_log` é `/proc/self/fd/2`. Testar mídia, logs, upload e `make check` antes de
  manter. Se houver dúvida, ficar em `disable_functions` (SK-07).
- **Verificação:** request web + upload + `grep -R 'open_basedir restriction' storage/logs`.
- **P/E:** P2 / M.

### SK-09 — PHP: orçamento de tempo coerente com 110 MiB

- **Skill:** `php-84-correct/25-config-web-phpini.md:118-139` recomenda `max_input_time=60` e
  `max_execution_time=30`.
- **Atual:** `max_input_time=30` (`docker/php-fpm/php.ini:41`); o pool não tem
  `request_terminate_timeout` (`zz-custom.conf:12-27`); a proposta P-02 sugere 35 s.
- **Ajuste:** decidir o orçamento:
  - upload de até 110 MiB em 2 MB/s ≈ 55 s → `max_input_time=60`;
  - `request_terminate_timeout` precisa ser **maior** que `max_input_time` + execução
    (`120s`) ou o worker morre no meio do upload;
  - `max_execution_time=30` continua valendo para CPU.
  Mitigar o custo de um worker preso por até 120 s com rate limit na aplicação e
  `RequestReadTimeout ... MinRate=500` no Apache.
- **Ganho:** uploads grandes deixam de falhar por timeout; o limite fica explícito e testável.
- **Risco:** aumentar `request_terminate_timeout` amplia a janela de ocupação de worker; sem rate
  limit, é um vetor de exaustão. Alternativa conservadora: manter 35 s e limitar o upload a
  arquivos que caibam nessa janela (documentar).
- **Verificação:** upload de 100 MiB com `--limit-rate 2M`; `docker compose logs php-fpm` sem
  “execution timed out”; `request_slowlog_timeout` não dispara em upload normal.
- **P/E:** P1 / M (depende de SK-01).

### SK-10 — Queue: timeout explícito, backoff e poda

- **Skill:** `laravel-12/19-deploy-e-escala.md:117-131` (`--timeout=120`, `--tries=3`,
  `--backoff`, `queue:prune-failed --hours=48`).
- **Atual:** `docker-compose.yml:71` usa `--sleep=1 --tries=3 --max-time=3600 --max-jobs=1000`,
  **sem** `--timeout` (default 60 s) e sem `--backoff`; não há poda de `failed_jobs`.
- **Ajuste:** `--timeout=120 --backoff=3 --max-time=3600 --max-jobs=1000`;
  `stop_grace_period: 150s` (maior que o timeout); agendar
  `php artisan queue:prune-failed --hours=48` (diário) e `queue:prune-batches` se aplicável.
- **Ganho:** jobs de mídia/PDF que passam de 60 s deixam de ser mortos e re-tentados; backoff
  evita retry em rajada; a tabela `failed_jobs` não cresce sem limite.
- **Risco:** job travado ocupa o worker por até 120 s; `stop_grace_period` precisa ser maior que
  `--timeout` para o SIGTERM terminar o job.
- **Verificação:** job artificial de 90 s; `docker compose stop queue` e conferir conclusão;
  `queue:failed` e a poda.
- **P/E:** P1 / B (refina R-02).

### SK-11 — Postgres: compressão de WAL e checkpoints

- **Skill:** `postgresql/06-configuracao-producao.md:45-57` (`wal_compression=lz4|zstd`,
  `checkpoint_timeout`, `max_wal_size`, `min_wal_size`); perfil pequeno em `:163-169`.
- **Atual:** `docker/postgres/postgresql.conf` não define `wal_compression` nem checkpoint;
  vale o default (5 min, 1 GB) e `archive_mode=on` com `archive_timeout=300` (`:37-40`).
- **Ajuste:** `wal_compression = lz4`, `checkpoint_timeout = 10min`,
  `checkpoint_completion_target = 0.9`, `max_wal_size = 1GB`, `min_wal_size = 256MB`.
  Manter `archive_timeout=300` até R-05 (retenção/offsite) estar resolvido; o valor 60 s da skill
  multiplicaria por 5 o volume arquivado localmente.
- **Ganho:** menos I/O de WAL e checkpoints menos frequentes; menos picos de latência em escrita.
- **Risco:** `max_wal_size` maior ocupa mais disco; `lz4` exige build com liblz4 (imagem oficial
  tem). Validar `SHOW wal_compression`.
- **Verificação:** `SHOW wal_compression; SHOW checkpoint_timeout;` logs de checkpoint; volume de
  `pg_wal`/`wal_archive` em 24 h.
- **P/E:** P1 / B.

### SK-12 — Postgres: autovacuum e manutenção

- **Skill:** `postgresql/06-configuracao-producao.md:88-98`;
  `postgresql/09-manutencao-monitoramento.md:58-68`.
- **Atual:** defaults (`autovacuum_naptime=1min`, scale factor 0.2/0.1,
  `maintenance_work_mem=64MB`).
- **Ajuste:** `maintenance_work_mem = 256MB`, `autovacuum_naptime = 30s`,
  `autovacuum_vacuum_scale_factor = 0.05`, `autovacuum_analyze_scale_factor = 0.02`,
  `autovacuum_vacuum_cost_delay = 2ms`, `track_wal_io_timing = on`. Avaliar
  `max_connections = 50` (10 workers FPM + 1 queue + CLI + migrações é folgado).
- **Ganho:** menos bloat e planos desatualizados em tabelas de conteúdo/auditoria; vacuum e
  `CREATE INDEX` mais rápidos; menos conexões ociosas consumindo memória.
- **Risco:** autovacuum mais agressivo gera I/O; `max_connections` baixo pode esgotar se houver
  picos de CLI/migração concorrente. Medir com `pg_stat_activity` e `pg_stat_user_tables`.
- **Verificação:** `SHOW autovacuum_naptime`, `SELECT * FROM pg_stat_user_tables ORDER BY n_dead_tup DESC`;
  `pg_stat_activity` sob carga.
- **P/E:** P2 / B.

### SK-13 — Backup diário verificado (não só no deploy)

- **Skill:** `postgresql/05-backup-pitr.md:197-205` (base backup diário, restore testado,
  retenção) e `:33` (archive que falha enche `pg_wal` até o disco); `09-manutencao-monitoramento.md:249`.
- **Atual:** `up_prod.sh` faz `pg_dump -Fc` **antes de migrar** (`up_prod.sh:378-388`) e poda por
  `BACKUP_KEEP`; não há backup diário independente de deploy, nem teste de restore automatizado.
- **Ajuste:** serviço/profile `backup` (imagem `postgres:18` pinada) com `pg_dump -Fc` diário,
  `pg_restore --list` como validação, retenção e envio para `BACKUP_DISK=offsite`; ou cron do host
  chamando o mesmo comando do `up_prod.sh` (`--backup-only`). Alertar se
  `pg_stat_archiver.failed_count` subir ou `wal_archive` crescer sem poda.
- **Ganho:** RPO real independente de deploy; restore comprovado; disco protegido.
- **Risco:** o dump diário compete por I/O; rodar fora do pico e com `--compress=9`/`-Z`.
  Nunca sobrescrever o dump anterior antes de validar o novo.
- **Verificação:** restaurar em base limpa e comparar `pg_restore --list`; `pg_stat_archiver`.
- **P/E:** P2 / M.

## Validado — não mexer

| Tema | Evidência da skill | Estado atual |
|---|---|---|
| Dono do TLS/HSTS | `caddy/06-security-tls.md:303-315` (HSTS no edge) | Correto: TLS/HSTS no Apache (`docker/web/app-ssl.conf:10-17`), Caddy sem TLS (`Caddyfile:5`). |
| `trusted_proxies` | `caddy/07-reverse-proxy.md:194-198` | Correto: Caddy confia nas faixas privadas (`Caddyfile:8-9`) e o Laravel em `config/app.php:66`. O `X-Forwarded-Proto: https` do Apache é honrado. |
| Compressão e precompressed | `caddy/05-performance.md:29-56` | Correto: `encode zstd gzip` (`Caddyfile:23`), `precompressed br gzip` (`:39`) e `.br`/`.gz` determinísticos (`vite.config.js:41-66`). |
| Assets imutáveis | `web-performance/15-delivery-and-caching.md:44-72` | Correto: `Cache-Control: public, max-age=31536000, immutable` para `/build/assets/*` (`Caddyfile:81-82`). |
| OPcache + reload no deploy | `php-84-correct/15-performance-84.md:112-113`; `07-performance-baseline.md:28-30` | Correto: `validate_timestamps=0` em produção (`php.ini:25`) e `kill -USR2 1` no `up_prod.sh:406`. |
| `zend.assertions=-1` | `php-84-correct/25-config-web-phpini.md:87,103` | Correto: o entrypoint grava `zend.assertions=-1` em produção (`docker/php-fpm/entrypoint.sh:46-56,68`). |
| Cookies de sessão | `php-84-correct/25-config-web-phpini.md:27-30` | Correto: `HttpOnly`, `Secure`, `SameSite=Lax`, `use_strict_mode` (`php.ini:47-50`). |
| `expose_php`/`display_errors`/`cgi.fix_pathinfo` | `php-84-correct/25-config-web-phpini.md:19-22,31` | Correto: `php.ini:4-8,53`. |

O item de maior valor aqui é o **primeiro**: a skill de Caddy recomenda HSTS no edge, mas o
contrato do V7 é o oposto (Apache é o dono); mover isso para o Caddy criaria header duplicado e
quebraria o `security-check`.

## Onde a skill conflita com este runtime (não adotar)

| Recomendação da skill | Evidência | Por que não | Substituto |
|---|---|---|---|
| Caddy como edge TLS/HTTP3 (`auto_https`, `key_type`, OCSP, 0-RTT, `Alt-Svc`) | `caddy/06-security-tls.md:3-25,210-260,303-315`; `caddy/05-performance.md:180-217` | O TLS/HSTS é do Apache do WHM (ADR-005/spec 16); Caddy é HTTP em `:8080`. | `auto_https off`, sem HSTS/Alt-Svc no Caddy (já). |
| HSTS no Caddy | `caddy/06-security-tls.md:310` | Dois donos do header; preload exige cuidado. | `docker/web/app-ssl.conf:17`. |
| `read_body 10s`/`write 30s` como default | `caddy/05-performance.md:98-100` | Corta upload/download de 110 MiB. | SK-01. |
| `max_header_size 1MB` como default | `caddy/05-performance.md:226` | Default real é 16 KiB; 1 MB amplia memória. | SK-02. |
| `shutdown_delay 30s` + 503 no `/health` | `caddy/05-performance.md:244-259`; `caddy/09-deployment.md:272-283` | O Apache usa `retry=0`; 503 derruba o cliente em vez de re-tentar. | SK-03 (`grace_period` apenas). |
| `resolvers 8.8.8.8` para upstream dinâmico | `caddy/07-reverse-proxy.md:219` | O upstream é estático (`php-fpm:9000`); resolver externo contornaria o DNS do Docker. | DNS interno do Compose. |
| Postgres `listen_addresses='localhost'` | `postgresql/06-configuracao-producao.md:70` | O FPM conecta por outro container; localhost isola o banco do app. | `'*'` na rede `internal`, sem porta publicada. |
| Postgres `ssl=on`/`sslmode=verify-full` | `postgresql/06-configuracao-producao.md:76-78`; `10-client-security.md:13,32` | Rede Docker interna e isolada; TLS adiciona CPU/complexidade sem ameaça correspondente. | Conexão interna + senha; reavaliar se a rede for compartilhada. |
| Postgres `log_destination=csvlog` + collector | `postgresql/06-configuracao-producao.md:118-124` | Contêiner coleta `stderr`; arquivo dentro do PGDATA duplica rotação. | `log_destination=stderr` (já). |
| `archive_timeout=60` | `postgresql/06-configuracao-producao.md:62` | Sem poda/offsite, multiplica o lixo em `wal_archive`. | 300 s + R-05/SK-13. |
| PHP `opcache.jit=tracing` | `php-84-correct/25-config-web-phpini.md:83,99` | A medição do projeto mostrou `tracing` 2,4× pior (spec 17 §4). | `jit=off` (medido). |
| `opcache.preload_user=www-data` | `php-84-correct/25-config-web-phpini.md:82,98` | O FPM do V7 roda non-root; `preload_user` só faz sentido com master root. | Se adotar preload, omitir `preload_user` (P-09). |
| `error_log` em arquivo + `display_errors` do exemplo | `php-84-correct/25-config-web-phpini.md:36-39` | O container loga em stderr e exibe erro só em dev. | `/proc/self/fd/2` (já). |
| Supervisor para `queue:work` | `laravel-12/19-deploy-e-escala.md:129,137-142` | O Docker já reinicia o serviço; Supervisor duplicaria o dono do processo. | `restart: unless-stopped` + R-02/SK-10. |
| Octane/Horizon | `laravel-12/19-deploy-e-escala.md:61-90`; `16-observabilidade-e-cache.md:30-34` | Decisão registrada: sem Octane/Horizon (simplicidade, mínimas dependências). | FPM + queue nativo. |
| CSP com nonce | `vite/11-deployment.md:120,134-135` | A CSP do V7 é cacheável e sem nonce (ADR-022); nonce por request quebra ETag/cache. | CSP estática no Caddy (já). |
| Cache/compressão via `.htaccess` | `vite/11-deployment.md:72-87` | Apache é proxy burro e não lê `.htaccess`; Caddy serve os assets. | `Caddyfile:79-82` + precompressed. |

## Lacunas das skills

1. **Pool FPM não é coberto.** Nenhuma skill trata `pm.max_children`, `request_terminate_timeout`,
   `slowlog`, `ping.path`, `rlimit_files`, `listen.backlog` ou `clear_env`; os valores de
   `docker/php-fpm/zz-custom.conf` não têm respaldo documental. P-02/S-05 continuam sendo a
   referência, com medição.
2. **Log JSON estruturado não é coberto.** As skills de Laravel citam canais Monolog e `daily`,
   mas não `JsonFormatter`/stderr; o contrato do V7 (ADR-024) vem do projeto, não das skills.
3. **Contexto de container é ausente.** As recomendações assumem host dedicado, systemd e TLS local;
   por isso vários defaults precisam ser rejeitados (tabela acima).
4. **Defaults mudam entre versões.** `max_header_size` é o exemplo: a skill diz 1 MB, a Caddy 2.11
   diz 16 KiB. Sempre validar com a versão pinada.

## Verificação sugerida (após aplicar)

```bash
docker compose config -q
docker compose up -d
curl --limit-rate 2M -F "file=@amostra-100mib.bin" http://127.0.0.1:8080/cms/media
curl --limit-rate 2M -o /dev/null http://127.0.0.1:8080/arquivo-grande.pdf
docker compose logs web --tail=5
docker compose exec php-fpm php -i | grep -E 'allow_url_fopen|disable_functions|max_input_time'
docker compose exec php-fpm php -r 'var_dump(function_exists("exec"), function_exists("proc_open"));'
docker compose exec postgres psql -U "$DB_USERNAME" -d "$DB_DATABASE" \
  -c 'show wal_compression' -c 'show checkpoint_timeout' -c 'show autovacuum_naptime'
make check
```

Para o que muda borda/headers (SK-01..SK-05), `make check E2E=1` é obrigatório (spec 16).

## RustFS (skill `rustfs`)

A skill `rustfs` foi adicionada depois desta revisão e tem documento próprio:
`08-rustfs-storage.md`. Ela cobre o que as skills de Caddy/PHP/Postgres não cobrem — topologia
single-drive/`EC:0`, readiness, backup S3, IAM do storage, versioning/lifecycle e durabilidade.
O achado P0 é o backup da mídia (RS-06), que não existe hoje; os demais estão priorizados em
`08` para não duplicar os itens `SK-xx`.
