teqo-backup/docs/plans/mapa-projecao-municipios.md
Francisco Ignácio Fontoura Solla e8a440b837 Pass 2 W5: close-out de documentacao
- 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)
2026-07-25 16:08:38 -03:00

13 KiB
Raw Permalink Blame History

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 — electoralNucleus absorve a camada estratégica (metas, prioridade, dobradinhas). A visão município-a-município é derivada: núcleos + baseline TSE agregados por cities[]. (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:tse parametrizado 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 com Consent. (AGENTS.md — pessoa = Contact + junção com consent; Onda 0)
  • Tendência é derivada, não persistida — computeVoteTrend calcula em leitura a partir das collections A3, com limiares versionados como constantes (mesmo padrão de computeGapVs2022 em src/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.1 em electionInsights.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, sem Contact/leadership) permanece documentado abaixo como referência histórica.

E1 — Metas em cenários + prioridade no Núcleo (migration)

  • electoralNucleus (src/collections/ElectoralNucleus.ts): grupo voteGoals (good/regular/minimum, numbers opcionais, validação good ≥ regular ≥ minimum) e priority (select alta|normal, default normal, indexado). Escrita staff via canUpdateElectoralNucleus (geral ou coordenador designado no núcleo) — sem canSetDerivedNucleusField (esse helper é só Payload-admin, usado nas estimativas derivadas). Leitura para os três papéis; E3 (dobradinhaNotes/nextSteps) staff-only via select/VM como strengths/risks.
  • UI: card "Metas 2026" na aba Visão geral (3 cenários + Progress da estimativa confirmada vs. meta regular, ao lado de NucleusElectoralBaseline); filtro priority na lista (padrão dos filtros existentes em src/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 (skill payload-migrations).

E2 — Série histórica TSE 2014/2018 + tendência automática

  • Seed: scripts/seed-tse-results.mjs parametrizado por --year=2014|2018|2022 (URLs *_YYYY.zip das mesmas famílias TSE; SHA-256 logado, pins opcionais em EXPECTED_SHA256). Escopo v1 histórico: só deputado_federal, turno 1, BA. Parser em electionResultsCsv.ts + build em electionResultsBuild.ts (offices opcional via federalOnlyElectionOffices()). 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%) em src/lib/electionInsights.ts → queda|mantem|aumento|semBaseline; par preferido 2018→2022.
  • Loader: loadCandidateSeriesByGeography (query candidate-only multi-ano, espelha overview Gap) em nucleusElectoralBaseline.ts; loadNucleusTrendOverview para distribuição na lista.
  • UI: série 2014→2018→2022 + badge no NucleusElectoralBaseline; Alert vote-trend no NucleusInsights; card "Tendência histórica" no NucleusListOverview.
  • Custo de I/O: série usa query estreita (não multiplica o path caro do ranking federal — A7 F1 permanece backlog separado). Passagem /simplify no 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) e nextSteps (textarea — encaminhamentos). Editados via NucleusIntelligenceDialog (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.mjs que 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 (alta quando flagado ou presente na aba PRIORITÁRIAS), dobradinhaNotes, nextSteps (encaminhamentos + observações). Município → nome canônico via canonicalizeMunicipalityName (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; override ALLOW_REMOTE_DB=true com 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, seed db: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, skill payload-migrations.

Não escopo

  • Criar Contact/leadership a partir da planilha — vai para o fluxo normal com Consent (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–3
  • docs/sheets/Mapa_projecao_votos_Solla_2026.xlsx e docs/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 estender
  • src/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-/simplify do E2
  • AGENTS.md — Campaign auth, pessoa = Contact + junção, overrideAccess: false, migrations com push: false, naming