- Compile docs/roadmap.md into a next-steps document (277→206 lines): R0–R5/M1–M5 tables and the delivered map/list item paragraphs compressed into 'Já entregue'; janelas, grafo and cortes list only open IDs; C10/C11/C8 fill-ins keep their original triggers; C5 moved under 'A validar'. - Record the production deploy of the destructive remodel chain (2026-07-23 Vercel builds) across roadmap, AGENTS.md, the project notebook and plans — supersedes every 'deploy pendente'; smoke pós-deploy + onboarding remain the open blockers. - Fix AGENTS.md internal contradictions: Município locked-decisions bullet (435, Camaçari whole, candidate role), campaignUser role enum, deploy-checklist item 7 as historical, access/* structure note, election-aggregates artifact + election-tse revalidate runbook. - Sync open plans to real identifiers (plaza*/nucleus* → municipality*): the intelligence program (master + E8–E15/B13/C12 with artifact/caching context and satisfied deps), B8 rescoped to Salvador ZE 1–19 only, WhatsApp D3–D5, D2, A6 (now the insight layer over stateDeputy), RS+, onda-0 smoke step. - Close items verified done in code: C8 F4 (DRY de forms) complete, filtros-auto shared pending shell, petition generateStaticParams select/depth; ledger the surviving user-visible 'Praça' copy in src as new P2 debt. - Banner delivered praça-era plans (production note + rename pointer); mark remodelagem-pracas.md superseded and remodelagem-municipios.md delivered. - TECH-DEBT smell statuses, TESTING RBAC row, README quality gates, campanha UI rules updated to current component names.
14 KiB
Escala e DRY pós-A9 (loader da lista de Praças)
Nota (2026-07-24): entregue na era Praças e em produção desde 2026-07-23. A remodelagem M1 renomeou os identificadores
plaza*→municipality*e as rotaspracas/→municipios/; os nomes neste plano são os da época. Nomes atuais: ver remodelagem-municipios.md.
Status: entregue 2026-07-21 (F1+F2; em produção desde 2026-07-23)
Atualizado em: 2026-07-21 (implementação + /simplify + rebase em main com B11; capture-review-debts pós-implementação)
Item do roadmap: docs/roadmap.md (Trilha A, fill-in A9+ pós-A9)
Impeccable: A — N/A (sem superfície UI; otimização de loader)
Appetite: ~1–1,5 dia eng (F1 loader + F2 revalidate pós-B9; PR único ou dois commits)
Responsável: —
Contexto
O A9 (estimativa-votos-praca.md) entrega plaza.expectedVotes (staff-only), setPlazaExpectedVotes, fallback expectedVotes ?? effectiveTotal em mapa/overview/dashboard, UI em /editar + leitura na lista/detalhe, migration 20260721_133444_add_plaza_expected_votes, e helpers resolvePlazaStaffVoteTotal / rollupPlazaStaffVotes / sumStaffPledgeEffectiveTotal em votePledgeData.ts.
Uma passagem /simplify pós-entrega já limpou o que cabia em cleanup: form optionalIntegerFormValue único; remoção de hasStaffVoteData; StaffPlazaVotesDisplay compartilhado; JSDoc da métrica 2026; testes unit do rollup.
O revisor de performance marcou como maior que simplify os três problemas abaixo (contexto pré-A9+). Resolvidos na entrega F1+F2:
Query triplicada de pledges—loadPlazaListPageBundlechamaaggregatePledgesByPlazauma vez; overview, mapa e coluna da página reutilizam o mesmoMap.- Dois
findde Praças no mesmo filtro — mantido de propósito (paginado + conjunto filtrado inteiro); ver “Questões em aberto”. Revalidação full-page sem escopo no detalhe—revalidatePlazaListPaths({ slug })via hiddenplazaSlug; lista ainda revalida full-page (filtros?trend=+ KPIs).
Como entregue (2026-07-21)
F1 — Loader: loadPlazaListPageBundle em plazaPageData.ts; buildPlazaMapBundleFromPlazas + scopePlazasFromDocs em plazaMapData.ts; page.tsx com uma única chamada; removidos loadPlazaListPageData / loadPlazaListOverviewData do export público.
F2 — Revalidate: plazaRevalidation.ts (revalidatePlazaListPaths com validação de slug); hidden plazaSlug em PlazaList*Control, PlazaStrategyForm, PlazaAdvisorsForm; plazaStaffFormActions + editar/formActions (via C8 F4).
Helpers compartilhados (prep C8 F4): optionalPlazaSlugFromForm, parsePoliticalTrendStatusFormValue.
Testes: tests/int/plazaPageData.int.spec.ts (4 casos: vazio, rollup, advisor+filtro, leader sem mapa/overview).
Validação: tsc --noEmit; int plazaPageData + plazaMapData verdes; Aikido 0 findings nos arquivos editados.
Já resolvido no simplify/critique (não reabrir): parsing duplo do form expectedVotes; hasStaffVoteData derivado de staffVoteTotal > 0; rollupPlazaStaffVotes + sumStaffPledgeEffectiveTotal; StaffPlazaVotesDisplay; assinatura estreita de resolvePlazaStaffVoteTotal; testes unit do rollup. Pós-B7 simplify: PlazaListSearchParams exportado de plazaUi.ts; parse único no map loader (loadScopedPlazas recebe PlazaListState). Pós-B11 simplify: Promise.all nos loads TSE por ano em loadPlazaMapBundle (votes + válidos em paralelo; branch ?compare= também paraleliza os 3 loadCandidateVotesByCityZone) — micro-opt que estava fora do escopo A9+; resolvido no cleanup B11, não reabrir aqui.
Explicitamente fora (triage capture-review-debts 2026-07-21 pós-A9): semântica lista (só expectedVotes manual) vs mapa/overview (fallback) — decisão de produto A9; merge de forms Estratégia + Votos no /editar; grid cosmético do PlazaStrategyCard; loadPlazaPledges em abas distintas do detalhe (uma aba por request); débitos adiados/não escopo do plano A9 (nota/autor, chip auto-sugerir, histórico, filtro ?expectedVotes=); critique Impeccable formal por superfície → R6.
Explicitamente fora (triage capture-review-debts 2026-07-21 pós-B7): testes int extras em plazaMapData (region/leader/compare) — cobertura opcional no próximo toque no loader; split do teste advisor por kind (legibilidade).
Explicitamente fora (triage capture-review-debts 2026-07-21 pós-B9): ~75 hooks useActionState por página (3 controles × 25 linhas × 2 views) — gatilho: reclamação de perf ou R6; merge (entregue 2026-07-21); hook popover compartilhado / layout responsivo único / lazy advisor options → gatilhos no plano B9.listFormActions ↔ /editar → C8 F4
Simplify pós-implementação (2026-07-21)
Passagem /simplify sobre o diff A9+ (F1+F2) após rebase em main com B11.
Já resolvido (não reabrir): optionalPlazaSlugFromForm (formData.ts); parsePoliticalTrendStatusFormValue (schemas/plaza.ts); colapso needsFilteredSet → isCampaignStaff; remoção de declaredVotesTotal / highPriorityCount mortos em PlazaListOverviewData; validação de slug em revalidatePlazaListPaths; compareCandidate no branch ?compare= de buildPlazaMapBundleFromPlazas; int plazaPageData sem re-query redundante de pledges.
F2 como entregue: plazaRevalidation.ts + hidden plazaSlug nos forms; lista e /editar chamam revalidatePlazaListPaths({ slug }) — revalida o detalhe de forma estreita quando o slug está presente; a lista continua com revalidatePath full-page (paridade com filtros ?trend= e KPIs). Rejeitado neste fill-in: cache por tag / matriz scope por tipo de save.
Explicitamente fora (triage capture-review-debts 2026-07-21 pós-implementação A9+): scope: 'detail' só em save de tendência (a lista precisa refletir filtro ?trend=); cache/revalidate por tag (rabbit hole); mover plazaListFilteredSelect → plazaViewModels (gatilho: 3º consumidor); paralelizar pledge aggregate + loads TSE no mesmo tick do bundle (gatilho: profiling TTFB); unificar os dois find de Praças (decisão travada em “Questões em aberto”); twin completo (entregue 2026-07-21 — ver escala-dry-pos-c6.md Fase 4).listFormActions ↔ /editar → C8 F4
Objetivos
- Abrir
/campanha/pracascom filtro amplo não executa trêsaggregatePledgesByPlazaindependentes quando um mapa de agregados compartilhado basta. - Overview, mapa 2026 e coluna da página reutilizam o mesmo
Map<plazaId, PlazaPledgeAggregate>(e, onde os filtros coincidirem, o mesmofindde Praças). - Saves inline da lista (B9) não disparam rerender full-page desnecessário quando um refresh parcial (segmento de lista, tag de cache, ou
revalidatePathestreito) preserva paridade de KPIs/mapa. - Paridade funcional: KPIs, métrica 2026 e sublinha “Nas lideranças” permanecem idênticos ao comportamento pós-A9.
- Guardrails: sem migration, sem collection, sem Consent, sem server action nova; access inalterado (
overrideAccess: falsenos reads de Praça; agregado de pledges continua admin-bypass intencional com ids já access-checked).
Decisões travadas
- Um fill-in A9+, duas fases ordenadas (F1 loader + F2 revalidate). Mesmo racional de A7/C6/A8+: um slug de plano, PR único. Rejeitado: item de roadmap separado
A10— follow-up pós-entrega, não feature nova. - Dependência dura de A9. Só faz sentido com
expectedVotes,rollupPlazaStaffVotese os três loaders já no produto; não reabre escopo de schema ou UI de edição. - F1 não remove overview nem mapa. Otimiza como os dados chegam; paridade com a plano A9 permanece. Rejeitado: unificar lista+overview num único
findunpaginado sem validar regressão de paginação/depth. - Cortável se a lista com mapa permanecer aceitável em campo com dezenas de assessores; menos cortável desde B7 entregue (mapa filtrado + sempre visível para staff amplifica o custo do triplo agregado quando o filtro é amplo).
- i18n e naming (AGENTS.md): identificadores em inglês (
loadPlazaListPageBundle,PlazaListPledgeAggregates); strings visíveis inalteradas em pt-BR.
Questões em aberto
- Bundle único vs compartilhar só o mapa de pledges? Resolvido (2026-07-21): opção (A) —
loadPlazaListPageBundlecom umfindunpaginado filtrado + umaggregatePledgesByPlaza;buildPlazaMapBundleFromPlazasrecebe agregados pré-computados. - Overview unpaginado vs página paginada? Recomendação: manter dois
findde Praças (conjunto filtrado inteiro vs página) — o gargalo registrado é o terceiro agregado de pledges, não a paginação em si; unificarfindsó se profiling mostrar custo dominante em Praças, não em pledges.
Abordagem proposta
flowchart TD
A9["A9 expectedVotes ✓"] --> F1
B9["B9 edição inline lista"] --> F2
F1["Fase 1 — loadPlazaListPageBundle<br/>1× aggregatePledgesByPlaza"]
F2["Fase 2 — revalidate escopado<br/>pós-save inline"]
F1 --> List["PlazaList página"]
F1 --> Overview["PlazaListOverview KPIs"]
F1 --> Map["PlazaMapBundle 2026"]
B7["B7 mapa filtrado ✓"] -.mesmo bundle.-> F1
F2 -.menos rerender.-> List
Fase 1 — Loader compartilhado da lista de Praças
- Introduzir
loadPlazaListPageBundle(ou equivalente emplazaPageData.ts+ ajuste fino emplazaMapData.ts) que:- Resolve filtros uma vez (
parsePlazaListParams/buildPlazaListWhere). - Carrega Praças do escopo necessário (página + overview + mapa) com o mínimo de round-trips.
- Chama
aggregatePledgesByPlazauma vez sobre a união de ids necessários (ou sobre o conjunto filtrado inteiro se mais simples e dentro do appetite). - Expõe
pledgeAggregatesreutilizado portoPlazaListViewModel,rollupPlazaStaffVoteseloadPlazaMapBundle(refatorar para aceitar agregados pré-computados ou extrair helper puro).
- Resolve filtros uma vez (
- Atualizar
src/app/(campaign)/campanha/(app)/pracas/page.tsxpara consumir o bundle. - Testes: int ou unit do bundle — paridade de
staffVoteTotale métrica 2026 antes/depois; advisor scope inalterado.
Fase 2 — Revalidação escopada pós-B9
- Avaliar
revalidatePathestreito (só segmento necessário) ou tag de cache dedicada à lista vs full-pagerevalidatePath('/campanha/pracas', 'page')emplazaStaffFormActions.ts— gatilho: N saves inline + profiling de rerender inaceitável (ver Adiado com gatilho). - Critério de aceite: após N saves inline seguidos, overview/mapa/lista permanecem corretos sem rerodar loaders caros quando o dado alterado não afeta KPI agregado (ex.: tendência não muda métrica 2026 — validar caso a caso).
Coordenar com C8 F4 se extrairEntregue 2026-07-21.revalidatePlazaListPathscompartilhado entre lista e/editar.- Testes: int leve ou e2e smoke de save inline + paridade visual de KPIs (manual ok se appetite apertar).
Migration: nenhuma.
Dependências
- Dura: A9 estimativa-votos-praca.md — campo, agregadores e superfícies (merge em
main). - Suave: B7 mapa-pracas-filtrado.md — entregue 2026-07-21; mesmo URL de filtro; F1 deduplica pledges e pode compartilhar parse/
findonde os escopos coincidem. - Suave: B9 edicao-rapida-lista-pracas.md — F2 só faz sentido com saves inline na lista; não reabre escopo de UI B9.
- Reusa:
aggregatePledgesByPlaza,rollupPlazaStaffVotes,resolvePlazaStaffVoteTotal, precedente E6 F1 em escala-dry-pos-e1.md.
Não escopo
- SQL aggregate de pledges (
COUNT(*) FILTER) — volume ainda baixo; reavaliar no 3º hot path ou com evidência de latency. - Unificar
loadPlazaPledgesentre abas Overview/Lideranças no detalhe — defer (uma aba por request). - Implementar os controles inline da lista → B9 (entregue em código; polish Impeccable → R6).
- Merge de forms no
/editar— fora do triage. - Impeccable critique das superfícies A9 → R6.
Rabbit holes
- Unificar lista+overview+mapa num único
findunpaginado de 436 Praças sempre. Mitigação: F1 foca em deduplicar pledges;findde Praças permanece paginado onde já é. - Cache cross-request de agregados. Mitigação: bundle por request RSC apenas; sem Redis/memória global neste fill-in.
Adiado com gatilho
- SQL aggregate espelhando
buildPlazaListWhere. Revisitar quando profiling mostraraggregatePledgesByPlazadominando TTFB com filtros amplos + mapa sempre montado. - Compartilhar bundle com dashboard geral. Revisitar se o dashboard passar a carregar mapa+lista na mesma rota.
- Paralelizar pledge aggregate + loads TSE históricos no bundle. Revisitar se profiling mostrar TTFB dominado pelo mapa com filtro amplo (anos TSE já paralelizam dentro de
buildPlazaMapBundleFromPlazasdesde B11/A9+ simplify). - Cache/revalidate por tag ou
scopepor tipo de save. Revisitar se N saves inline seguidos na lista mostrarem custo de rerender inaceitável em campo.
Referências
docs/roadmap.md(Trilha A, A9+ entregue 2026-07-21)docs/plans/estimativa-votos-praca.md— plano pai A9docs/plans/escala-dry-pos-e1.md— precedente E6 F1 (lista+overview núcleos)src/app/(campaign)/campanha/(app)/pracas/page.tsx— consomeloadPlazaListPageBundlesrc/app/(campaign)/campanha/(app)/pracas/plazaStaffFormActions.ts—revalidatePlazaListPathscomplazaSlugsrc/utilities/plazaPageData.ts—loadPlazaListPageBundlesrc/utilities/plazaMapData.ts—buildPlazaMapBundleFromPlazas,scopePlazasFromDocssrc/utilities/plazaRevalidation.ts— helper compartilhado (C8 F4)tests/int/plazaPageData.int.spec.ts— paridade overview/mapa/escoposrc/utilities/plazaMapData.ts—loadPlazaMapBundle, métrica 2026src/utilities/votePledgeData.ts—aggregatePledgesByPlaza,rollupPlazaStaffVotes- AGENTS.md — naming EN/pt-BR; access staff; sem PII