# F6.I2.T2 — Restore em base limpa + **PITR** ensaiado

| Campo | Valor |
|---|---|
| **Fase** | F6 — Deploy e operação |
| **Issue** | F6.I2 — Base nova: backup, restore e PITR |
| **kd task** | `task_01jy6h6i` |
| **Âncora** | `wiki/implementation/06-deploy-migracao.md` |
| **Status da revisão** | 🔧 |

## O que foi implementado

- `tools/db-restore-drill.sh`: restaura o dump numa base descartável, confere tabelas/`migrations`/
  `migrate:status` sem pendências, imprime `rto_s` e remove a base — medido **9 s**.
- PITR real: `wal_level=replica`, `archive_mode=on`, `archive_timeout=300`, `archive_command` →
  `${PGDATA}/wal_archive`. `tools/pitr-drill.sh` faz base backup, arquiva WAL, escreve `late` **depois**
  do alvo e restaura com `recovery_target_lsn` + `recovery.signal` — medido **8 s / 24 s / 23 s**,
  recuperando `base,after` **sem** `late`.

## Arquivos para revisar

- `tools/db-restore-drill.sh`
- `tools/pitr-drill.sh`
- `docker/postgres/postgresql.conf`
- `tests/Feature/DeployPipelineTest.php` (gate de `archive_mode`/`recovery.signal`)
- `wiki/runbooks/deploy-e-recuperacao.md` §3

## Pontos de revisão

### Operação e determinismo (ADR-001)
- [ ] Idempotência real (2× sem mudar estado); nada depende de relógio local, ordem implícita ou `date` não-UTC em nome de artefato.
- [ ] Falha em qualquer fase **aborta** e o rollback devolve o estado anterior; nada falha "em silêncio".

### Segurança (ADR-019/020/030/035)
- [ ] Nenhum segredo/senha/e-mail em log, stdout de artefato ou arquivo versionado.
- [ ] Dump/backup contém dado pessoal → fora do git, retenção definida, acesso restrito.
- [ ] Cópia remota confere integridade (tamanho/checksum) e não engole erro.

### Banco (ADR-007/042)
- [ ] Nenhum caminho de ETL/legado (`sys_migration`/`legacy_mapping`/`migrate:legacy`).
- [ ] Config do PostgreSQL é a mesma dev/prod; nada de `$isPgsql`/driver branching.
- [ ] `pg_dump`/`pg_restore`/`pg_basebackup` na **mesma** versão do servidor.

### Scripts de shell (D1/D13)
- [ ] `set -Eeuo pipefail`; `sc()` de erro explícito; mensagens acionáveis; nada de `rm -rf`.
- [ ] `shellcheck`-limpo no que for razoável; helpers sem duplicação desnecessária.
- [ ] Caminhos derivados do `.env`/compose (sem porta/host hardcoded).

### Comandos de console (D2/D8/D12)
- [ ] `declare(strict_types=1)`; DI por construtor/injeção (sem `new`/`app()` no corpo).
- [ ] Validação de entrada no comando; `FAILURE` com mensagem clara (sem stack trace crua).
- [ ] `log_event('{module}.{action}')` **sem PII**; auditoria automática quando há mutação Eloquent.
- [ ] Textos de saída em pt_BR (CLI), sem string fixa em view.

### Testes (D13/ADR-038)
- [ ] Teste cobre o caminho feliz **e** as recusas; prova vermelho→verde registrada no `kd`.
- [ ] Estrutural: gate impede a regressão do invariante que a task criou.

### Documentação (D14)
- [ ] Runbook/spec/plano descrevem o que o código faz (comandos, flags, env) — sem promessa não implementada.
- [ ] `docs-check` verde; DoD do plano 06 reflete o estado real.

### Específicos desta task

- [ ] Alvo por **LSN** (tempo erra por fuso/corrida do arquivador — erro medido).
- [ ] `recovery.signal` presente (sem ele o PG faz só crash recovery e promove cedo).
- [ ] Ensaio **não toca** a base de produção (DB descartável, porta separada, cleanup no fim).
- [ ] Saída JSON com `rto_s` para o `kd`; `--keep` para inspeção.
- [ ] Produção arquiva WAL **fora do host** (S3/ADR-030) — documentado, não fingido.

---

> Revisar contra `AGENTS.md`, `wiki/adr/` e `wiki/implementation/06-deploy-migracao.md`. Achado real →
> `kd write --anchor` + teste de regressão. **Não** reescrever por gosto.

## Notas da revisão

| Data | Revisor | Resultado | Observações |
|---|---|---|---|
| 2026-10-20 | agente (revisão F6) | 🔧 | **A** (alto, D13/D3) a espera pelo WAL arquivado **podia expirar em silêncio** e seguir com o restore — o ensaio poderia “passar” recuperando menos do que devia; e a `cleanup()` existia **sem nunca ser chamada** → falha explícita após o laço + `trap` de falha (respeitando `--keep`), com teste estrutural (`error_01udotff`). **B** (baixo, obs) `restore_command` fixa o `PGDATA` do container (`risk_01urn5zn`). Verificado OK: alvo por LSN (não por relógio), `recovery.signal`, instância separada na 5433, `late` ausente, RTO impresso em JSON. Ensaio repetido: **11 s**, marker `base,after`. |
