diff --git a/docs/CHANGELOG-AGENTS.md b/docs/CHANGELOG-AGENTS.md index 582596ec..f5e3a7c8 100644 --- a/docs/CHANGELOG-AGENTS.md +++ b/docs/CHANGELOG-AGENTS.md @@ -4,6 +4,8 @@ Conteúdo movido do `AGENTS.md` em 2026-07-30 (fatiamento do paradigma de agente --- +**Recently resolved (2026-08-10):** **OPS21** (Issue #538) — `REVALIDATE_SECRET` adicionada na **Vercel production** (projeto `solla/jorgesolla`): diagnóstico D6 confirmado (env ausente em `vercel env ls production`; `POST /api/revalidate` respondia `500` "not configured" em prod, quebrando o runbook pós-seed/mudança direta de DB). Segredo gerado com `openssl rand -hex 32` (valor via stdin, nunca argv), `vercel env add REVALIDATE_SECRET production`; **fallback necessário:** o deploy existente congelava as envs em runtime → `vercel redeploy` do último deploy de produção (2×, pois o primeiro `env add` via pipe herdou o `\n` do `openssl` e gravou valor de 66 chars — removido e re-adicionado limpo). Validação real em prod: sem secret → `401`; secret errado → `401`; secret certo → `200 { revalidated: true, tag: 'posts' }`; tag desconhecida → `400`. Preview não adicionada (projeto sem Git conectado — `vercel env add ... preview` exige branch, precedente D6). Sem código/migration/access/UI. Impl [`revalidate-secret-vercel-production-impl.md`](plans/revalidate-secret-vercel-production-impl.md). + **Recently resolved (2026-08-10):** **B184** (Issue #514) — no mobile (<768px), o filtro de listas `/campanha` virou o **padrão sem moldura do chassis `CampaignListOmnibox` (B127) — todas as listas**: label oculta (`input` carrega o nome via `aria-label`), campo sem borda/sombra (`md:` restaura o desktop), **form do filtro sticky sob a top bar** com linha separadora edge-to-edge, "Limpar" vira **X circular dentro do input** (aparece com chips OU busca digitada; slot sempre renderizado com `invisible` para a largura não pular; hit target 44px + círculo visual 28px; live region própria "Busca e filtros limpos." — limpar só busca não navega e o pending region ficaria mudo para AT), botão texto "Limpar" desktop-only. **Por que sticky no `
`:** sticky não escapa do containing block (o pai) — a coluna do input dentro do wrapper curto do omnibox "grudava" ~12px e rolava junto (medido); o form é o único wrapper filho do `CampaignPageShell` (altura total). **Exceção agenda (C101, merge anterior):** a `/campanha/agenda` já tinha o strip sticky próprio (`activity-agenda-filter-strip`) — mantido; o chassis ainda a cobre no campo/X/Limpar. Em municípios, adicionalmente: **"Salvar filtro" vira icon button no header mobile** (slot C94/C95, `SaveMunicipalityFilterControl presentation='icon'`; popover de nomear intacto; nome de rename é o contrato 2.5.3) e **cards edge-to-edge sem borda** com uma linha separando (`border-b last:border-b-0`, `-mx-4`, gate `md:`). "Nova atualização" (atualizações) — único trailing visível no mobile — foi para o header (mesmo padrão); `campaignMobileHeaderIconClassName` extraída (3º consumidor). E2E novo em `campaignMunicipalities.e2e.spec.ts` (390×844): label oculta, borda 0, sticky, X (limpa chips+busca, foco volta sem reabrir popover), salvar→renomear no header, cards com separador, desktop invertido (1280). Débito registrado no caminho: **D7** (sticky dos headers da tabela desktop morto — wrapper `overflow-x-auto` é o scroll container; medido). Sem migration, access, Consent. Intenção [`municipios-mobile-sem-moldura.md`](plans/municipios-mobile-sem-moldura.md); impl [`municipios-mobile-sem-moldura-impl.md`](plans/municipios-mobile-sem-moldura-impl.md). **Recently resolved (2026-08-09):** **C103** (Issue #504) — o formulário de criação rápida da agenda ficou **usável de ponta a ponta no celular**: no narrow (mesmo breakpoint 640px do calendário), o sheet de criação abre **do topo** (`swipeDirection="up"` na primitiva `Drawer` — que já suportava top sheet; zero mudança em `Drawer.tsx`) preenchendo `100dvh-1rem`, o conteúdo **rola** num scroll container e o rodapé ("Mais detalhes" + "Salvar") é **fixo** — verificado em smoke com teclado aberto (viewport 280px: sheet encolhe via `dvh`, conteúdo rola 187/187px e o Salvar permanece visível). O form ganhou `variant: 'popover' | 'sheet'`: no sheet, **labels viram `sr-only`** (a11y e `getByLabelText` intactos), placeholders dão contexto ("Adicionar título _", "Município _", "Local (opcional)", "Adicionar responsáveis"), obrigatórios marcados com asterisco (título/município no placeholder, Início no trigger), campos empilhados **sem gaps com `divide-y`** e inputs/triggers sem borda (lista iOS do canvas). O **seletor de data/hora abre como bottom sheet aninhado** no mobile (`ActivityDateTimeField` ganhou `isNarrow`/`label`/`required`: conteúdo compartilhado Calendar+selects renderizado em `Drawer` com header "Início — " + "Pronto"; desktop continua popover). Props backward-compatible: `placeholder`/`className` no `StrictCombobox`, `labelClassName`/`emptyText`/`triggerClassName` no `ResponsibleMultiSelect`. Decisão do smoke: swipe handle **removido** do top sheet (primitiva o posiciona no rodapé, contradizendo o gesto de swipe-up; continua nos bottom sheets). E2E mobile novo (viewport 390×844): sheet do topo (`y ≤ 1`), Salvar visível sem scroll, picker em bottom sheet com Pronto, evento aparece após salvar; unit narrow cobre placeholders/asterisco/Pronto. Desktop visualmente inalterado (B181 segue na fila para o clip do popover ≤720px). Sem migration/access/Consent/schema. Intenção [`agenda-mobile-form-criacao-usavel.md`](plans/agenda-mobile-form-criacao-usavel.md); impl [`agenda-mobile-form-criacao-usavel-impl.md`](plans/agenda-mobile-form-criacao-usavel-impl.md). diff --git a/docs/plans/revalidate-secret-vercel-production-impl.md b/docs/plans/revalidate-secret-vercel-production-impl.md new file mode 100644 index 00000000..22531342 --- /dev/null +++ b/docs/plans/revalidate-secret-vercel-production-impl.md @@ -0,0 +1,76 @@ +# Impl: OPS21 — Adicionar REVALIDATE_SECRET na Vercel production (runbook /api/revalidate) + +Status: aprovado +Atualizado em: 2026-08-10 +Issue: #538 +Intenção: body da Issue #538 (ops pura, sem plano de intenção separado) +Appetite restante: herdado (ops ~30min + passos de validação) + +## Leitura da intenção + +- **Outcome:** `POST /api/revalidate` responde `200` em produção com o secret correto (hoje `500`), e o runbook pós-seed / mudança direta de DB (AGENTS.md "Posts & Tags") volta a funcionar: bust do cache `posts` / `election-tse` / `global_privacy-policy` / `municipality-catalog` em produção. +- **O que NÃO negociar:** o secret nunca é impresso/logado/commitado; `vercel env add` só no projeto correto (`solla/jorgesolla`); nenhum código muda (issue diz "Sem código"). +- **O que reavaliar:** "documentar no Vercel" — não há campo de descrição em `vercel env`; documentação = registrar a entrega no `docs/CHANGELOG-AGENTS.md` (padrão do repo) e deixar a env visível em `vercel env ls production`. Preview é **opcional** e o precedente D6 mostra que `vercel env add ... preview` exige branch (`branch_not_found`) porque o projeto não tem Git conectado (deploys CLI). + +## Diagnóstico (evidência, 2026-08-10) + +| Elo | Estado | Evidência | +| --- | ------ | --------- | +| 1. Env `REVALIDATE_SECRET` (Vercel prod) | **AUSENTE** | `vercel env ls production` (projeto `solla/jorgesolla`, link em `/home/fsolla/Code/teqo/.vercel`): não listada | +| 2. Endpoint prod | **500** | `curl -X POST https://pt.jorgesolla.com.br/api/revalidate -H "x-revalidate-secret: "` → `HTTP 500` (corpo: `REVALIDATE_SECRET is not configured` — route.ts:54-58) | +| 3. Comportamento esperado pós-fix | 401/200 | secret errado → `401`; secret certo → `200 { revalidated: true }` | + +## Abordagem recomendada + +```mermaid +flowchart LR + A["Gerar secret
openssl rand -hex 32"] --> B["vercel env add REVALIDATE_SECRET production"] + B --> C["preview: tentar; fallback D6
(exige branch — sem Git conectado)"] + B --> D["curl validação:
sem secret → 401
secret errado → 401
secret certo → 200"] + D --> E["docs: impl plan +
CHANGELOG-AGENTS entry"] + E --> F["gate:fast → pnpm push →
PR Closes #538 → merge"] +``` + +**Opções consideradas:** A) gerar com `openssl rand -hex 32` | B) `openssl rand -base64 32` | C) segredo legível digitado manualmente +**Recomendação:** A — 64 chars hex, compatível com o header HTTP sem encoding, documentado em `.env.example` (`Generate with e.g. openssl rand -hex 32`). +**Rejeitadas:** B porque `+` `/` `=` do base64 pedem encoding cuidadoso em headers (funcionaria, mas sem ganho); C porque segredo de baixa entropia é fraco contra brute-force do endpoint público. + +**Opções (environments):** A) só production | B) production + preview +**Recomendação:** A — production é o que o runbook usa; preview não tem deploy ativo de código (sem Git conectado, deploys CLI) e `vercel env add ... preview` exige branch (precedente D6: `branch_not_found`). Tentativa opcional de preview documentada como falha esperada, não como passo obrigatório. +**Rejeitadas:** B como obrigatório — ver acima. + +**Opções (validação):** A) curl no endpoint prod real | B) `vercel redeploy` + curl +**Recomendação:** A — env vars runtime (não `NEXT_PUBLIC_*`) são lidas em `process.env` a cada invocação; se após `env add` o endpoint ainda responder `500`, disparar `vercel redeploy --prod` e revalidar (fallback documentado, não passo padrão). + +### Componentes / mudanças + +- **Código:** nenhum (issue "Sem código" — route.ts, `.env.example` e AGENTS.md já documentam `REVALIDATE_SECRET`). +- **Vercel ops:** `vercel env add REVALIDATE_SECRET production` (projeto `solla/jorgesolla`, link `/home/fsolla/Code/teqo/.vercel`), valor via stdin (não em argv — histórico de shell). +- **Docs:** `docs/plans/revalidate-secret-vercel-production-impl.md` (este) + entrada curta em `docs/CHANGELOG-AGENTS.md` (padrão "Recently resolved"). +- **Migration:** nenhuma. **Access / Consent:** N/A. **UI:** N/A (Impeccable A). + +## Fases verificáveis + +1. **Ops** — gerar secret, `vercel env add` production, tentativa preview (esperado falhar). +2. **Validação prod** — curl: secret ausente → `401`; secret errado → `401`; secret certo → `200 { revalidated: true, tag: 'posts' }`; tag inválida → `400`. Fallback: `vercel redeploy` se `500` persistir. +3. **Docs + gates** — CHANGELOG-AGENTS entry; `pnpm gate:fast`; `pnpm push -u origin HEAD`; PR `--base main` com `Closes #538`; merge + `gh pr checks --watch --required`. + +## Rabbit holes / Não escopo (engenharia) + +- Rotacionar `PAYLOAD_SECRET` ou outras envs (fora do escopo). +- Codificar o valor do secret em qualquer arquivo do repo (nunca). +- Adicionar descrição/suporte de env no Vercel (não existe campo). +- Mudar o endpoint `/api/revalidate` (funciona; só falta a env). + +## Riscos e mitigação + +- **Env adicionada no projeto errado:** `vercel` CLI já logado (`franciscosolla`), projeto verificado via `vercel env ls production` antes e depois (nome `solla/jorgesolla`). +- **Segredo vazado em shell history:** valor passado via stdin (`vercel env add` aceita stdin), nunca em argv. +- **Endpoint continua 500 após env add:** fallback `vercel redeploy --prod` (env runtime pode estar congelada no deploy existente); revalidar. +- **Preview impossível (`branch_not_found`):** documentado como esperado (precedente D6), não bloqueia. + +## Aceite de engenharia + +- [ ] Aceite de produto da intenção ainda coberto (runbook revalida em prod com 200) +- [ ] Invariantes AGENTS/engineering-standards (sem código, sem migration, sem access) +- [ ] Validação real em produção: 401/401/200/400 conforme fases