Merge escala-dry B68+ into O0+ (same kimi-k3-low model). Split B79 chassis from drawer search (B91) so B80–B90 unblock without the search half. Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
11 KiB
Escala e DRY P2 — pós-Onda 0 + pós-B68 (consolidado)
Status: Fase 1 (Onda 0 revalidate) entregue; fases 2–5 O0 + F1–F2 B68 pendentes
Atualizado em: 2026-07-31
Issue: #35 (O0+) — absorveu B68+ (#39) em 2026-07-31 (mesmo model: kimi-k3-low)
Priority: P2
Model: kimi-k3-low
Item do roadmap: docs/roadmap.md (Onda 0+ + Fill-in B68+)
Impeccable: A — perf/estrutura + DRY Consent/cache; sem pixel novo
Appetite: ~1–1,5 dia eng (fases O0 restantes + F1 B68; F2 B68 cortável)
Responsável: —
Contexto
A Onda 0 (onda-0.md) entregou textos provisórios de Consent, global privacy-policy, rota /privacidade, migrations 20260719_054706_* / 20260719_054707_*, CLI pnpm db:seed:consent e duas passagens /simplify (2026-07-19). O cleanup aplicado cobriu: migration de dados só via SQL, constantes ONDA0_CONSENT_ENTRIES / ONDA0_PRIVACY_POLICY_BODY, batch find no provision Payload, DELETE … IN no down SQL, memoização de getCachedGlobal, singleton em /privacidade, resolução de conflito no roadmap.
Os três revisores do /simplify (quality / performance / reuse) deixaram débitos maiores que cleanup pontual, registrados aqui para não se perderem entre deploy e revisão jurídica.
- Cache de globals após writes fora do runtime Payload.
provisionOnda0ConsentAndPrivacyDb(deploy) eseed-consent.mjscomskipRevalidationnão disparamrevalidateTag('global_privacy-policy')./privacidadepode servirpublished/body stale até TTL ou até um edit no admin. O endpointPOST /api/revalidatesó bustaposts. - Chaves de Consent duplicadas.
lideranca-autopreenchimentojá vive emcampaignInvite.ts;apoiador-*emcampaignConsent.ts(server-only).onda0ConsentTexts.tsainda repete literais (só a chave de liderança foi unificada).campanha-notificacoes-pushsó existe em Onda 0 até D2. - Cobertura de testes incompleta no caminho de produção. Int tests exercitam
provisionOnda0ConsentAndPrivacy(Payload API); o deploy rodaprovisionOnda0ConsentAndPrivacyDb. Reconciliation chama o down do seed mas não asserta estado deprivacy_policy/ ausência das chaves. - DRY de superfícies públicas Lexical.
/privacidadee artigos (/[type]/[category]/[slug]) convertem Lexical→HTML com padrões CSS distintos (prosevs[&_…]). Layout editorial (SiteHeader+Footer) duplica(home)/layout.tsxsem shell compartilhado. - UX do link de privacidade.
Footersempre mostra/privacidade; compublished: falsea rota 404a (mesmo padrão da ficha de apoiador antes do seed). - Documentação redundante.
scripts/consent-texts/README.mdsó aponta paraonda0ConsentTexts.tseonda-0.md.
Já resolvido no simplify (não reabrir): tags em unstable_cache para globals; factory memoizada por (slug, depth); duplicate fetch metadata+page em /privacidade; N+1 no provision Payload; batch DELETE no down SQL; migration schema sem payload/req mortos.
Objetivos
- Deploy e
pnpm db:seed:consentdeixam/privacidadee globals cacheados consistentes sem exigir edit manual no admin. - Uma fonte de verdade para literais de chave
Consenttestável em unit tests (semserver-onlyno módulo de chaves). - Int tests cobrem o caminho SQL/migration que produção usa.
- Reduzir duplicação de layout e renderização Lexical no site público quando surgir a próxima página institucional (
Pages). - Guardrails: sem migration obrigatória; sem alterar textos jurídicos; hold de PII real permanece até assessoria.
Decisões travadas
- Um plano O0+, quatro fases ordenadas. Mesmo racional de A7/B5/C10: um registro no roadmap, PRs por fase. Ordem: revalidate/cache (correção operacional) → chaves Consent (DRY) → testes SQL → DRY UI público (só quando
Pagesou segunda página institucional justificar). - Dependência suave da Onda 0 engenharia ✓. Não bloqueia lote jurídico nem smoke com dados fictícios; bloqueia confiança em cache pós-deploy e paridade de testes.
- Módulo de chaves sem
server-only.src/lib/consentKeys.ts(ou equivalente) importável poronda0ConsentTexts.ts(unit) e re-exportado porcampaignConsent.ts/campaignInvite.ts— não o inverso. - Revalidate de globals no mesmo espírito de
posts. Estendersrc/app/(frontend)/api/revalidate/route.tscom tag opcional ou lista fixa de globals públicos; documentar no runbook Onda 0. - Cortável: Fase 4 (Lexical/layout shell) até existir
Pagesou bio/propostas; Fase 5 (Footer condicional) é barata mas opcional se o seed sempre publica em prod. - i18n e naming (AGENTS.md): identificadores em inglês (
consentKeys,revalidateGlobalTag), strings visíveis em pt-BR inalteradas.
Questões em aberto
- Revalidate automático no fim da migration vs endpoint manual? Resolvido (F1): endpoint allowlisted + curl no runbook; migration continua SQL-only.
- Unificar seed CLI no caminho SQL? Recomendação: manter Payload no CLI (valida hooks/admin); bust via curl documentado —
revalidateGlobalno CLI é no-op (seed-loader). - Extrair
LexicalProseagora ou comPages? Recomendação: adiar Fase 4 até o primeiro consumidor além de/privacidade+ artigo — evitar abstração de uma página.
Abordagem proposta
flowchart TD
O0["Onda 0 MVP ✓"] --> F1
F1["Fase 1 — revalidate globals<br/>(API + runbook)"]
F1 --> F2["Fase 2 — consentKeys.ts<br/>(DRY literais)"]
F2 --> F3["Fase 3 — int tests SQL/migration"]
F3 --> F4["Fase 4 — Lexical + EditorialLayout<br/>(quando Pages)"]
F3 --> F5["Fase 5 — Footer link condicional"]
Fase 1 — Cache de globals pós-migration/seed ✓
POST /api/revalidateaceita?tag=ou body JSON{ "tag": "..." }com allowlistposts(default) eglobal_privacy-policy; query vence sobre body.scripts/seed-consent.mjsloga curl pós-sucesso (CLI não invalida cache do runtime deployado —seed-loaderstubbanext/cache).- Checklist em onda-0.md § Smoke produção: curl
global_privacy-policyapós migrate/seed sem edit admin. getGlobalCacheTagexportado desrc/utilities/globals.ts; resolver testável emsrc/utilities/revalidateRequest.ts.
Fase 2 — Chaves Consent únicas
- Novo
src/lib/consentKeys.tsexportandoCAMPAIGN_INVITE_CONSENT_KEY,SUPPORTER_REGISTRATION_CONSENT_KEY,SUPPORTER_VOTE_INTENTION_CONSENT_KEY,CAMPAIGN_PUSH_CONSENT_KEY(constantes literais). campaignInvite.ts/campaignConsent.tsimportam e re-exportam (compatibilidade).onda0ConsentTexts.tsmontaONDA0_CONSENT_KEYS/ONDA0_CONSENT_KEY_LISTa partir do módulo.- Quando D2 iniciar,
get/requireCampaignPushConsentusaCAMPAIGN_PUSH_CONSENT_KEYdo mesmo módulo.
Fase 3 — Testes do caminho SQL
- Int spec:
provisionOnda0ConsentAndPrivacyDb+removeOnda0ConsentAndPrivacyDbcontrateqo_test, ou invocar migration054707up/down diretamente. - Assertar: quatro chaves presentes após up;
privacy_policy.published === true; após down chaves removidas epublished === false. - Em
campaignMigrationReconciliation.int.spec.ts: apósonda0Seed.down, assertar ausência das chaves Onda 0 (opcional: countprivacy_policy).
Fase 4 — DRY site público Lexical/layout (adiar até Pages)
- Componente
LexicalArticleou reutilizar classes do artigo de post em/privacidade. EditorialPageLayoutextraindo(home)/layout.tsxeprivacidade/layout.tsx.unstable_cachewrapper para HTML convertido keyed porupdatedAt+ tag global.
Fase 5 — Footer e metadata (opcional)
Footerserver component:getCachedGlobal('privacy-policy')depth 0,select/campopublished— esconder link se false.generateMetadatade/privacidade: sufixo| ${metadata.title}do globalmetadata(cosmético).
Migration: nenhuma nas fases 1–5.
Dependências
- Dura: Onda 0 engenharia entregue (migrations +
/privacidade+ provision utilities). - Suave: collection
Pages(AGENTS Known Gap #3) para justificar Fase 4; D2 pararequireCampaignPushConsent. - Reusa:
getCachedGlobal/revalidateGlobal(src/utilities/globals.ts),provisionOnda0ConsentAndPrivacyDb(src/utilities/onda0Provision.ts), padrãoPOST /api/revalidatede posts.
Não escopo
- Substituir textos provisórios por versão jurídica final — continua no lote Onda 0 / assessoria.
- Migrar fluxos públicos legados (
consent: 2emsubmitWhatsapp.ts) — AGENTS Known Gap #2; plano separado. - RBAC admin
users— Known Gap #1. - Links Lexical clicáveis em
/privacidadeno corpo do consent footer (/privacidadecomo texto plano é aceitável no MVP). - Unificar provision Payload e SQL num único caminho — dual path é intencional (migration sem hooks vs CLI com Payload).
Referências
docs/roadmap.md— § Onda 0, Fill-ins, Site públicodocs/plans/onda-0.md— runbook e provisionamentosrc/lib/onda0ConsentTexts.ts— textos canônicossrc/utilities/onda0Provision.ts— SQL vs Payloadsrc/utilities/globals.ts— cache de globalssrc/app/(frontend)/api/revalidate/route.ts— revalidate postssrc/app/(frontend)/privacidade/page.tsx— página públicasrc/components/Footer.tsx— link privacidadesrc/utilities/campaignConsent.ts/campaignInvite.ts— chaves runtimetests/int/onda0Provision.int.spec.ts/campaignMigrationReconciliation.int.spec.ts- AGENTS.md — Consent por chave estável,
revalidateGlobal, naming
Absorvido de B68+ (2026-07-31)
Plano original: escala-dry-pos-b68.md (Issue #39 fechada como absorvida). Mesma classe de tarefa (simplify/escala-dry → kimi-k3-low); domínio distinto (busca do Início) mas consolidado sob O0+ por decisão humana 2026-07-31.
Fase B68-F1 — RSC embed do suggest (P1 perf)
- Estender
loadCampaignHomeSummaryou extrairloadHomeSearchSuggestHitsreutilizável no mesmo request da página (page.tsx). - Passar
initialSuggestparaCampaignHomeStaffChrome→useHomeSearchResultsState({ initialSuggest }). - Client: usar payload inicial quando
uiFocused && !query.isActivee não houver stale/error; POST só para revalidação explícita ou após TTL (opcional v1: só primeiro paint). - Sem mudar política de ranking (assessor = frescor; CG = déficit + frescor). Sem migration/collection/Consent.
Fase B68-F2 — Comparator compartilhado de déficit (cortável)
- Extrair
compareNullableNumberDesc+ tiebreak hook paralib/municipalitySort.tsquando houver 3º call site com política diferente de tiebreak (hoje: B20 nome, B68 frescor, E9 lista).
Já resolvido no simplify B68 (não reabrir): aria-busy duplicado; shell de erro; id no ranker; factory toHomeSearchMunicipalityHit; bundle E8 só em priority === 'alta'.
Revisões
- 2026-07-31: Consolidação com B68+ (#39) — mesmo modelo
kimi-k3-low; fases B68-F1/F2 absorvidas neste plano; Issue #39 fechada. - 2026-07-19: Fase 1 entregue — endpoint allowlisted (
posts,global_privacy-policy),getGlobalCacheTagexportado, curl no runbook Onda 0 e lembrete noseed:consent.