teqo-backup/docs/plans/ops69-admin-branco-importmap-s3-impl.md
Francisco Solla 7162c65e37 ops(OPS69): regenera importMap com o S3ClientUploadHandler (admin abria em branco em prod)
O importMap commitado ficou sem a entrada @payloadcms/storage-s3/client#S3ClientUploadHandler
desde o OPS52 (dabc00f4 removeu o handler do blob sem adicionar o do S3, porque o
generate:importmap rodou sem as envs S3_*). Com o plugin s3Storage condicional, sem as
envs o importMap sai consistente (dev/test) mas em produção (envs ativas) o admin falhava
ao montar a view — getFromImportMap: PayloadComponent not found in importMap — login nunca
renderizava. Fix: regenerar com as 4 S3_* dummy inline (loadEnvConfig do @next/env não
sobrescreve envs do processo; iterateConfig não toca DB) — diff de exatamente +2 linhas.
Smoke headless com S3 ativo: /admin/login renderiza, zero erros de console. Gates verdes
(tsc/lint/format/knip/cycles/unit+int 791/build). Sem migration/access/UI. Docs: nota no
AGENTS.md (generate:importmap precisa das S3_* enquanto o plugin for condicional) +
changelog. Guard de CI registrado como OPS70 (#87, depends #77).
2026-08-19 12:45:22 -03:00

73 lines
6.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Impl: Admin do Payload abre em branco em produção (importMap sem o handler do storage S3)
Status: aprovado
Atualizado em: 2026-08-19
Issue: #77
Intenção: docs/plans/ops69-admin-branco-importmap-s3.md
Appetite restante: herdado (~0,25–0,5 dia eng)
## Leitura da intenção
- **Outcome:** o `/admin` renderiza o formulário de login em produção; o importMap commitado volta a ser consistente com o config que as envs de produção ativam; dev/test sem `S3_*` seguem com storage local.
- **O que NÃO negociar:** não mexer no contrato `/api/media/file/...`, não mudar o storage (Garage S3), não mudar o comportamento do plugin em dev/test, não tocar em nada além do fix mínimo do P0.
- **O que reavaliar:** a hipótese "regenerar com envs S3 + diff só de storage" — verificado em evidência da intenção (reprodução local); a mecânica do `payload generate:importmap` foi confirmada no código (gera sem tocar DB; plugin só registra o handler quando ativo; env inline no processo vence os env-files via `loadEnvConfig` do `@next/env`).
## Abordagem recomendada
```mermaid
flowchart LR
A[generate:importmap com S3_* dummy inline] --> B[Diff: só +2 linhas do S3ClientUploadHandler]
B --> C[Gates locais: tsc/lint/format/knip/cycles/unit+int/build]
C --> D[Smoke: dev local com S3_* dummy → /admin renderiza]
D --> E[changelog + linha AGENTS.md + item sucessor do guard]
E --> F[PR em main → CI verde → deploy automático]
```
**Opções consideradas:** A | B | C | D
**Recomendação:** **A — regenerar o importMap com as 4 envs `S3_*` (valores dummy inline) e commitar o diff**, porque é o fix mínimo que elimina exatamente a divergência dev/prod: o `generate:importmap` walka o config via `iterateConfig` (sem DB), o plugin `s3Storage` registra o `ClientUploadHandler` da collection `media` só quando `mediaStorage.enabled`, e o CLI carrega env-files com `@next/env` **sem sobrescrever** envs já no processo — valores dummy inline garantem o enable sem depender de `.env.local`/secrets.
**Alternativas rejeitadas:**
- **B — editar `importMap.js` à mão:** arquivo gerado; o próximo `pnpm generate:importmap` apaga a edição e a divergência volta sem aviso; nenhuma verificação de consistência.
- **C — tornar o `s3Storage` incondicional (sempre ativo, inclusive sem envs):** quebra dev/test (anti-goal da intenção — storage local sem `S3_*`), exigiria Garage local em todo dev e muda o aceite OPS52.
- **D — guard de CI embutido neste item (rodar `generate:importmap` com envs dummy no gate e falhar se o arquivo commitado divergir):** é a resposta certa para a classe de bug, mas a intenção cortou explicitamente melhorias de build/deploy do fix mínimo (questão em aberto → "C como item sucessor"). Registra-se como Issue sucessora no fechamento (`depends` em #77).
### Componentes / mudanças
- **`src/app/(payload)/admin/importMap.js`** (gerado, commitado): ganha 2 linhas — import + entrada `@payloadcms/storage-s3/client#S3ClientUploadHandler` (export confirmado em `node_modules/@payloadcms/storage-s3/dist/exports/client.js`). Nenhuma outra entrada muda.
- **`package.json` / scripts:** nada muda; usa-se o `pnpm generate:importmap` existente com envs inline.
- **`AGENTS.md`:** uma linha na checklist de verificação local — `generate:importmap` deve rodar com as envs `S3_*` setadas (dummy ok) enquanto o plugin de storage for condicional; sem elas o importMap fica órfão do handler (classe OPS69).
- **Migration:** sem migration. **Access / Consent:** não se aplica. **UI:** Impeccable A — sem UI nova.
### Guardas de consistência da regeneração
Antes de commitar: `git diff` do `importMap.js` deve conter **exclusivamente** as 2 linhas do storage S3. Se aparecer qualquer outra entrada (drift de componentes admin desde a última regen, `a638a8ac`), **parar e investigar**: se o drift é real (componente admin novo sem regen posterior), incluí-lo é consistência correta, mas exige conferir o commit que o introduziu; nunca commitar um diff não compreendido.
## Fases verificáveis
1. **Regeneração** — `S3_BUCKET=teqo-media S3_ENDPOINT=http://localhost:3900 S3_ACCESS_KEY_ID=test S3_SECRET_ACCESS_KEY=test pnpm generate:importmap`; diff restrito às 2 linhas do S3. (Quota: minutos)
2. **Verificação de consistência + smoke** — `git grep` do handler no importMap; `pnpm dev` local com as mesmas envs dummy: `/admin/login` renderiza o formulário (o repro da intenção documenta que com S3 ativas + importMap órfão o admin fica branco; com o handler presente, renderiza). O e2e `admin.e2e.spec.ts` roda sem `S3_*` (CI) e continua passando — a entrada extra fica inerte.
3. **Gates** — `tsc --noEmit`, `pnpm lint`, `pnpm format:check`, `pnpm exec knip`, `pnpm check:cycles`, `pnpm test`, `pnpm build` (DB local). E2e: CI (PR).
4. **Fechamento** — `docs/changelog/2026-08-19-ops69.md` + `pnpm changelog:build`; linha no AGENTS.md; registrar Issue sucessora do guard via capture-review-debts (`depends` #77).
## Rabbit holes / Não escopo (engenharia)
- **Regenerar e ver drift** — acima: diff não compreendido = parada obrigatória.
- **"Aproveita e roda o guard no CI já"** — item sucessor, não este P0.
- **Perseguir o erro de beacon do Cloudflare** — ruído de console, fora de escopo (já cortado na intenção).
- **Testar upload real contra Garage local no dev** — o smoke só precisa que a view renderize; o fluxo de upload S3 já é coberto pelo OPS52 em produção.
## Riscos e mitigação
- **Regeneração traz drift além do S3** (componente admin adicionado desde `a638a8ac` sem regen): parar, conferir o commit que introduziu o componente, incluir só se for consistência legítima.
- **Alguém regenera sem envs no futuro e reintroduz o bug:** mitigado pela linha do AGENTS.md + Issue sucessora do guard de CI (que falha o gate na divergência).
- **Deploy em produção com o importMap novo:** verificação pós-deploy pelo runbook `docs/ops/teqo-1313-deploy.md` — `/admin` abre o login e o log do container não re-emite `getFromImportMap: PayloadComponent not found in importMap` para `@payloadcms/storage-s3/client#S3ClientUploadHandler`.
## Aceite de engenharia
- [ ] Aceite de produto da intenção ainda coberto (admin renderiza em prod; upload S3 intacto; dev/test sem `S3_*` intactos)
- [ ] Invariantes AGENTS/engineering-standards (nenhum schema/access/UI tocado; importMap é arquivo gerado e commitado)
- [ ] Diff do importMap restrito às 2 linhas do handler S3, verificado e compreendido
- [ ] Gates verdes (tsc/lint/format/knip/cycles/unit+int/build; e2e no CI)
- [ ] Changelog + AGENTS.md + Issue sucessora do guard registrada