Expand A5 ticket leverage plan with flip-opportunity phase 2.

Absorb the virada insight into insight-alavancagem-chapa (winner or right-share triggers), align dobradinha taxonomy with electionPartySpectrum, and update roadmap and project notebook.
This commit is contained in:
Francisco Ignácio Fontoura Solla 2026-07-19 04:49:03 -03:00
parent e5e051d175
commit 9855d557f9
4 changed files with 143 additions and 84 deletions

View file

@ -104,6 +104,7 @@ alwaysApply: false
- O DTO paginado de atualizações contém somente filtro/paginação/feed; a visão geral chama um loader dedicado que busca `limit: 3` e apenas os autores necessários à prévia.
- A lista paginada de lideranças seleciona e serializa somente os campos da linha; avaliação interna e dados completos de contato são carregados apenas para a liderança selecionada. O fechamento usa `onCloseAutoFocus` no Sheet e `finalFocus` no Drawer, focaliza a linha de origem (ou o título do Núcleo) antes de navegar para a URL canônica, sem RAF/timeout.
- Em 2026-07-19, decisões de produto da Trilha E (mapa de projeção): **sem entidade nova por município** — `electoralNucleus` absorve a camada estratégica (metas, prioridade, dobradinhas) e a visão município-a-município é derivada de núcleos + baseline TSE agregados por `cities[]`; **série histórica 2014/2018 vem do TSE oficial** (seed A3 parametrizado por ano, só dep. federal turno 1 BA), não dos números da planilha; **lideranças/assessores da planilha entram como texto staff-only no import E4, nunca como `Contact`/`leadership`** (sem telefone nem consent — bloqueador LGPD da Onda 0 se aplica; o CRM real é alimentado depois pelo fluxo com Consent); tendência (Queda/Mantém/Aumento) é **derivada em leitura** (`computeVoteTrend`, limiares versionados), não persistida.
- Em 2026-07-19, insight “oportunidade de virada” absorvido como **Fase 2** de [insight-alavancagem-chapa.md](../../../docs/plans/insight-alavancagem-chapa.md) (A5): majoritários locais de esquerda + (vencedor federal de direita **ou** share da direita nos nominais ≥ limiar); mapa estático de espectro partidário; sem item paralelo.
- Fonte: `/Users/francisco.solla/.cursor/plans/núcleos_eleitorais_mvp_7f788d29.plan.md`.
## Bloqueadores e próximas frentes

View file

@ -1,69 +1,126 @@
# Insight: alavancagem da chapa (Lula/Jerônimo)
# Insight: alavancagem da chapa (conversão + oportunidade de virada)
Status: rascunho
Atualizado em: 2026-07-17
Item do roadmap: [docs/roadmap.md](../roadmap.md) (seção "Campanha → Próximos ciclos")
Atualizado em: 2026-07-19
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha A, item A5 — um dos cinco insights derivados do baseline)
Responsável: —
## Referência visual (UX Pilot)
Design: [`Baseline-Eleitoral-2022.png`](../design-refs/latest/Baseline-Eleitoral-2022.png) — card "Insights do território", linha "Alavancagem da chapa: 22% · Da base Lula/Jerônimo ainda não convertida". Implementar como um card do stack `NucleusInsights.tsx` ([baseline-eleitoral-tse.md](baseline-eleitoral-tse.md)), com os tokens claros do tema `campaign` em vez da paleta antiga do HTML/PNG.
Design: [`Baseline-Eleitoral-2022.png`](../design-refs/latest/Baseline-Eleitoral-2022.png) · [`Baseline-Eleitoral-2022.html`](../design-refs/latest/Baseline-Eleitoral-2022.html)
![Card Insights do território com linha Alavancagem da chapa](../design-refs/latest/Baseline-Eleitoral-2022.png)
Como usar:
- **Adotar a estrutura:** card "Insights do território" no detalhe do núcleo — uma linha/Alert por insight (ícone + veredito curto + números de apoio). A linha existente "Alavancagem da chapa: 22% · Da base Lula/Jerônimo ainda não convertida" cobre a **Fase 1**. A **Fase 2** (oportunidade de virada) entra como **segunda linha** no mesmo stack quando algum gatilho dispara — não inventar tela nova.
- **Fora deste plano:** Gap vs 2022 (já em A4), conversão/classificação/mobilização/competitiva (outros planos A5), dobradinha 2026 (A6).
- **Ajustar cores e código:** o HTML/PNG usa a paleta antiga (vermelho escuro `#8E0E23`, navy `#1B2B4B`, dourado `#C8874B`) e Tailwind via CDN. Implementar com `Alert` / tokens do tema `data-theme='campaign'` (`src/app/(frontend)/styles.css`), no stack `NucleusInsights.tsx`.
## Contexto
Solla apoia a chapa principal do PT (Lula para presidente, Jerônimo para governador, ambos nº 13). Os votos que Lula e Jerônimo receberam em 2022 numa geografia são o **teto natural do PT** no local — um piso de simpatia partidária que a candidatura de Solla pode capturar como dobradinha. Com o baseline TSE 2022 (ver [baseline-eleitoral-tse.md](baseline-eleitoral-tse.md)), podemos medir quanto da base da chapa a estimativa do núcleo já captura e quanto ainda falta — meta implícita de alavancagem.
Solla (PT) apoia a chapa majoritária de esquerda (Lula presidente, Jerônimo governador — `BASELINE_TICKET_2022` em `src/lib/electionResults.ts`). O baseline TSE 2022 (A3/A4) já expõe no detalhe do núcleo:
- votos da chapa (`president` / `governor` via `getNucleusElectoralBaseline`);
- `winnerFederal` (mais votado a dep. federal na geografia, com `party`);
- ranking federal agregável por candidato (hoje materializado no loader; A7 F1 deve agregar no SQL);
- em `electionTally`, por município×zona×cargo×turno: `winnerCandidateName` / `winnerParty` / `winnerVotes` (ainda **não** no view model do núcleo).
Hoje o único insight ligado à chapa no produto é o Gap vs 2022 (candidato). Faltam dois sinais de alavancagem da majoritária:
1. **Conversão da base** — quanto da base Lula/Jerônimo a estimativa confirmada já captura.
2. **Oportunidade de virada** _(produto 2026-07-19)_ — território onde o eleitorado já escolheu (ou favorece) o time majoritário de esquerda, mas o voto **proporcional federal** ainda foi para a direita — seja porque o **mais votado** federal é de direita, seja porque a **soma dos votos de partidos de direita** é alta em relação ao total válido. Em ambos os casos o discurso de **completar a chapa / eleger o time** é operacionalmente útil.
A Fase 2 foi **absorvida neste plano** (não virou item A5 paralelo): mesma superfície (`NucleusInsights`), mesmos dados TSE, mesma taxonomia de espectro que a dobradinha A6 precisará depois. Precedente: Gap vs 2022 absorvido no baseline.
## Objetivos
- Computar `lulaVotes` (presidente, nº 13, 1º e 2º turno) e `jeronimoVotes` (governador, nº 13, 1º e 2º turno) por geografia do núcleo.
- Calcular `ticketLeverage = confirmedVoteEstimate / lulaVotes` (e `/ jeronimoVotes`) — % da base da chapa capturada.
- Exibir no detalhe do núcleo: "% da base de Lula capturada", "teto da chapa: X votos de Lula / Y de Jerônimo", e gap `lulaVotes − confirmedVoteEstimate` como potencial de alavancagem.
- No overview da lista: soma de `lulaVotes` e `jeronimoVotes` sobre o conjunto filtrado, e `Σ confirmedVoteEstimate / Σ lulaVotes`.
- **Fase 1 — conversão:** `ticketLeverage` = `confirmedVoteEstimate / lulaVotes` (e `/ jeronimoVotes`); % capturada, teto da chapa e gap no detalhe; no overview, `Σ estimate / Σ lulaVotes` sobre o filtro.
- **Fase 2 — oportunidade de virada:** detectar, na geografia do núcleo (com majoritários de esquerda), **qualquer** dos gatilhos abaixo e exibir Alert com copy de completar a chapa; no overview, contagem de núcleos com oportunidade.
- **Gatilho A — vencedor federal de direita:** `winnerFederal.party` → espectro `direita`.
- **Gatilho B — participação da direita alta:** `rightShare = Σ votos federais (partido→direita) / Σ votos federais válidos nominais` ≥ limiar versionado — **mesmo se o mais votado não for de direita** (ex.: vencedor de centro/esquerda estreito, mas PL+PP+União+… somam 35%+).
- Guardrails: **leitura derivada** — sem escrita, sem `Consent`, sem migration, sem collection nova; `overrideAccess: false`; falha fechada se faltar tally/voto ou partido fora do mapa (não inventar classificação).
## Decisões travadas
- **Leitura derivada** — sem escrita, sem `Consent`, sem migration.
- **Reusa** `getNucleusElectoralBaseline` (já expõe `lulaVotes1t/2t`, `jeronimoVotes1t/2t` no view model).
- **Turno decisivo** como referência principal (presidente 2º turno; governador 2º turno), com 1º turno como secundário.
- **Duas fases no mesmo insight A5, não item novo.** Fase 1 = métrica contínua; Fase 2 = flag com gatilhos A e/ou B. _(produto 2026-07-19)_
- **Gatilho B é independente do vencedor.** Participação proporcional da direita basta; o vencedor de direita (A) é caso especial de B com share concentrado, mas A e B disparam o mesmo Alert com `trigger: 'winner' | 'share' | 'both'` para o copy citar o motivo certo. _(refino de produto 2026-07-19)_
- **Espectro por partido, estático e versionado** — `SG_PARTIDO` → `esquerda | direita | centro | outro` em `src/lib/electionPartySpectrum.ts`. Fonte inicial: federações/coligações 2022. Sem ML. _(compartilhável com A6)_
- **Share usa votos nominais federais agregados por partido**, não legenda isolada no v1 (legenda pode entrar depois se o seed expuser). Denominador = soma dos nominais na geografia (turno 1 dep. federal), alinhado ao ranking A4.
- **Vencedores majoritários vêm de `electionTally.winner*`** (turno decisivo), agregados por geografia; federal A usa `winnerFederal`; federal B precisa da **soma por espectro** — preferir a mesma agregação que A7 F1 (`SUM(votes) GROUP BY party` ou por candidato→partido), não materializar todas as rows no React.
- **Campanha é esquerda via ticket** — `BASELINE_TICKET_2022.*.party` (PT) no mapa; Fase 2 só faz sentido quando majoritários locais são esquerda (pré-condição) e o proporcional ainda “vaza” para a direita.
- **Fase 2 opt-in na UI** — Alert só quando `status === 'opportunity'`; silêncio caso contrário.
- **i18n/naming:** `computeTicketLeverage`, `computeTicketFlipOpportunity`, `partySpectrum`, `rightShare`, `winnerPresident`, `winnerGovernor`; strings em pt-BR.
## Questões em aberto
- Usar Lula ou Jerônimo como teto principal? Recomendação: ambos, com Lula (presidente) como teto mais alto e Jerônimo (governador) como referência estadual.
- `lideranca` vê? Recomendação: sim.
- **Limiar do gatilho B (`RIGHT_SHARE_THRESHOLD`)?** **Recomendação:** `0.25` (25% dos nominais federais) como constante versionada em `electionInsights.ts`; validar com produto (faixa plausível 20–35%). Abaixo do limiar e sem gatilho A → sem Alert.
- **Turno decisivo dos majoritários?** **Recomendação:** 2º turno (pres./gov.); fallback para 1º se não houver tally de 2º.
- **Geografia multi-zona com vencedores majoritários diferentes?** **Recomendação:** somar `winnerVotes` por candidato; empate → `ambiguous` (não dispara). Share federal (B) soma todas as zonas sem ambiguidade.
- **Partido ausente do mapa?** **Recomendação:** votos com espectro `null` entram no denominador mas **não** no numerador da direita (fail-closed no share); se o vencedor federal tiver partido desconhecido → gatilho A não dispara.
- **Exigir os dois majoritários esquerda?** **Recomendação:** **ambos**; validar com produto se quiserem OR.
- **Quando A e B disparam juntos?** **Recomendação:** um único Alert com `trigger: 'both'` e copy que menciona o vencedor **e** o % da direita.
- **`lideranca` vê?** **Recomendação:** sim (dado público).
- **Overview:** **Recomendação:** contagem de núcleos com oportunidade; breakdown A vs B opcional no v1.
## Abordagem proposta
```mermaid
flowchart LR
Base["getNucleusElectoralBaseline<br/>lulaVotes + jeronimoVotes"]
Tally["electionTally<br/>winner* pres/gov"]
FedAgg["agregação federal<br/>por candidato + por partido"]
Spec["electionPartySpectrum.ts"]
Est["confirmedVoteEstimate"]
Calc["computeTicketLeverage(lulaVotes, jeronimoVotes, estimate)<br/>% capturada + gap de alavancagem"]
Detail["Detalhe: '% da base de Lula capturada'<br/>+ teto da chapa + gap"]
Overview["Overview: Σ estimate / Σ lulaVotes<br/>sobre o filtro"]
Base --> Calc
Est --> Calc
Calc --> Detail
Calc --> Overview
F1["computeTicketLeverage"]
F2["computeTicketFlipOpportunity<br/>pré-condição majoritária esq<br/>+ gatilho A winner dir<br/>+ gatilho B rightShare ≥ limiar"]
UI["NucleusInsights<br/>Alert F1 + Alert F2 condicional"]
Overview["NucleusListOverview<br/>Σ leverage + contagem flip"]
Tally --> F2
FedAgg --> F2
Spec --> F2
Est --> F1
F1 --> UI
F2 --> UI
F1 --> Overview
F2 --> Overview
```
- **Helper** `src/lib/electionInsights.ts`: `computeTicketLeverage(lulaVotes, jeronimoVotes, confirmedVoteEstimate)` → `{ lulaLeverage, jeronimoLeverage, lulaGap, jeronimoGap, status }`.
- **Componente** `src/components/campaign/NucleusTicketLeverage.tsx` (server). Overview: bloco agregado no `NucleusListOverview`.
- **Teste int** cenários: `lulaVotes=0`, `confirmedVoteEstimate=null`, `estimate > lulaVotes` (superou a chapa).
Componentes:
## Arquivos a criar/alterar
- Criar: `src/components/campaign/NucleusTicketLeverage.tsx`.
- Alterar: `src/lib/electionInsights.ts`, `nucleos/[slug]/page.tsx` + `nucleusDetailPageData.ts`, overview da lista.
- **`electionPartySpectrum.ts`** (`src/lib/electionPartySpectrum.ts`): `partySpectrum(party) → 'esquerda' | 'direita' | 'centro' | 'outro' | null`; teste de cobertura dos `SG_PARTIDO` do seed BA 2022.
- **`computeTicketLeverage`** (`src/lib/electionInsights.ts`): Fase 1 — `{ lulaLeverage, jeronimoLeverage, lulaGap, jeronimoGap, status }`.
- **`computeTicketFlipOpportunity`** (`src/lib/electionInsights.ts`): entrada `{ winnerPresident, winnerGovernor, winnerFederal, federalVotesByParty: Record<string, number> | Array<{ party, votes }> }` → `{ status, trigger, rightShare, rightVotes, totalFederalVotes, president, governor, federal, message }`. Pré-condição: ambos majoritários `esquerda`. Gatilho A: espectro do `winnerFederal` = `direita`. Gatilho B: `rightShare >= RIGHT_SHARE_THRESHOLD`. `status`: `opportunity | noOpportunity | ambiguous | unknownSpectrum | incomplete`.
- **Loader** (`nucleusElectoralBaseline.ts` + view model): `winnerPresident` / `winnerGovernor` a partir de tallies; para B, expor `federalVotesByParty` (ou `rightShare` já computado no server) junto da agregação federal — **alinhar com A7 F1** (`GROUP BY` candidato/partido, sem rows zonais no cliente).
- **UI:** `NucleusInsights.tsx` — Alert Fase 1; Alert Fase 2 só em `opportunity` (copy distingue winner vs share vs both). Overview: contagem no `NucleusListOverview`.
- **Testes:** unit — vencedor direita / vencedor esquerda com share 40% / share 10% sem oportunidade / majoritário direita (pré-condição falha) / partido desconhecido / empate majoritário; int do agregador multi-zona.
- **Migration:** nenhuma. Sem Consent.
## Dependências
- [baseline-eleitoral-tse.md](baseline-eleitoral-tse.md) (votos de presidente/governador no baseline).
- **Dura:** A4 Baseline no produto ✓ — `getNucleusElectoralBaseline`, `winnerFederal`, votos da chapa, `electionTally.winner*`, votes federais nominais.
- **Suave (forte na prática):** A7 F1 — agregar federal no detalhe; o gatilho B **precisa** de soma por partido e herda o custo do ranking se A7 atrasar.
- **Suave (saída):** A6 — reusa `electionPartySpectrum` → tiers em `electionAlliances.ts`.
- Reusa: `BASELINE_TICKET_2022`, `NucleusInsights`, `computeGapVs2022`, design `Baseline-Eleitoral-2022`.
## Não escopo
- Dobradinha efetiva (plano [insight-dobradinha-2026.md](insight-dobradinha-2026.md)) — aqui só medimos alavancagem contra a chapa, não sugerimos parceiros.
- Sugerir parceiros de dobradinha 2026 → [insight-dobradinha-2026.md](insight-dobradinha-2026.md) (A6).
- Ranking/margem competitiva (top-N, `sollaRank`) → [insight-inteligencia-competitiva.md](insight-inteligencia-competitiva.md) — reusa a mesma agregação, não o copy de virada.
- Classificação/conversão por aptos → outros planos A5.
- Kits WhatsApp ou scripts de discurso gerados — só o insight.
- Persistência do flag no núcleo — sempre derivado.
## Referências
- [baseline-eleitoral-tse.md](baseline-eleitoral-tse.md)
- AGENTS.md
- `docs/roadmap.md` — A5 Insights; grafo A4 → A5; Janela 3 ordem 19
- [baseline-eleitoral-tse.md](baseline-eleitoral-tse.md) — A3/A4
- [escala-dry-pos-a4.md](escala-dry-pos-a4.md) — A7 F1 (agregação federal)
- [insight-dobradinha-2026.md](insight-dobradinha-2026.md) — taxonomia futura
- `src/lib/electionInsights.ts` — padrão `computeGapVs2022`
- `src/lib/electionResults.ts` — `BASELINE_TICKET_2022`
- `src/utilities/nucleusElectoralBaseline.ts` — loader + `winnerFederal`
- `src/utilities/nucleusViewModels.ts` — `NucleusElectoralBaselineViewModel`
- `src/collections/ElectionTally.ts` — `winnerParty` / `winnerVotes`
- `src/collections/ElectionCandidateVote.ts` — votos nominais + `party`
- `src/components/campaign/NucleusInsights.tsx`
- AGENTS.md — Election baseline; naming; `overrideAccess: false`; sem PII

View file

@ -25,7 +25,7 @@ Responsável: —
## Questões em aberto
- **Taxonomia de alinhamento político**: como classificar partidos/coligações em tiers (aliado / aliado histórico / neutro / adversário) relativos à chapa PT/Solla. Recomendação: mapeamento estático versionado em `src/lib/electionAlliances.ts` (partido→tier), derivado das federações/coligações oficiais de 2022 (FE Brasil da Esperança, Pela Bahia Pelo Brasil) e revalidado para 2026. Decisão de produto por tiers.
- **Taxonomia de alinhamento político**: como classificar partidos/coligações em tiers (aliado / aliado histórico / neutro / adversário) relativos à chapa PT/Solla. Recomendação: mapeamento estático versionado em `src/lib/electionAlliances.ts` (partido→tier), derivado das federações/coligações oficiais de 2022 (FE Brasil da Esperança, Pela Bahia Pelo Brasil) e revalidado para 2026. Preferir **estender** o espectro `esquerda|direita|centro` de `electionPartySpectrum.ts` (Fase 2 de [insight-alavancagem-chapa.md](insight-alavancagem-chapa.md)) em vez de um segundo mapa de partidos. Decisão de produto por tiers.
- **Fórmula do score**: como combinar alinhamento (ordinal) com força eleitoral (votos). Recomendação: score = `wAlign * tierWeight + wVotes * normalizedVotes`, com pesos `wAlign`/`wVotes` a definir com produto.
- Considerar só candidatos do mesmo cargo (dep. federal + estadual) ou também majoritários? Recomendação: só proporcionais (federal/estadual) para dobradinha com Solla.
- `lideranca` vê? Recomendação: sim, mas sem expor o tier de adversário (mostrar só aliados/neutros).

View file

@ -1,6 +1,6 @@
# Roadmap — Teqo
Atualizado em: 2026-07-19 (MVP + Ciclo 2 deployados; A2 entregue; A4 Baseline no produto + Gap vs 2022 implementado e mesclado em `main` (Fases 2–4 + simplify); A7 registrado — escala/DRY pós-A4 do `/simplify`; C2 engenharia pronta e mesclada em `main`; C3 Planos de Ação implementado e mesclado em `main`; C6 Escala e DRY pós-C2 implementado e mesclado em `main` (Fases 1–5 + simplify); C7 Escala e DRY pós-C3 implementado (Fases 1–5; feed O(n) condicional fora de escopo); C8 Escala e DRY pós-C6 implementado e mesclado em `main` (Fases 1–4 + duas passagens `/simplify`); C9 Escala e DRY pós-C8 implementado e mesclado em `main` (Fases 1–4 + simplify); C10 registrado — escala/DRY pós-C9 do `/simplify`; **C11 registrado** — escala/DRY pós-C7 do `/simplify` — [plano](plans/escala-dry-pos-c7.md); B2 fundação de geometrias do mapa entregue; B5 registrado — escala/DRY pós-B2 do `/simplify`; **Trilha E registrada — E1–E5 mapa de projeção por município**, equiparar/superar as planilhas "Mapa de projeção de votos 2026" — [plano](plans/mapa-projecao-municipios.md); **E6 registrado** — escala/DRY pós-E1+E3 do `/simplify` (aggregate lista/overview, geografia baseline, DRY formatação) — [plano](plans/escala-dry-pos-e1.md); **Onda 0 MVP** — Consent/privacidade provisórios auto-provisionados (migrations) + `/privacidade`; hold de PII real até jurídico — [plano](plans/onda-0.md); **O0+ registrado** — escala/DRY pós-Onda 0 do `/simplify` — [plano](plans/escala-dry-pos-onda0.md); **Visitados recentemente MVP** implementado (fill-in, client-side `localStorage`) — [plano](plans/visitados-recentemente.md); **VR+ registrado** — escala/DRY pós-visitados recentemente do `/simplify` — [plano](plans/escala-dry-pos-visitados-recentemente.md))
Atualizado em: 2026-07-19 (MVP + Ciclo 2 deployados; A2 entregue; A4 Baseline no produto + Gap vs 2022 implementado e mesclado em `main` (Fases 2–4 + simplify); A7 registrado — escala/DRY pós-A4 do `/simplify`; C2 engenharia pronta e mesclada em `main`; C3 Planos de Ação implementado e mesclado em `main`; C6 Escala e DRY pós-C2 implementado e mesclado em `main` (Fases 1–5 + simplify); C7 Escala e DRY pós-C3 implementado (Fases 1–5; feed O(n) condicional fora de escopo); C8 Escala e DRY pós-C6 implementado e mesclado em `main` (Fases 1–4 + duas passagens `/simplify`); C9 Escala e DRY pós-C8 implementado e mesclado em `main` (Fases 1–4 + simplify); C10 registrado — escala/DRY pós-C9 do `/simplify`; **C11 registrado** — escala/DRY pós-C7 do `/simplify` — [plano](plans/escala-dry-pos-c7.md); B2 fundação de geometrias do mapa entregue; B5 registrado — escala/DRY pós-B2 do `/simplify`; **Trilha E registrada — E1–E5 mapa de projeção por município**, equiparar/superar as planilhas "Mapa de projeção de votos 2026" — [plano](plans/mapa-projecao-municipios.md); **E6 registrado** — escala/DRY pós-E1+E3 do `/simplify` (aggregate lista/overview, geografia baseline, DRY formatação) — [plano](plans/escala-dry-pos-e1.md); **Onda 0 MVP** — Consent/privacidade provisórios auto-provisionados (migrations) + `/privacidade`; hold de PII real até jurídico — [plano](plans/onda-0.md); **O0+ registrado** — escala/DRY pós-Onda 0 do `/simplify` — [plano](plans/escala-dry-pos-onda0.md); **Visitados recentemente MVP** implementado (fill-in, client-side `localStorage`) — [plano](plans/visitados-recentemente.md); **VR+ registrado** — escala/DRY pós-visitados recentemente do `/simplify` — [plano](plans/escala-dry-pos-visitados-recentemente.md); **A5 alavancagem da chapa expandido** — Fase 2 “oportunidade de virada” (majoritários esquerda + federal direita por vencedor **ou** por participação proporcional alta) absorvida em [insight-alavancagem-chapa.md](plans/insight-alavancagem-chapa.md), sem item paralelo)
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.
@ -98,30 +98,30 @@ Objetivo: equiparar e superar as planilhas "Mapa de projeção de votos Solla 20
Os designs gerados pelo UX Pilot para os próximos ciclos estão em [`docs/design-refs/latest/`](design-refs/latest/) (pares `.png` + `.html` com o mesmo nome). **A UX/estrutura é a referência a seguir; a paleta não é** — os arquivos usam a paleta antiga (vermelho escuro `#8E0E23`, navy `#1B2B4B`, dourado `#C8874B`); toda implementação usa os tokens claros do tema `data-theme='campaign'` (`src/app/(frontend)/styles.css`: fundo branco, primário `#C51414`, superfícies neutras). Cada plano detalha o uso na sua seção "Referência visual (UX Pilot)".
| Item do roadmap | Design | Plano com instruções |
| ------------------------------------- | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| B1 Overview da lista de núcleos | `Lista-Nucleos-Overview` | [overview-lista-nucleos.md](plans/overview-lista-nucleos.md) |
| C1 Compartilhar página | `Compartilhar-Nucleo` | [compartilhar-pagina.md](plans/compartilhar-pagina.md) |
| A1 Território multi-município/bairro | `Formulario-Territorio` (bloco Território) | [territorio-multi-municipio-bairro.md](plans/territorio-multi-municipio-bairro.md) |
| A2 Zonas TSE + sugestões cruzadas | `Formulario-Territorio` (seção Zonas TSE + chips `{rótulo} +`) | [zonas-por-municipio.md](plans/zonas-por-municipio.md) |
| A3/A4 Baseline TSE 2022 + Gap vs 2022 | `Baseline-Eleitoral-2022` (+ bloco "Baseline 2022" em `Lista-Nucleos-Overview`) | [baseline-eleitoral-tse.md](plans/baseline-eleitoral-tse.md) |
| A5 Insights (5 planos) | `Baseline-Eleitoral-2022` (card "Insights do território", uma linha por insight) | [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) |
| A7 Escala e DRY pós-A4 | — (sem design novo; reusa baseline A4 + Alert/Progress) | [escala-dry-pos-a4.md](plans/escala-dry-pos-a4.md) |
| C2 Cadastro nominal de apoiadores | `Apoiadores-Lista`, `Apoiador-Ficha`, `Importar-CSV-Apoiadores` | [cadastro-nominal-apoiadores.md](plans/cadastro-nominal-apoiadores.md) |
| 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) |
| C8 Escala e DRY pós-C6 | — (sem design novo; reusa os de C2/C6) | [escala-dry-pos-c6.md](plans/escala-dry-pos-c6.md) |
| C9 Escala e DRY pós-C8 | — (sem design novo; reusa os de C2/C6/C8) | [escala-dry-pos-c8.md](plans/escala-dry-pos-c8.md) |
| C10 Escala e DRY pós-C9 | — (sem design novo; reusa os de C2/C6/C8/C9) | [escala-dry-pos-c9.md](plans/escala-dry-pos-c9.md) |
| C11 Escala e DRY pós-C7 | — (sem design novo; reusa os de C3 + padrões de núcleos/C6/C9) | [escala-dry-pos-c7.md](plans/escala-dry-pos-c7.md) |
| O0+ Escala e DRY pós-Onda 0 | — (sem design novo; reusa `/privacidade` + padrão revalidate `posts`) | [escala-dry-pos-onda0.md](plans/escala-dry-pos-onda0.md) |
| VR+ Escala e DRY pós-visitados | — (sem design novo; reusa dashboard + padrões de lista de núcleos) | [escala-dry-pos-visitados-recentemente.md](plans/escala-dry-pos-visitados-recentemente.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) |
| E6 Escala e DRY pós-E1+E3 | — (sem design novo; reusa Metas 2026 + overview B1) | [escala-dry-pos-e1.md](plans/escala-dry-pos-e1.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) |
| Item do roadmap | Design | Plano com instruções |
| ------------------------------------- | -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| B1 Overview da lista de núcleos | `Lista-Nucleos-Overview` | [overview-lista-nucleos.md](plans/overview-lista-nucleos.md) |
| C1 Compartilhar página | `Compartilhar-Nucleo` | [compartilhar-pagina.md](plans/compartilhar-pagina.md) |
| A1 Território multi-município/bairro | `Formulario-Territorio` (bloco Território) | [territorio-multi-municipio-bairro.md](plans/territorio-multi-municipio-bairro.md) |
| A2 Zonas TSE + sugestões cruzadas | `Formulario-Territorio` (seção Zonas TSE + chips `{rótulo} +`) | [zonas-por-municipio.md](plans/zonas-por-municipio.md) |
| A3/A4 Baseline TSE 2022 + Gap vs 2022 | `Baseline-Eleitoral-2022` (+ bloco "Baseline 2022" em `Lista-Nucleos-Overview`) | [baseline-eleitoral-tse.md](plans/baseline-eleitoral-tse.md) |
| A5 Insights (5 planos) | `Baseline-Eleitoral-2022` (card "Insights do território", uma linha por insight) | [conversão](plans/insight-taxa-conversao.md) · [classificação](plans/insight-classificacao-territorial.md) · [alavancagem](plans/insight-alavancagem-chapa.md) (Fase 1 conversão da base + Fase 2 virada: vencedor dir. **ou** share dir. alto) · [mobilização](plans/insight-mobilizacao-brancos-nulos.md) · [competitiva](plans/insight-inteligencia-competitiva.md) |
| A7 Escala e DRY pós-A4 | — (sem design novo; reusa baseline A4 + Alert/Progress) | [escala-dry-pos-a4.md](plans/escala-dry-pos-a4.md) |
| C2 Cadastro nominal de apoiadores | `Apoiadores-Lista`, `Apoiador-Ficha`, `Importar-CSV-Apoiadores` | [cadastro-nominal-apoiadores.md](plans/cadastro-nominal-apoiadores.md) |
| 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) |
| C8 Escala e DRY pós-C6 | — (sem design novo; reusa os de C2/C6) | [escala-dry-pos-c6.md](plans/escala-dry-pos-c6.md) |
| C9 Escala e DRY pós-C8 | — (sem design novo; reusa os de C2/C6/C8) | [escala-dry-pos-c8.md](plans/escala-dry-pos-c8.md) |
| C10 Escala e DRY pós-C9 | — (sem design novo; reusa os de C2/C6/C8/C9) | [escala-dry-pos-c9.md](plans/escala-dry-pos-c9.md) |
| C11 Escala e DRY pós-C7 | — (sem design novo; reusa os de C3 + padrões de núcleos/C6/C9) | [escala-dry-pos-c7.md](plans/escala-dry-pos-c7.md) |
| O0+ Escala e DRY pós-Onda 0 | — (sem design novo; reusa `/privacidade` + padrão revalidate `posts`) | [escala-dry-pos-onda0.md](plans/escala-dry-pos-onda0.md) |
| VR+ Escala e DRY pós-visitados | — (sem design novo; reusa dashboard + padrões de lista de núcleos) | [escala-dry-pos-visitados-recentemente.md](plans/escala-dry-pos-visitados-recentemente.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) |
| E6 Escala e DRY pós-E1+E3 | — (sem design novo; reusa Metas 2026 + overview B1) | [escala-dry-pos-e1.md](plans/escala-dry-pos-e1.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) |
**Sem design nesta leva** (encomendar na próxima rodada ou desenhar na implementação): A6 dobradinha 2026, B2/B3/B4 mapa da Bahia, C4 demandas, E1–E4 mapa de projeção por município (seguir os padrões de `Baseline-Eleitoral-2022` e `Lista-Nucleos-Overview`), fill-ins (listas globais, reset de senha). O detalhe do plano de ação (tabs Visão geral/Tarefas/Atualizações) e a tela de opt-in de push também não vieram — os planos correspondentes indicam o padrão a seguir.
@ -143,7 +143,7 @@ flowchart TD
A3["A3 Baseline TSE 2022 — Fase 1<br/>(import, collections) ✓"]
A4["A4 Baseline no produto + Gap vs 2022<br/>(detalhe, overview) ✓"]
A7["A7 Escala e DRY pós-A4<br/>(agregar federal / cityCode / Alert)"]
A5["A5 Insights: conversão, classificação,<br/>alavancagem, mobilização, competitiva"]
A5["A5 Insights: conversão, classificação,<br/>alavancagem (+virada), mobilização, competitiva"]
A6["A6 Dobradinha 2026"]
end
@ -246,33 +246,33 @@ Itens sem seta de entrada (**paralelizáveis a qualquer momento**): E1, E3, **O0
**Janela 2 — 05/08 → 16/08 (pré-propaganda): base nominal + inteligência + agenda prontas para o arranque.**
| Ordem | Item | Plano | Depende de | Paralelizável com |
| ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ | ---------------------------------- | -------------------------- |
| 7 | C2 Cadastro nominal de apoiadores _(engenharia pronta e mesclada em `main` 2026-07-18 — deploy + Consent keys ainda pendentes)_ | [detalhes](plans/cadastro-nominal-apoiadores.md) | produção: lote jurídico (Onda 0) | A4, C3, C6, C7 |
| 8 | C6 Escala e DRY pós-C2 (import em massa, KPI, shells compartilhados com núcleos) **(implementado e mesclado em `main` 2026-07-19 — Fases 1–5 + simplify)** | [detalhes](plans/escala-dry-pos-c2.md) | C2 (merge) | A4, C3, C7, C8 |
| 9 | A4 Baseline no produto + insight Gap vs 2022 **(implementado e mesclado em `main` 2026-07-18 — Fases 2–4 + simplify)** | [detalhes](plans/baseline-eleitoral-tse.md) | A3 + B1 (suave: A2) | C2, C3, C6, C7, C8 |
| 10 | C3 Eventos / agenda de mobilização (`actionPlan`) **(implementado e mesclado em `main` 2026-07-18)** | [detalhes](plans/eventos-agenda-mobilizacao.md) | A1 | C2, A4, C6, C7, C8 |
| 11 | C7 Escala e DRY pós-C3 **(entregue 2026-07-19 — Fases 1–5; feed O(n) condicional fora de escopo)** | [detalhes](plans/escala-dry-pos-c3.md) | C3 (suave: C6 F2) | A4, C6, C8, E1+E3, E2 |
| 12 | C8 Escala e DRY pós-C6 (locks bulk em 1 RT, leituras/`pg_trgm`, helpers drizzle, forms DRY) **(implementado e mesclado em `main` 2026-07-19 — Fases 1–4 + simplify)** _(cortável se a base nominal permanecer pequena)_ | [detalhes](plans/escala-dry-pos-c6.md) | C6 (merge) | A4, C3, C7, E1+E3, E2, C9 |
| 13 | C9 Escala e DRY pós-C8 (filtro unificado Payload↔SQL, `contactSearchQuery` no aggregate, loader único `/apoiadores`, forms restantes) **(implementado e mesclado em `main` 2026-07-19 — Fases 1–4 + simplify)** _(cortável se a base nominal permanecer pequena)_ | [detalhes](plans/escala-dry-pos-c8.md) | C8 (merge) | A4, C3, C7, E1+E3, E2, C10 |
| 14 | C10 Escala e DRY pós-C9 (dedup access lista, scope único de núcleos do coordenador, `errorProps` nos forms) _(cortável se a base nominal permanecer pequena; Fase 3 DRY pode entrar isolada)_ | [detalhes](plans/escala-dry-pos-c9.md) | C9 (merge) | A4, C3, C7, E1+E3, E2, C11 |
| 15 | C11 Escala e DRY pós-C7 (feed O(n) condicional, loaders do detalhe, selects por aba, hot paths mutação/form, utilities de busca) _(cortável se a agenda permanecer pequena; Fase 1 migration só com volume medido)_ | [detalhes](plans/escala-dry-pos-c7.md) | C7 (merge) | A4, C3, C10, E1+E3, E2 |
| 16 | E1 Metas em cenários + prioridade + E3 dobradinhas/encaminhamentos manuais (migration compartilhada em `electoralNucleus`; `/simplify` → E6) | [detalhes](plans/mapa-projecao-municipios.md) | — | tudo da janela |
| 17 | E6 Escala e DRY pós-E1+E3 (aggregate SQL metas/prioridade, geografia baseline, DRY formatação) _(cortável se poucos núcleos ativos)_ | [detalhes](plans/escala-dry-pos-e1.md) | E1+E3 merge em `main` | E2, C10, A7 |
| 18 | E2 Série histórica TSE 2014/2018 + tendência automática (seed por ano; sem migration) | [detalhes](plans/mapa-projecao-municipios.md) | A4 ✓ (suave: A7 F1 — mesmo loader) | E1+E3, E6, C7, C8, C9, C10, C11 |
| Ordem | Item | Plano | Depende de | Paralelizável com |
| ----- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ | ---------------------------------- | ------------------------------- |
| 7 | C2 Cadastro nominal de apoiadores _(engenharia pronta e mesclada em `main` 2026-07-18 — deploy + Consent keys ainda pendentes)_ | [detalhes](plans/cadastro-nominal-apoiadores.md) | produção: lote jurídico (Onda 0) | A4, C3, C6, C7 |
| 8 | C6 Escala e DRY pós-C2 (import em massa, KPI, shells compartilhados com núcleos) **(implementado e mesclado em `main` 2026-07-19 — Fases 1–5 + simplify)** | [detalhes](plans/escala-dry-pos-c2.md) | C2 (merge) | A4, C3, C7, C8 |
| 9 | A4 Baseline no produto + insight Gap vs 2022 **(implementado e mesclado em `main` 2026-07-18 — Fases 2–4 + simplify)** | [detalhes](plans/baseline-eleitoral-tse.md) | A3 + B1 (suave: A2) | C2, C3, C6, C7, C8 |
| 10 | C3 Eventos / agenda de mobilização (`actionPlan`) **(implementado e mesclado em `main` 2026-07-18)** | [detalhes](plans/eventos-agenda-mobilizacao.md) | A1 | C2, A4, C6, C7, C8 |
| 11 | C7 Escala e DRY pós-C3 **(entregue 2026-07-19 — Fases 1–5; feed O(n) condicional fora de escopo)** | [detalhes](plans/escala-dry-pos-c3.md) | C3 (suave: C6 F2) | A4, C6, C8, E1+E3, E2 |
| 12 | C8 Escala e DRY pós-C6 (locks bulk em 1 RT, leituras/`pg_trgm`, helpers drizzle, forms DRY) **(implementado e mesclado em `main` 2026-07-19 — Fases 1–4 + simplify)** _(cortável se a base nominal permanecer pequena)_ | [detalhes](plans/escala-dry-pos-c6.md) | C6 (merge) | A4, C3, C7, E1+E3, E2, C9 |
| 13 | C9 Escala e DRY pós-C8 (filtro unificado Payload↔SQL, `contactSearchQuery` no aggregate, loader único `/apoiadores`, forms restantes) **(implementado e mesclado em `main` 2026-07-19 — Fases 1–4 + simplify)** _(cortável se a base nominal permanecer pequena)_ | [detalhes](plans/escala-dry-pos-c8.md) | C8 (merge) | A4, C3, C7, E1+E3, E2, C10 |
| 14 | C10 Escala e DRY pós-C9 (dedup access lista, scope único de núcleos do coordenador, `errorProps` nos forms) _(cortável se a base nominal permanecer pequena; Fase 3 DRY pode entrar isolada)_ | [detalhes](plans/escala-dry-pos-c9.md) | C9 (merge) | A4, C3, C7, E1+E3, E2, C11 |
| 15 | C11 Escala e DRY pós-C7 (feed O(n) condicional, loaders do detalhe, selects por aba, hot paths mutação/form, utilities de busca) _(cortável se a agenda permanecer pequena; Fase 1 migration só com volume medido)_ | [detalhes](plans/escala-dry-pos-c7.md) | C7 (merge) | A4, C3, C10, E1+E3, E2 |
| 16 | E1 Metas em cenários + prioridade + E3 dobradinhas/encaminhamentos manuais (migration compartilhada em `electoralNucleus`; `/simplify` → E6) | [detalhes](plans/mapa-projecao-municipios.md) | — | tudo da janela |
| 17 | E6 Escala e DRY pós-E1+E3 (aggregate SQL metas/prioridade, geografia baseline, DRY formatação) _(cortável se poucos núcleos ativos)_ | [detalhes](plans/escala-dry-pos-e1.md) | E1+E3 merge em `main` | E2, C10, A7 |
| 18 | E2 Série histórica TSE 2014/2018 + tendência automática (seed por ano; sem migration) | [detalhes](plans/mapa-projecao-municipios.md) | A4 ✓ (suave: A7 F1 — mesmo loader) | E1+E3, E6, C7, C8, C9, C10, C11 |
**Janela 3 — 16/08 → set (campanha de rua): inteligência ampliada, visualização e engajamento.**
| Ordem | Item | Plano | Depende de | Paralelizável com |
| ----- | -------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ---------------------- |
| 18 | E4 Import único da planilha (`pnpm db:seed:mapa`; sem criar `Contact`/`leadership`) | [detalhes](plans/mapa-projecao-municipios.md) | E1 + E3 (suave: E2 — tendência visível na revisão; E6 F1 — perf da lista) | A5, A7, B3, B5, C4, D2 |
| 19 | 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 | A7, B3, B5, C4, E4, D1 |
| 20 | A7 Escala e DRY pós-A4 (agregar federal no detalhe, `cityCode` TSE, Alert `confirmed` + Progress) | [detalhes](plans/escala-dry-pos-a4.md) | A4 ✓ (suave: A5/B3/E2 amplificam o custo) | A5, B3, B5, C4, E4, D1 |
| 21 | 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; E1/E2 métricas; B5 F1 lazy geometrias) | A5, A7, B5, C4, E4, D1 |
| 22 | B5 Escala e DRY pós-B2 (lazy geometrias + cache CLI) | [detalhes](plans/escala-dry-pos-b2.md) | B2 ✓ (F1 preferencialmente com B3) | A5, A7, B3, C4, E4, D1 |
| 23 | C4 Demandas | [detalhes](plans/demandas-campanha.md) | A1 (suave: C3 para a relação `actionPlan`) | A5, A7, B3, B5, E4, D1 |
| 24 | D1 PWA `/campanha` **(entregue antecipado em 2026-07-18)** | [detalhes](plans/pwa-campanha.md) | — | tudo |
| 25 | 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, A7, B3, B5, C4, E4 |
| Ordem | Item | Plano | Depende de | Paralelizável com |
| ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ---------------------- |
| 18 | E4 Import único da planilha (`pnpm db:seed:mapa`; sem criar `Contact`/`leadership`) | [detalhes](plans/mapa-projecao-municipios.md) | E1 + E3 (suave: E2 — tendência visível na revisão; E6 F1 — perf da lista) | A5, A7, B3, B5, C4, D2 |
| 19 | A5 Insights derivados do baseline (5 itens, paralelizáveis entre si; conversão e classificação exigem limiares de produto; alavancagem inclui Fase 2 virada por vencedor **ou** share da direita) | [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 | A7, B3, B5, C4, E4, D1 |
| 20 | A7 Escala e DRY pós-A4 (agregar federal no detalhe, `cityCode` TSE, Alert `confirmed` + Progress) | [detalhes](plans/escala-dry-pos-a4.md) | A4 ✓ (suave: A5/B3/E2 amplificam o custo) | A5, B3, B5, C4, E4, D1 |
| 21 | 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; E1/E2 métricas; B5 F1 lazy geometrias) | A5, A7, B5, C4, E4, D1 |
| 22 | B5 Escala e DRY pós-B2 (lazy geometrias + cache CLI) | [detalhes](plans/escala-dry-pos-b2.md) | B2 ✓ (F1 preferencialmente com B3) | A5, A7, B3, C4, E4, D1 |
| 23 | C4 Demandas | [detalhes](plans/demandas-campanha.md) | A1 (suave: C3 para a relação `actionPlan`) | A5, A7, B3, B5, E4, D1 |
| 24 | D1 PWA `/campanha` **(entregue antecipado em 2026-07-18)** | [detalhes](plans/pwa-campanha.md) | — | tudo |
| 25 | 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, A7, B3, B5, C4, E4 |
**Janela 4 — set → 04/10 (reta final): dobradinha, dia D e estabilização.**
@ -296,6 +296,7 @@ Itens sem seta de entrada (**paralelizáveis a qualquer momento**): E1, E3, **O0
### Itens consolidados/removidos nesta revisão (2026-07-17)
- **"Insight: oportunidade de virada pela chapa"** _(2026-07-19)_ — não virou item A5/A8 paralelo: absorvido como **Fase 2** de [alavancagem da chapa](plans/insight-alavancagem-chapa.md). Gatilhos: (A) dep. federal mais votado de direita **ou** (B) participação proporcional da direita nos nominais federais ≥ limiar — ambos com pré-condição de majoritários locais de esquerda; discurso de completar a chapa.
- **"Insight: Gap vs 2022" como item separado** — removido: é a Fase 4 do plano de [baseline TSE 2022](plans/baseline-eleitoral-tse.md) (item A4), não um item próprio.
- **"Import do cadastro oficial de zonas TSE e/ou polígonos GeoJSON"** — absorvido: o cadastro tabular é o plano [zonas-por-municipio](plans/zonas-por-municipio.md) (A2); os polígonos são a camada de zonas do [plano do mapa](plans/mapa-bahia-geometrias.md) (B4).
- **"Sugestões cruzadas no formulário de território"** (chips `{Município} +` / `{TI} +` em ZEs; irmãos do TI e cidades da ZE em Municípios) — absorvido em A2 na revisão 2026-07-18; substitui o auto-preenchimento forçado + read-only da versão 2026-07-17 do mesmo plano.