From f04dbe07f900b9ad35661ac5a59b2c4e64d2704b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Ign=C3=A1cio=20Fontoura=20Solla?= Date: Sat, 18 Jul 2026 22:10:47 -0300 Subject: [PATCH] Register B5 scale/DRY follow-ups after the B2 geometries simplify pass. --- .cursor/rules/projects/nucleos-eleitorais.mdc | 1 + docs/plans/escala-dry-pos-b2.md | 90 +++++++++++++++++++ docs/plans/mapa-bahia-geometrias.md | 9 +- docs/roadmap.md | 30 ++++--- 4 files changed, 115 insertions(+), 15 deletions(-) create mode 100644 docs/plans/escala-dry-pos-b2.md diff --git a/.cursor/rules/projects/nucleos-eleitorais.mdc b/.cursor/rules/projects/nucleos-eleitorais.mdc index 68950627..4ba3236a 100644 --- a/.cursor/rules/projects/nucleos-eleitorais.mdc +++ b/.cursor/rules/projects/nucleos-eleitorais.mdc @@ -12,6 +12,7 @@ alwaysApply: false - **A2 entregue e mesclado em `main` (2026-07-18):** cadastro estático município↔ZE (`bahiaTseZones`, TSE 2024) + chips opt-in no formulário (`NucleusTerritoryAndZonesFields` / `territorySuggestions`); sem migration. Plano: [`docs/plans/zonas-por-municipio.md`](../../../docs/plans/zonas-por-municipio.md). - **C3 Planos de Ação implementado (2026-07-18):** collection `actionPlan` + vertical `/campanha/planos` (lista com tabs Próximos/Todos/Realizados/Rascunhos, detalhe com tabs Visão geral/Tarefas/Atualizações, forms novo/editar), blocos "Próximos eventos" no overview de núcleos e no dashboard; `startAt` opcional só em rascunho; access por `coordinators`/`leadership`; migration `20260718_222832_add_action_plan`. - **B2 Mapa Fase 1 (geometrias) entregue (2026-07-18):** TopoJSON estático municípios IBGE + TIs por dissolução, `bahiaMunicipalityCodes`, helpers `bahiaGeometries`, script `pnpm build:geometries`. Sem UI (B3). Plano: [`docs/plans/mapa-bahia-geometrias.md`](../../../docs/plans/mapa-bahia-geometrias.md). +- **B5 registrado (2026-07-18):** débitos de escala/DRY pós-B2 do `/simplify` (lazy load de geometrias mun/TI + helper `ensureCachedDownload` para CLIs) — plano [`docs/plans/escala-dry-pos-b2.md`](../../../docs/plans/escala-dry-pos-b2.md); F1 preferencialmente com B3; F2 cortável. - **C7 registrado (2026-07-18):** débitos de escala/DRY pós-C3 do `/simplify` (território/contato compostos, helpers FormData/URL, leituras por aba, RMW/índice) — plano [`docs/plans/escala-dry-pos-c3.md`](../../../docs/plans/escala-dry-pos-c3.md); não bloqueia o MVP da agenda. - Fluxos operacionais cobertos: login isolado, CRUD/arquivo de núcleos por slug, designação de coordenador (`geral`), CRM de lideranças, estimativas sugerir/confirmar com versão UUID, feed de atualizações, convites WhatsApp (`wa.me` + hash de uso único), dashboard com agregados só de núcleos ativos. - Pendência operacional (não de código): Consent `lideranca-autopreenchimento` + lote jurídico LGPD antes de dados reais; smoke pós-deploy Vercel. Ver Onda 0 em [`docs/roadmap.md`](../../../docs/roadmap.md). diff --git a/docs/plans/escala-dry-pos-b2.md b/docs/plans/escala-dry-pos-b2.md new file mode 100644 index 00000000..7ec2d82f --- /dev/null +++ b/docs/plans/escala-dry-pos-b2.md @@ -0,0 +1,90 @@ +# Escala e DRY pós-B2 (geometrias + scripts CLI) + +Status: rascunho +Atualizado em: 2026-07-18 +Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha B, item B5) +Responsável: — + +## Contexto + +O B2 ([mapa-bahia-geometrias.md](mapa-bahia-geometrias.md) Fase 1) entregou a fundação estática do mapa: `*.topo.json` de municípios e Territórios de Identidade, `bahiaMunicipalityCodes`, helpers em `bahiaGeometries.ts`, script `pnpm build:geometries`. A passagem `/simplify` de 2026-07-18 limpou dead code e churns baratos no script/codegen, mas **deixou de fora** dois follow-ups que os revisores (performance / reuse) marcaram como importantes e maiores que cleanup pontual: + +1. **Carregamento eager de geometrias no cliente.** `src/lib/bahiaGeometries.ts` importa os dois TopoJSON, roda `topojson-client` `feature()` em 417+27 polígonos e monta índices no load do módulo — custo de bundle/memória antes de qualquer superfície Leaflet (B3). +2. **Cache de download duplicado nos CLIs.** `ensureCachedJson` em `scripts/build-bahia-geometries.mjs` é estruturalmente o mesmo que `ensureCachedZip` em `scripts/seed-tse-results.mjs` (mkdir → hit → `downloadToBuffer` → SHA-256 → write), só muda extensão/parse. + +Este plano é o registro canônico desses follow-ups. Sem ele, o B3 pode embarcar o mapa com payload desnecessário no path default, e o próximo script de dado aberto volta a clonar o cache. + +**Explicitamente fora (revisores pediram skip):** helpers genéricos de `localeCompare('pt-BR')` e de checksum/evidence nos testes int — a duplicação com `bahiaTseZones.int.spec.ts` é intencional e legível por domínio. + +## Objetivos + +- Superfícies de mapa (B3) carregam geometria só sob demanda: município e território podem ser importados em separado; nenhum path de `/campanha` que não mostre mapa paga o decode TopoJSON→GeoJSON. +- Scripts CLI de dado aberto (`build:geometries`, `db:seed:tse`, futuros) compartilham um helper de cache em disco + download, sem mudar proveniência/SHA nem guards de banco. +- Guardrails: sem migration, sem collection, sem Consent, sem server action de produto; B2 permanece a fonte dos artefatos estáticos versionados. + +## Decisões travadas + +- **Um item B5, duas fases ordenadas.** Mesmo racional do C6/C7: um ID de roadmap, PRs por fase. Ordem: lazy load (impacta B3) → helper de cache CLI (barato, sem UI). +- **Dependência dura de B2.** Este item só faz sentido com os `*.topo.json` e `bahiaGeometries` já commitados; não reabre o escopo de geração/simplificação das malhas. +- **Fase 1 preferencialmente junto com B3.** Se o implementador de B3 aplicar o carregamento lazy no mesmo PR das superfícies Leaflet, a Fase 1 deste plano fecha ali (marcar B5 F1 ✓ e seguir só a F2). Se B3 embarcar import estático por pressa, a F1 permanece como débito explícito. +- **Cortável se o mapa permanecer fora do caminho crítico.** B3 já é corte seguro no roadmap; B5 F2 (cache CLI) é ainda mais cortável — a duplicação atual é ~20 linhas. F1 só é “não cortável” no sentido de qualidade do B3 (bundle), não de calendário eleitoral. +- **Não extrair helpers de sort pt-BR nem de evidence SHA nos testes.** Decisão dos revisores do simplify (2026-07-18): indirection sem ganho. +- **i18n e naming** (AGENTS.md): identificadores em inglês (`loadMunicipalityFeatures`, `loadTerritoryFeatures`, `ensureCachedDownload`), strings visíveis em pt-BR só se algum log de CLI for user-facing (hoje são logs de build). + +## Questões em aberto + +- **API pública pós-lazy: manter `getMunicipalityFeature` sync após warm, ou só async loaders?** **Recomendação:** loaders async (`loadMunicipalityGeometryModule()` / `loadTerritoryGeometryModule()`) que resolvem o módulo já indexado; os getters sync atuais viram métodos do módulo carregado (ou ficam em submodule lazy). Evitar Promise em cada hover do mapa. +- **Onde vive o helper de cache CLI?** **Recomendação:** `src/lib/cliCachedDownload.ts` (ou `scripts/lib/cached-download.mjs` se quiser zero risco de import no app). Preferir `src/lib/` só se tipado e importado só pelos scripts via `--import=tsx/esm` — não expor no client bundle. +- **Unificar env `TSE_CACHE_DIR` / `GEOMETRIES_CACHE_DIR`?** **Recomendação:** não — um parâmetro `cacheDir` por chamada; cada script mantém sua env e pasta sob `data/`. + +## Abordagem proposta + +```mermaid +flowchart TD + B2["B2 geometrias ✓"] --> F1 + F1["Fase 1 — Lazy geometrias
split mun/TI + dynamic import"] + F1 --> F2["Fase 2 — ensureCachedDownload
CLI compartilhado"] + F1 -.preferencialmente.-> B3["B3 Leaflet"] + B2 --> B3 +``` + +### Fase 1 — Lazy load de geometrias (antes ou com B3) + +- Partir `src/lib/bahiaGeometries.ts`: deixar de avaliar `feature()` + Maps no top-level do entry que o app importa por padrão. +- Submódulos ou dynamic `import()` separados para municípios vs territórios (dashboard só TI; overview pode precisar dos dois sob toggle). +- `BahiaMap` / superfícies B3: `dynamic(() => import(...), { ssr: false })` já previsto no plano do mapa; o import interno deve apontar para o loader lazy, não para um barrel que decoda tudo. +- Testes int: importar o submodule (ou await loader) — ambiente node, sem regressão de cobertura 417/27. +- Critério de verificação: nenhum import estático de página `/campanha` (exceto a ilha do mapa) puxa os `*.topo.json` no grafo de módulo (inspecionar bundle / `pnpm build` + analysis leve). + +### Fase 2 — Helper de cache CLI + +- Extrair `ensureCachedDownload({ cacheDir, key, url, ext?, logPrefix })` a partir de `ensureCachedJson` / `ensureCachedZip`. +- Retorno: `{ url, buffer, hash }` (parse JSON/ZIP fica no caller — `build-bahia-geometries` já faz `JSON.parse`; seed TSE usa `readZipEntry`). +- Continuar usando `downloadToBuffer` (`src/lib/electionResultsZip.ts`). +- Migrar `scripts/build-bahia-geometries.mjs` e `scripts/seed-tse-results.mjs`; sem mudança de comportamento nem de pastas `data/geometries/` / `data/tse/`. + +**Migration:** nenhuma. Sem collection, sem Consent, sem server action de produto. + +## Dependências + +- **Dura:** B2 Mapa Fase 1 (geometrias) — já entregue. +- **Suave:** B3 Leaflet — consome a Fase 1; se B3 implementar o lazy load, fecha F1 neste plano. +- Reusa: `downloadToBuffer` (`src/lib/electionResultsZip.ts`), artefatos em `src/lib/geometries/`, padrões de script de `scripts/seed-tse-results.mjs`. + +## Não escopo + +- Implementação Leaflet / componentes de mapa → **B3** ([mapa-bahia-geometrias.md](mapa-bahia-geometrias.md) Fase 2). +- Camada de zonas TSE no mapa → **B4**. +- Helper genérico de `localeCompare('pt-BR')` ou de evidence SHA nos testes int — rejeitado no simplify. +- Mudança de qualidade/simplificação das malhas IBGE ou re-download obrigatório — só se a F2 tocar proveniência (não deve). + +## Referências + +- `docs/roadmap.md` (Trilha B, item B5; B2/B3) +- `docs/plans/mapa-bahia-geometrias.md` — plano pai; B3 deve preferir a F1 deste item +- `docs/plans/escala-dry-pos-c2.md` / `escala-dry-pos-c3.md` — precedente de registro pós-`/simplify` +- `src/lib/bahiaGeometries.ts` — eager `feature()` + índices +- `scripts/build-bahia-geometries.mjs` — `ensureCachedJson` +- `scripts/seed-tse-results.mjs` — `ensureCachedZip` +- `src/lib/electionResultsZip.ts` — `downloadToBuffer` +- AGENTS.md — naming inglês; Bahia implícita; dado estático versionado diff --git a/docs/plans/mapa-bahia-geometrias.md b/docs/plans/mapa-bahia-geometrias.md index 6295df9c..2612a494 100644 --- a/docs/plans/mapa-bahia-geometrias.md +++ b/docs/plans/mapa-bahia-geometrias.md @@ -2,7 +2,7 @@ Status: Fase 1 entregue (B2); Fase 2 (B3 Leaflet) pendente Atualizado em: 2026-07-18 -Item do roadmap: [docs/roadmap.md](../roadmap.md) — B2 (Fase 1) / B3 (Fase 2) / B4 (camada de zonas) +Item do roadmap: [docs/roadmap.md](../roadmap.md) — B2 (Fase 1) / B3 (Fase 2) / B4 (camada de zonas); lazy load e DRY de scripts → [B5](escala-dry-pos-b2.md) Responsável: — ## Contexto @@ -58,6 +58,7 @@ Hierarquia TSE: município → (1+) zona → (muitas) seção; a relação munic ## Leaflet na campanha (B3 — pendente) - Dependência: `leaflet` (+ `topojson-client` já instalado na Fase 1). `react-leaflet` é opcional; preferir wrapper cliente fino com `leaflet` direto e `dynamic(() => import(...), { ssr: false })` para evitar SSR. +- **Lazy load (B5 F1):** não importar `bahiaGeometries` de forma eager no path default de `/campanha`. Preferir split município/TI + `import()` dinâmico no mount do mapa — ver [escala-dry-pos-b2.md](escala-dry-pos-b2.md). Se B3 aplicar isso no mesmo PR, fecha B5 Fase 1. - **Base tiles:** OpenStreetMap raster (ou CartoDB Positron) com atribuição obrigatória; ou fundo neutro sem tiles (só coroplético) como opção mais leve. Decidir na implementação; default OSM. - **Componentes novos (todos `src/components/campaign/`, PascalCase):** - `BahiaMap.tsx` — cliente, recebe `geometry` (TopoJSON) + `values: Record` (chave→métrica) + `mode: 'municipality' | 'territory'` + `colorScale`; renderiza coroplético Leaflet. @@ -69,9 +70,9 @@ Hierarquia TSE: município → (1+) zona → (muitas) seção; a relação munic ## Phasing - **Fase 1 — Fundação de geometrias (B2) ✓ entregue 2026-07-18:** `bahiaMunicipalityCodes.ts` + fixture + teste; `*.topo.json` de municípios e territórios; script `pnpm build:geometries`; helpers em `bahiaGeometries.ts`. -- **Fase 2 — Leaflet nas superfícies (B3):** `BahiaMap` + `NucleusOverviewMap` + `NucleusDetailMap` + `DashboardMap`, com agregados existentes. +- **Fase 2 — Leaflet nas superfícies (B3):** `BahiaMap` + `NucleusOverviewMap` + `NucleusDetailMap` + `DashboardMap`, com agregados existentes. Preferir fechar [B5 F1](escala-dry-pos-b2.md) (lazy geometrias) no mesmo PR. -**Sequenciamento:** a Fase 1 é independente e paralelizável. A Fase 2 rende mais depois de B1 ✓ (overview) e ganha valor com A4 (baseline no produto). Nenhum dos dois bloqueia B3 — o mapa funciona só com estimativa/nº de núcleos. +**Sequenciamento:** a Fase 1 é independente e paralelizável. A Fase 2 rende mais depois de B1 ✓ (overview) e ganha valor com A4 (baseline no produto). Nenhum dos dois bloqueia B3 — o mapa funciona só com estimativa/nº de núcleos. B5 F1 é dependência suave de qualidade de bundle. ## Ciclo seguinte — Zonas TSE como camada (B4) @@ -93,10 +94,12 @@ Após A2 ✓ + B3. Polígono por dissolução dos municípios membros (abordagem - Reusa `bahiaIdentityTerritoryRecords`/`bahiaMunicipalities` (`src/lib/bahiaTerritories.ts`), `canonicalizeMunicipalityName` (`src/lib/electionResults.ts`), `CitiesByState.BA` (`src/lib/cities`), `campaignDashboardPageData` / `nucleusViewModels` / `nucleusListOverviewPageData` (agregados para B3), e o padrão de fixture/teste de `bahiaTerritories`. - **Camada de Zonas (B4)** depende de A2 (`docs/plans/zonas-por-municipio.md`) + B3. +- **Lazy load / DRY CLI (B5):** débitos do `/simplify` pós-B2 — [escala-dry-pos-b2.md](escala-dry-pos-b2.md). F1 preferencialmente com B3; F2 (`ensureCachedDownload`) independente. ## Revisões - **2026-07-18 (auditoria pré-B2 + entrega Fase 1):** territórios por dissolução IBGE (não shapefile IDE Bahia); extensão `*.topo.json`; reuso de `canonicalizeMunicipalityName`; núcleo usa `cities[]` (A1); script sem guard de banco; referências de linha do roadmap antigas removidas; Fase 1 implementada e marcada entregue. +- **2026-07-18 (B5 registrado):** follow-ups do `/simplify` que não entraram no cleanup (lazy geometrias + cache CLI compartilhado) viraram item B5; B3 deve preferir F1. ## Referências diff --git a/docs/roadmap.md b/docs/roadmap.md index 93b738c2..fc7a5839 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -1,6 +1,6 @@ # Roadmap — Teqo -Atualizado em: 2026-07-18 (MVP + Ciclo 2 deployados; A2 entregue; C2 engenharia pronta e mesclada em `main`; C3 Planos de Ação implementado e mesclado em `main`; C6/C7 registrados — escala/DRY pós-C2 e pós-C3; B2 fundação de geometrias do mapa entregue) +Atualizado em: 2026-07-18 (MVP + Ciclo 2 deployados; A2 entregue; C2 engenharia pronta e mesclada em `main`; C3 Planos de Ação implementado e mesclado em `main`; C6/C7 registrados — escala/DRY pós-C2 e pós-C3; B2 fundação de geometrias do mapa entregue; B5 registrado — escala/DRY pós-B2 do `/simplify`) Registro canônico no repositório dos planos futuros e débitos conhecidos. Status operacional do ciclo atual de Núcleos fica em [`.cursor/rules/projects/nucleos-eleitorais.mdc`](../.cursor/rules/projects/nucleos-eleitorais.mdc); este arquivo lista o que ainda é futuro ou bloqueador, **em ordem de execução**, com dependências e paralelismo explícitos. @@ -63,6 +63,7 @@ MVP de território + reporte implementado e enviado (ondas 1–8 + refactors de ### Ciclo 2+ — B2 fundação de geometrias do mapa (2026-07-18) - **B2 Mapa Fase 1 (geometrias)** — TopoJSON estático de municípios IBGE (`bahia-municipalities.topo.json`) + Territórios de Identidade por dissolução (`bahia-identity-territories.topo.json`), tabela `bahiaMunicipalityCodes` (nome canônico → `codarea`), helpers `bahiaGeometries` (`getMunicipalityFeature` / `getTerritoryFeature`), script re-executável `pnpm build:geometries`. Sem migration, sem UI (Leaflet = B3). [Plano](plans/mapa-bahia-geometrias.md). +- **B5 Escala e DRY pós-B2 registrado (2026-07-18)** — débitos do `/simplify` que não entraram no cleanup: lazy load de geometrias (split mun/TI + dynamic import, preferencialmente com B3) e helper compartilhado de cache CLI (`ensureCachedDownload` para `build:geometries` / `db:seed:tse`). [Plano](plans/escala-dry-pos-b2.md). ### Ciclo 2+ — C2 e C3 mesclados em `main` (2026-07-18) @@ -85,6 +86,7 @@ Os designs gerados pelo UX Pilot para os próximos ciclos estão em [`docs/desig | C6 Escala e DRY pós-C2 | — (sem design novo; reusa os de C2 + padrões de núcleos) | [escala-dry-pos-c2.md](plans/escala-dry-pos-c2.md) | | C3 Eventos / agenda de mobilização | `Planos-de-Acao`, `Novo-Plano-de-Acao` | [eventos-agenda-mobilizacao.md](plans/eventos-agenda-mobilizacao.md) | | C7 Escala e DRY pós-C3 | — (sem design novo; reusa os de C3 + padrões de núcleos/C6) | [escala-dry-pos-c3.md](plans/escala-dry-pos-c3.md) | +| B5 Escala e DRY pós-B2 | — (sem design novo; reusa geometrias B2 + padrões de scripts CLI) | [escala-dry-pos-b2.md](plans/escala-dry-pos-b2.md) | | D2 Notificações (sino) | `Notificacoes-PWA` (central de notificações) | [notifications.md](plans/notifications.md) | | D1 PWA `/campanha` | `Notificacoes-PWA` (banner "Instale o app da campanha") | [pwa-campanha.md](plans/pwa-campanha.md) | | C5 Operação dia D / GOTV | `Dia-D-GOTV` | sem plano ainda — o design é a primeira materialização do item (check-off de apoiadores por seção/zona, progresso "62% da base", filtro "Ainda não confirmados", estado "Sincronizado" offline-tolerante) | @@ -114,6 +116,7 @@ flowchart TD subgraph TrilhaB["Trilha B — superfícies de coordenação"] B1["B1 Overview da lista de núcleos ✓"] B2["B2 Mapa Fase 1 (geometrias) ✓"] + B5["B5 Escala e DRY pós-B2
(lazy geometrias / cache CLI)"] B3["B3 Mapa Fase 2 (Leaflet nas superfícies)"] B4["B4 Camada de zonas TSE no mapa"] end @@ -142,6 +145,8 @@ flowchart TD TSE2026["TSE publica candidaturas 2026
(externo, após 15/08)"] --> A6 B1 --> B3 B2 --> B3 + B2 --> B5 + B5 -.lazy geometrias.-> B3 A4 -.métricas do coroplético.-> B3 A2 --> B4 B3 --> B4 @@ -159,7 +164,7 @@ flowchart TD JUR -.chave de push.-> D2 ``` -Itens sem seta de entrada (**paralelizáveis a qualquer momento**): fill-ins (visitados recentemente, listas globais, reset de senha, higiene PascalCase). **Entregues e mesclados em `main` em 2026-07-18** (✓ no grafo): A1, A2, A3, B1, B2, C1, C2, C3 e D1 — ver "Ciclo 2" e "Ciclo 2+" acima. C2 segue com produção bloqueada pelo lote jurídico (Onda 0: Consent keys `apoiador-cadastro` / `apoiador-intencao-voto` + aprovação). Destravados por eles: A4 (A2/A3 + B1 prontos), B3 (B1 ✓ + B2 ✓; suave: A4 para coroplético), C4 (A1 + C3 prontos), C6 (merge de C2 ✓), C7 (após C3; suave: C6 F2) e D2 push (D1 pronto; falta a chave jurídica). +Itens sem seta de entrada (**paralelizáveis a qualquer momento**): fill-ins (visitados recentemente, listas globais, reset de senha, higiene PascalCase). **Entregues e mesclados em `main` em 2026-07-18** (✓ no grafo): A1, A2, A3, B1, B2, C1, C2, C3 e D1 — ver "Ciclo 2" e "Ciclo 2+" acima. C2 segue com produção bloqueada pelo lote jurídico (Onda 0: Consent keys `apoiador-cadastro` / `apoiador-intencao-voto` + aprovação). Destravados por eles: A4 (A2/A3 + B1 prontos), B3 (B1 ✓ + B2 ✓; suave: A4 para coroplético e B5 F1 para lazy load), B5 (após B2; preferencialmente com B3), C4 (A1 + C3 prontos), C6 (merge de C2 ✓), C7 (após C3; suave: C6 F2) e D2 push (D1 pronto; falta a chave jurídica). ### Sequência de execução por janela do calendário @@ -188,20 +193,21 @@ Itens sem seta de entrada (**paralelizáveis a qualquer momento**): fill-ins (vi | Ordem | Item | Plano | Depende de | Paralelizável com | | ----- | -------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | ----------------- | -| 12 | A5 Insights derivados do baseline (5 itens, paralelizáveis entre si; conversão e classificação exigem limiares de produto) | [conversão](plans/insight-taxa-conversao.md) · [classificação](plans/insight-classificacao-territorial.md) · [alavancagem](plans/insight-alavancagem-chapa.md) · [mobilização](plans/insight-mobilizacao-brancos-nulos.md) · [competitiva](plans/insight-inteligencia-competitiva.md) | A4 | B2/B3, C4, D1 | -| 13 | B2 Mapa Fase 1 (geometrias) **(entregue 2026-07-18)** + B3 Leaflet nas superfícies | [detalhes](plans/mapa-bahia-geometrias.md) | B3 ← B1 ✓ + B2 ✓ (suave: A4 para coroplético de baseline/classe) | A5, C4, D1 | -| 14 | C4 Demandas | [detalhes](plans/demandas-campanha.md) | A1 (suave: C3 para a relação `actionPlan`) | A5, B3, D1 | -| 15 | D1 PWA `/campanha` **(entregue antecipado em 2026-07-18)** | [detalhes](plans/pwa-campanha.md) | — | tudo | -| 16 | D2 Notificações (push + sino) — sino não depende do PWA; push sim | [detalhes](plans/notifications.md) | D1 ✓ (push) + chave `campanha-notificacoes-push` do lote jurídico | A5, B3, C4 | +| 12 | A5 Insights derivados do baseline (5 itens, paralelizáveis entre si; conversão e classificação exigem limiares de produto) | [conversão](plans/insight-taxa-conversao.md) · [classificação](plans/insight-classificacao-territorial.md) · [alavancagem](plans/insight-alavancagem-chapa.md) · [mobilização](plans/insight-mobilizacao-brancos-nulos.md) · [competitiva](plans/insight-inteligencia-competitiva.md) | A4 | B3, B5, C4, D1 | +| 13 | B2 Mapa Fase 1 (geometrias) **(entregue 2026-07-18)** + B3 Leaflet nas superfícies | [detalhes](plans/mapa-bahia-geometrias.md) | B3 ← B1 ✓ + B2 ✓ (suave: A4 coroplético; B5 F1 lazy geometrias) | A5, B5, C4, D1 | +| 14 | B5 Escala e DRY pós-B2 (lazy geometrias + cache CLI) | [detalhes](plans/escala-dry-pos-b2.md) | B2 ✓ (F1 preferencialmente com B3) | A5, B3, C4, D1 | +| 15 | C4 Demandas | [detalhes](plans/demandas-campanha.md) | A1 (suave: C3 para a relação `actionPlan`) | A5, B3, B5, D1 | +| 16 | D1 PWA `/campanha` **(entregue antecipado em 2026-07-18)** | [detalhes](plans/pwa-campanha.md) | — | tudo | +| 17 | D2 Notificações (push + sino) — sino não depende do PWA; push sim | [detalhes](plans/notifications.md) | D1 ✓ (push) + chave `campanha-notificacoes-push` do lote jurídico | A5, B3, B5, C4 | **Janela 4 — set → 04/10 (reta final): dobradinha, dia D e estabilização.** | Ordem | Item | Plano | Depende de | | ----- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | -| 17 | A6 Insight dobradinha 2026 | [detalhes](plans/insight-dobradinha-2026.md) | A4 + TSE publicar candidaturas 2026 (externo, após 15/08) + taxonomia de alinhamento (produto) | -| 18 | B4 Camada de zonas TSE no mapa | ciclo seguinte do [plano do mapa](plans/mapa-bahia-geometrias.md) | A2 + B3 | -| 19 | C5 **Operação dia D / GOTV** _(item proposto nesta revisão — validar com produto)_: confirmação de comparecimento da base nominal em 04/10 (lista de apoiadores por núcleo/zona com marcação "confirmou que vai votar / votou", visão de cobertura para a coordenação). É o uso final da base construída em C2; a literatura de campanha trata a mobilização do dia como onde eleições apertadas se decidem. Sem plano ainda. | design: [`Dia-D-GOTV`](design-refs/latest/Dia-D-GOTV.png) | C2 (suave: C6 para escala da base) | -| 20 | Congelamento: a partir de ~20/09, só correção de bug e dados; nada de migration arriscada perto do dia D. | — | — | +| 18 | A6 Insight dobradinha 2026 | [detalhes](plans/insight-dobradinha-2026.md) | A4 + TSE publicar candidaturas 2026 (externo, após 15/08) + taxonomia de alinhamento (produto) | +| 19 | B4 Camada de zonas TSE no mapa | ciclo seguinte do [plano do mapa](plans/mapa-bahia-geometrias.md) | A2 + B3 | +| 20 | C5 **Operação dia D / GOTV** _(item proposto nesta revisão — validar com produto)_: confirmação de comparecimento da base nominal em 04/10 (lista de apoiadores por núcleo/zona com marcação "confirmou que vai votar / votou", visão de cobertura para a coordenação). É o uso final da base construída em C2; a literatura de campanha trata a mobilização do dia como onde eleições apertadas se decidem. Sem plano ainda. | design: [`Dia-D-GOTV`](design-refs/latest/Dia-D-GOTV.png) | C2 (suave: C6 para escala da base) | +| 21 | Congelamento: a partir de ~20/09, só correção de bug e dados; nada de migration arriscada perto do dia D. | — | — | **Fill-ins (qualquer janela, quando houver folga; nenhum bloqueia nada):** @@ -210,7 +216,7 @@ Itens sem seta de entrada (**paralelizáveis a qualquer momento**): fill-ins (vi - **Reset de senha self-service + foto de perfil** (UX adiada do ciclo 1). _(plano MVP)_ - **Higiene de código:** varredura PascalCase dos componentes legados. _(notebook Núcleos)_ -**Cortes seguros se o prazo apertar** (nesta ordem): B4 camada de zonas, B3 mapa Leaflet, C4 demandas, D2 push (mantendo o sino), C6 fases 3–5 (import bulk / preview token / KPI — só se a base nominal permanecer pequena), C7 fases 4–5 (leituras/RMW/índice — só se a agenda permanecer pequena), fill-ins (D1 PWA já entregue, saiu da lista de cortes). Preferir manter C6 fases 1–2 e C7 fases 1–2 (DRY de UI) se houver folga. **Não cortáveis:** Onda 0 (jurídico/Consent), C2 cadastro de apoiadores, C3 eventos/agenda, A4 baseline + gap — são respectivamente o risco legal, a base de dados, a operação da propaganda e o instrumento de alocação de esforço. +**Cortes seguros se o prazo apertar** (nesta ordem): B4 camada de zonas, B5 F2 (cache CLI — duplicação pequena), B3 mapa Leaflet (preferir levar B5 F1 lazy load junto se B3 entrar), C4 demandas, D2 push (mantendo o sino), C6 fases 3–5 (import bulk / preview token / KPI — só se a base nominal permanecer pequena), C7 fases 4–5 (leituras/RMW/índice — só se a agenda permanecer pequena), fill-ins (D1 PWA já entregue, saiu da lista de cortes). Preferir manter C6 fases 1–2 e C7 fases 1–2 (DRY de UI) se houver folga. **Não cortáveis:** Onda 0 (jurídico/Consent), C2 cadastro de apoiadores, C3 eventos/agenda, A4 baseline + gap — são respectivamente o risco legal, a base de dados, a operação da propaganda e o instrumento de alocação de esforço. ### Itens consolidados/removidos nesta revisão (2026-07-17)