- AGENTS.md: Known Gap #2 resolvido (D3), arvore de estrutura (+AllocationDecision, PrivacyPolicy, CampaignGoals; components por dominio), URLs novas (/campanha/assessores|conceitos|perfil), entrada 'Recently resolved' do Pass 2 - tracker: todos os workstreams done; secao de achados de drift de docs de produto (PRODUCT/DESIGN/CUSTOMER - reportados, nao aplicados) - TESTING.md: 46/48/7 + linhas do safety net (formActions wrapper, voteTrend, submitWhatsapp) - TECH-DEBT: smells fechados (municipalityUi split, detail RSCs, formActions, components subpastas, electionInsights) - planos: status entregue nos 5 do Pass 2; escala-dry-pos-b16 absorvido; C8 F3/F4 fechados em escala-dry-pos-c6; notas de costura em B17/B18/cenario-junto; nota de conflito E4R em mapa-projecao - roadmap: entrada Pass 2 no 'Ja entregue', B17/B18 marcados como triviais, B16+ riscado, header atualizado - notebook do projeto: entrada Pass 2 - DESIGN.md: secao do sistema de listas (unico drift aplicado, conforme plano)
13 KiB
Mapa de projeção de votos por município (equiparar e superar as planilhas 2026)
Status: E1, E2 e E3 implementados e mesclados em main (2026-07-19); E4 cortado (2026-07-19) e reaberto/entregue como E4R em 2026-07-24 — atenção (nota 2026-07-25): o E4R fase 2 importa TAMBÉM as colunas de estratégia (SITUAÇÃO/DOBRADINHAS/LIDERANÇAS/ASSESSOR/…), superando o que este plano descrevia como import zero-PII (import-planilha-projecao.md — pnpm db:seed:projecao); E5 pendente
Atualizado em: 2026-07-24 (E4R entregue — seed overwrite-always)
Item do roadmap: docs/roadmap.md (Trilha E — itens E1–E5, "Ciclo 3")
Responsável: —
Contexto
A campanha opera hoje sobre duas planilhas Excel versionadas em docs/sheets/ ("Mapa projeção de votos Solla 2026.xlsx" e Mapa_projecao_votos_Solla_2026.xlsx — a segunda é a rica). Elas cobrem os 417 municípios da Bahia com: série histórica de votos de Solla (2014/2018/2022), classificação de tendência (Queda/Mantém/Aumento), metas de votos em três cenários (Bom/Regular/Mínimo), lista de ~50 municípios prioritários com meta estadual de 71.000 votos, rede política por município (assessor responsável, lideranças, dobradinhas negociadas), encaminhamentos operacionais e um drill de Salvador por bairro (reconciliação não-oficial local↔bairro).
A vertical /campanha já cobre ou supera parte disso: baseline TSE 2022 por geografia do núcleo (A3/A4, mais rico que a planilha), CRM de lideranças (leadership↔Contact), responsável por território (coordinators), overview agregado do conjunto filtrado (B1) e Salvador por zona (baseline município×zona). Os gaps que este plano fecha: metas em cenários, prioridade, série histórica além de 2022 com tendência automática, dobradinhas/encaminhamentos manuais e o import único da planilha. Decisão de produto de 2026-07-19: equiparar e superar as planilhas dentro do produto, sem manter a planilha como fonte paralela.
Objetivos
- Metas de votos em três cenários e flag de prioridade por núcleo, com escrita staff e leitura pelos três papéis (E1).
- Série histórica TSE 2014/2018/2022 (deputado federal, turno 1, BA) com classificação automática de tendência por núcleo (E2) — dado público, sem
Consent. - Campos manuais staff-only de dobradinhas e encaminhamentos na inteligência do núcleo (E3), excluídos dos view models de
lideranca. Import único e idempotente da planilha rica para núcleos (E4)— cortado 2026-07-19 (preenchimento manual via UI E1+E3; ver roadmap § Itens cortados).- Registrar Salvador por bairro como item futuro (E5), sem implementar.
- Migrations somente em E1+E3 (campos novos em
electoralNucleus); E2 e E4 não alteram schema.
Decisões travadas
- Sem entidade nova por município —
electoralNucleusabsorve a camada estratégica (metas, prioridade, dobradinhas). A visão município-a-município é derivada: núcleos + baseline TSE agregados porcities[]. (decisão de produto 2026-07-19; evita cadastro paralelo e reusa access/UI de núcleos) - Série histórica 2014/2018 vem do TSE oficial, estendendo o seed A3 (
pnpm db:seed:tseparametrizado por ano) — não dos números da planilha. (decisão de produto 2026-07-19; mesma proveniência e idempotência por escopo(year, office, turn)já existentes) - Lideranças e assessores da planilha entram como texto staff-only, nunca como
Contact/leadership. Não há telefone nem consent na planilha; o CRM real é alimentado depois pelo fluxo normal comConsent. (AGENTS.md — pessoa =Contact+ junção com consent; Onda 0) - Tendência é derivada, não persistida —
computeVoteTrendcalcula em leitura a partir das collections A3, com limiares versionados como constantes (mesmo padrão decomputeGapVs2022emsrc/lib/electionInsights.ts). - i18n e naming seguem o AGENTS.md: identificadores em inglês (
voteGoals.good/regular/minimum,priority,dobradinhaNotes,nextSteps,computeVoteTrend), strings visíveis e valores de enum de dados em pt-BR (alta|normal,queda|mantem|aumento).
Questões em aberto
- Banda de estabilidade da tendência? Resolvido (2026-07-19): ±10% (
VOTE_TREND_STABLE_BAND = 0.1emelectionInsights.ts); par preferido 2018→2022. - E4 cria núcleo para todo município com dado estratégico (≈193) ou só para os ~50 prioritários? Cortado (2026-07-19) — item E4 não será implementado; dados estratégicos entram manualmente via E1+E3.
- Meta estadual (71.000) fica onde? Recomendação: derivada — soma das metas dos núcleos no dashboard; sem campo global novo. Se produto quiser meta editável independente da soma, vira campo em global de campanha (fase posterior).
Abordagem proposta
flowchart LR
subgraph E ["Trilha E — mapa de projeção"]
E1["E1 Metas em cenários + prioridade<br/>(migration)"]
E2["E2 Série TSE 2014/2018<br/>+ tendência derivada"]
E3["E3 Dobradinhas + encaminhamentos<br/>manuais (staff-only)"]
E5["E5 Salvador por bairro<br/>(registrado; futuro)"]
end
A3["A3/A4 baseline TSE 2022 ✓"] --> E2
E1 -.metas no coroplético.-> B3["B3 Mapa Leaflet"]
E2 -.classe por cor.-> B3
E2 -.dataset votacao_secao.-> E5
E4 (import único
pnpm db:seed:mapa) — cortado 2026-07-19. Decisão de produto: preencher metas, prioridade, dobradinhas e encaminhamentos manualmente na UI (E1+E3). O desenho original (script CLI idempotente, semContact/leadership) permanece documentado abaixo como referência histórica.
E1 — Metas em cenários + prioridade no Núcleo (migration)
electoralNucleus(src/collections/ElectoralNucleus.ts): grupovoteGoals(good/regular/minimum, numbers opcionais, validaçãogood ≥ regular ≥ minimum) epriority(selectalta|normal, defaultnormal, indexado). Escrita staff viacanUpdateElectoralNucleus(geralou coordenador designado no núcleo) — semcanSetDerivedNucleusField(esse helper é só Payload-admin, usado nas estimativas derivadas). Leitura para os três papéis; E3 (dobradinhaNotes/nextSteps) staff-only via select/VM comostrengths/risks.- UI: card "Metas 2026" na aba Visão geral (3 cenários +
Progressda estimativa confirmada vs. meta regular, ao lado deNucleusElectoralBaseline); filtropriorityna lista (padrão dos filtros existentes emsrc/utilities/nucleusUi.ts/buildNucleusListWhere); bloco no overview B1 (src/components/campaign/NucleusListOverview.tsx): soma das metas por cenário no conjunto filtrado + contagem de prioritários; dashboard: soma estadual das metas (equivalente à "META: 71.000"). - Migration:
pnpm migrate:create add_nucleus_goals_strategy(uma migration só para E1+E3),pnpm generate:types(skillpayload-migrations).
E2 — Série histórica TSE 2014/2018 + tendência automática
- Seed:
scripts/seed-tse-results.mjsparametrizado por--year=2014|2018|2022(URLs*_YYYY.zipdas mesmas famílias TSE; SHA-256 logado, pins opcionais emEXPECTED_SHA256). Escopo v1 histórico: sódeputado_federal, turno 1, BA. Parser emelectionResultsCsv.ts+ build emelectionResultsBuild.ts(officesopcional viafederalOnlyElectionOffices()). Número do candidato:CANDIDATE_NUMBER_BY_YEAR(1313 nos três pleitos). Idempotência por(year, office, turn)— sem migration. - Insight:
computeVoteTrend+VOTE_TREND_STABLE_BAND = 0.1(±10%) emsrc/lib/electionInsights.ts→queda|mantem|aumento|semBaseline; par preferido 2018→2022. - Loader:
loadCandidateSeriesByGeography(query candidate-only multi-ano, espelha overview Gap) emnucleusElectoralBaseline.ts;loadNucleusTrendOverviewpara distribuição na lista. - UI: série 2014→2018→2022 + badge no
NucleusElectoralBaseline; Alertvote-trendnoNucleusInsights; card "Tendência histórica" noNucleusListOverview. - Custo de I/O: série usa query estreita (não multiplica o path caro do ranking federal — A7 F1 permanece backlog separado). Passagem
/simplifyno branch; débitos maiores → escala-dry-pos-e2.md (E7).
E3 — Inteligência estratégica manual: dobradinhas + encaminhamentos
- Campos staff-only no bloco de inteligência do núcleo (mesma redação/access dos campos estratégicos existentes —
strengths,risks):dobradinhaNotes(textarea — quem dobra ali hoje, estado da negociação) enextSteps(textarea — encaminhamentos). Editados viaNucleusIntelligenceDialog(src/components/campaign/NucleusIntelligenceDialog.tsx+nucleusIntelligenceUi.ts). - Excluídos dos view models de
lideranca(src/utilities/nucleusViewModels.ts, padrão já existente de campos sensíveis). - Não conflita com A6 (dobradinha automática pós-TSE-2026) nem C4 (Demandas estruturadas): E3 é a versão manual imediata; os planos futuros complementam sem substituição forçada.
- Migration compartilhada com E1 (acima).
E4 — Import único da planilha (pnpm db:seed:mapa) — CORTADO
Status: cortado em 2026-07-19. Motivo: decisão de produto — preferência por recolocar dados manualmente na UI em vez de bootstrap via planilha. Alternativa: criar/editar núcleos e preencher voteGoals, priority, dobradinhaNotes e nextSteps via E1+E3 em /campanha/nucleos.
Desenho original (não implementar):
- Script
scripts/seed-mapa-projecao.mjsque lêdocs/sheets/Mapa_projecao_votos_Solla_2026.xlsx(a rica) e cria/atualiza núcleos por município via Local API em transação:voteGoals(parse "Bom: X | Regular: Y | Mínimo: Z"),priority(altaquando flagado ou presente na aba PRIORITÁRIAS),dobradinhaNotes,nextSteps(encaminhamentos + observações). Município → nome canônico viacanonicalizeMunicipalityName(src/lib/bahiaMunicipalityCodes.ts); região derivada no servidor (A1). - Nomes de assessores/lideranças da planilha entram como texto nos campos staff-only — nunca criam
Contact/leadership(decisão travada acima). - Guard de banco local (mesma família dos outros seeds:
pnpm db:seed:posts/db:seed:tse; overrideALLOW_REMOTE_DB=truecom o runbook documentado); idempotente por slug de núcleo; municípios sem nenhum dado estratégico não geram núcleo. Sem revalidate (páginas de campanha são dinâmicas).
E5 — Salvador por bairro (registrar, não implementar)
- A aba SALVADOR da planilha usa uma reconciliação não-oficial local-TSE→bairro (DE-PARA). Reproduzir exigiria o dataset TSE
votacao_secao+ tabela de mapeamento versionada — custo alto para valor incerto. Fica registrado no roadmap como item futuro condicionado a decisão de produto; a visão por zona de Salvador já é coberta pelo baseline município×zona atual.
Dependências
- E2 ← A3/A4 (entregues): collections
electionTally/electionCandidateVote/electionCandidate, seeddb:seed:tse,getNucleusElectoralBaseline,computeGapVs2022. E4 ← E1 + E3 (duras)— E4 cortado; E1+E3 atendem operação manual.- E1/E3 não dependem de nada novo — paralelizáveis com o restante da Janela 2.
- Suaves para B3 (mapa Leaflet): metas e classe de tendência como métricas do coroplético.
- Reusa:
withPayloadTransaction, locks advisory, padrões de filtro/overview B1, skillpayload-migrations.
Não escopo
- Criar
Contact/leadershipa partir da planilha — vai para o fluxo normal comConsent(Onda 0 / cadastro-nominal-apoiadores.md). - Dobradinha automática por dados TSE 2026 — insight-dobradinha-2026.md (A6).
- Encaminhamentos estruturados com status/responsável — demandas-campanha.md (C4).
- Classificação territorial por limiares de produto — insight-classificacao-territorial.md (A5);
computeVoteTrendé série própria do candidato, não classificação de território. - Salvador por bairro (E5) — registrado, sem plano de implementação neste ciclo.
- Previsão estatística de votos — fora de escopo do ciclo (roadmap, revisão 2026-07-17).
Referências
docs/roadmap.md— Trilha E / Ciclo 3, janelas 2–3docs/sheets/Mapa_projecao_votos_Solla_2026.xlsxedocs/sheets/Mapa projeção de votos Solla 2026.xlsx— planilhas-fonte (estrutura, abas RESUMO/PRIORITÁRIAS/SALVADOR)src/collections/ElectoralNucleus.ts— campos, hooks e access do núcleo (E1/E3)src/lib/electionInsights.ts+src/utilities/nucleusElectoralBaseline.ts— padrão de insight derivado e agregação do baseline (E2)scripts/seed-tse-results.mjs+src/lib/electionResultsBuild.ts+src/lib/electionResultsCsv.ts— seed TSE parametrizado por ano (E2)src/components/campaign/NucleusElectoralBaseline.tsx,NucleusListOverview.tsx,NucleusIntelligenceDialog.tsx— superfícies de UI a estendersrc/lib/bahiaMunicipalityCodes.ts(canonicalizeMunicipalityName) — reconciliação de nomes no import (E4)- escala-dry-pos-a4.md — A7 F1, custo de I/O do loader do baseline
- escala-dry-pos-e2.md — E7, débitos pós-
/simplifydo E2 - AGENTS.md — Campaign auth, pessoa =
Contact+ junção,overrideAccess: false, migrations compush: false, naming