# F5.I3.T2 — `ETag` estável + assets `immutable` + FPC opcional

| Campo | Valor |
|---|---|
| **Fase** | F5 — Borda |
| **Issue** | F5.I3 — Cache, resposta e hardening (plano 18) |
| **kd task** | `task_00eeu3vw` |
| **Âncora** | `wiki/implementation/05-borda.md` |
| **Status da revisão** | 🔧 com correções |

## O que foi implementado

- `ETag` estável por hash do corpo, **sem** `'nonce-'`/`unsafe-inline` no HTML nem na CSP (gate de
  fonte em `SecurityHeadersTest` + e2e `14`).
- Assets do Vite com hash no nome → `Cache-Control: public, max-age=31536000, immutable` no edge
  (`@viteAssets` no `docker/web/Caddyfile`; cobre também `.br`/`.gz`).
- **FPC (Valkey) descartado** — ADR-022 §6 prevê descartar sem ganho demonstrado.

## Arquivos para revisar

- `docker/web/Caddyfile` (matcher `@viteAssets` + CSP)
- `tests/Feature/SecurityHeadersTest.php`
- `src/tests-e2e/tests/14-security-headers.spec.ts`
- `wiki/specs/05-seguranca-lgpd.md`

## Pontos de revisão

### PHP 8.4 / Laravel 12
- [ ] Nada de PHP novo aqui; só o edge (Caddy) e o gate.

### Segurança / ADRs
- [ ] ADR-022 §5 (assets imutáveis) e ADR-002 (zero CDN).
- [ ] O `immutable` não deve se aplicar a HTML/arquivos sem hash.

### Observabilidade/segurança/testes
- [ ] Gate de fonte (`SecurityHeadersTest`) + e2e que mede o header real no edge.
- [ ] `make check E2E=1` 2× verde.

### Específicos desta task

- [ ] `.br`/`.gz` herdam o `immutable` (servidos pelo mesmo path).
- [ ] FPC fica documentado como descartado (com justificativa), não como pendência.

## Comandos úteis

```bash
make check E2E=1
tools/e2e.sh test tests/14-security-headers.spec.ts
```

---

> Revisar contra `AGENTS.md`, `wiki/adr/` e o plano citado. Registrar achados no `kd` (`kd write --anchor`) e, se houver correção, teste de regressão.

## Notas da revisão

| Data | Revisor | Resultado | Observações |
|---|---|---|---|
| 2026-10-19 | agente (PROTOCOLO v2.0) | 🔧 | **A** (**médio**, D13/ADR-022) o bundle novo `src/services/Gallery/Assets/js/batch.js` entrou no `vite.config.js` **fora** do `BUDGET_KB` — tamanho e variantes precomprimidas não eram cobrados → entrada de 8 KB (medido ~2,2 KB brotli) (`fact_01ls8rto`); **B** (**alto**, D13) o `make check` só construía assets quando o manifesto **não existia**: manifesto velho quebrava 21 testes com “Unable to locate file in Vite manifest” → `assets_stale()` por mtime + gate `AssetsManifestTest` (vermelho provado removendo `batch.js` do manifesto) (`fact_01jvjp28`). **Verificado OK**: CSP sem `'nonce-'`/`unsafe-inline` (gate de fonte + e2e 14), `@viteAssets` cobrindo `.br`/`.gz` (`/build/assets/*`), `immutable` medido no edge (e2e), FPC descartado com justificativa na ADR-022 §6 (sem pendência fantasma). |

Evidência: `make check E2E=1` verde (22 OK · 0 FAIL · 1 SKIP); `PerformanceBudgetTest` 5/5; `AssetsManifestTest` 1/1. |

> Resultado: `✅ revisado` · `🔧 com correções` · `⏭️ delegado` (com justificativa).
