From e468f4192e88a8f99f9fd311e0d2334062a99967 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Ign=C3=A1cio=20Fontoura=20Solla?= Date: Fri, 24 Jul 2026 02:54:28 -0300 Subject: [PATCH] Sync roadmap and intel plans with the 2026-07-23 field session; add E4R/A11/E16/E17 plans --- .cursor/rules/projects/nucleos-eleitorais.mdc | 3 +- AGENTS.md | 2 +- docs/plans/conta-da-cadeira.md | 6 +- docs/plans/dossie-municipio.md | 91 +++++++++++++++++ docs/plans/fila-de-alocacao.md | 10 +- docs/plans/import-planilha-projecao.md | 97 +++++++++++++++++++ docs/plans/inteligencia-campanha.md | 40 +++++--- docs/plans/mapa-projecao-municipios.md | 2 +- docs/plans/onda-0.md | 3 +- docs/plans/planejador-de-giros.md | 4 +- docs/plans/ranking-votos-municipio.md | 83 ++++++++++++++++ docs/plans/registro-fundacao.md | 1 + docs/plans/tabela-ti-inicio.md | 92 ++++++++++++++++++ docs/roadmap.md | 67 ++++++++----- 14 files changed, 447 insertions(+), 54 deletions(-) create mode 100644 docs/plans/dossie-municipio.md create mode 100644 docs/plans/import-planilha-projecao.md create mode 100644 docs/plans/ranking-votos-municipio.md create mode 100644 docs/plans/tabela-ti-inicio.md diff --git a/.cursor/rules/projects/nucleos-eleitorais.mdc b/.cursor/rules/projects/nucleos-eleitorais.mdc index 0a8da487..e2ba6694 100644 --- a/.cursor/rules/projects/nucleos-eleitorais.mdc +++ b/.cursor/rules/projects/nucleos-eleitorais.mdc @@ -7,6 +7,7 @@ alwaysApply: false ## Status +- **Sync roadmap × sessão de campo (2026-07-24):** os achados da sessão real do CG (2026-07-23, `docs/CUSTOMER.md`) foram incorporados ao roadmap e aos planos de inteligência — âncora da mesa = **% da própria votação** (A11 entrega a lente; E9/E10/B13 expõem ao lado de LQ/captura); piso 150 mil resolve a meta inicial de E8; O6 vira **E16 dossiê do município** + gap **G11 emendas por município**; O7 confirma C12 não cortável; E13 rebaixado na fila de corte ("perna" é a restrição dominante); gatilho de D3 reformulado (deltas do ZAP não absorvidos pelo fluxo sede-digita/C12); FD2 absorvido por R6; O8 "ilhas isoladas" em A validar. **Decisão de produto: E4 reaberto como E4R** — seed único via script das planilhas de `docs/sheets/` (arquivo com espaços = canônico de estratégia; underscore = referência Salvador; v1 só `voteGoals`/`priority`, sem PII; Salvador pulado com relatório). Novos itens: **E4R** (janela 1, não cortável), **A11**, **E17 tabela TI no Início** (pedido do candidato; primeira fatia de E12). Planos novos: `import-planilha-projecao.md`, `ranking-votos-municipio.md`, `tabela-ti-inicio.md`, `dossie-municipio.md`. - **DEPLOY EM PRODUÇÃO REALIZADO (2026-07-23).** A cadeia destrutiva da remodelagem (`20260721_020109_remodel_municipalities` → `20260723_200000_remodel_municipalities` + `20260723_202000_reconcile_municipality_remodel`) foi aplicada em produção pelos builds Vercel de 2026-07-23 (deploys production READY dos commits `a7b8f1b`/`c7a1b74`/`59868a9`). **Este item supersede toda menção "deploy pendente" nas entradas abaixo.** Pendências operacionais: smoke pós-deploy + onboarding do time (Onda 0 §2/§4 em [`docs/roadmap.md`](../../../docs/roadmap.md)); hold de PII real segue no lote jurídico. - **Hardening de engenharia entregue e em produção (2026-07-23, Fases 0–6):** (0) access lockdown — `users`/`signature`/`subscription`/`consent` admin-only, escrita de CMS (`post`/`tag`/`media`/`petition`) admin-only, gates de rota do leader em `demandas*`/`planos*`, `createLeaderSupporter` com escopo correto, `candidate` tratado como staff irrestrito em todas as superfícies; (1) tooling gate — knip no CI (`.github/workflows/ci.yml`, mirror GitHub), `pnpm lint --max-warnings=0`, ban `as never` (helpers `hookFilledCreateData`/`stub`), tsconfig consolidado + `noUnusedLocals`, ~12 módulos mortos deletados; (2) caching — artefato TSE commitado (`pnpm build:election-aggregates` → `src/lib/electionAggregates/`, loader `bahiaElectionAggregates.ts`), `unstable_cache` com tag `election-tse` nos loaders de eleições, `loadMunicipalityScope` (React `cache()`) dedupe dashboard+mapa+lista; (3) pending honesto — bug de revalidação do toggle de tarefa, `CampaignListPendingBoundary` esmaecendo resultados, transição no compare do mapa, mapa do dashboard + abas do detalhe em Suspense; (4) state scoping — hover do mapa isolado em `MunicipalityMapSelection`, provider de cenário estreitado, `LeaderContactForm` extraído como ilha cliente; (5) estrutura — `campaignAccess` dividido em `src/utilities/access/*` (re-export preservado), `supporterImport.ts` separado, `runStaffEntityMutation` + `runCampaignRedirectFormAction` dedupando twins e 4 ladders de formActions, rename `PLAZA_*`→`MUNICIPALITY_*` concluído; (6) docs + regra `.cursor/rules/engineering-standards.mdc`. Tracker: [`docs/IMPROVE-CODE-QUALITY-PLAN.md`](../../../docs/IMPROVE-CODE-QUALITY-PLAN.md) · ledger: [`docs/TECH-DEBT.md`](../../../docs/TECH-DEBT.md) · mapa de testes: [`docs/TESTING.md`](../../../docs/TESTING.md). - **Remodelagem Municípios entregue em código (2026-07-23; deploy pendente — migrações `20260723_200000_remodel_municipalities` + `20260723_202000_reconcile_municipality_remodel` são destrutivas para a vertical campanha).** Decisões pós-reunião 2026-07-23: rename Praça→Município (`municipality`, `/campanha/municipios`); **435** entradas (Camaçari única; Salvador 19 zonas); role `candidate` (visão irrestrita); demandas staff-only; leader lockdown (ferramenta de contatos de apoiadores); dobradinhas (`stateDeputy`); mapa no Início (dashboard). Plano-mestre: [`remodelagem-municipios.md`](../../../docs/plans/remodelagem-municipios.md) (substitui `remodelagem-pracas.md`). @@ -14,7 +15,7 @@ alwaysApply: false - **Polish overview “Votos estimados” (2026-07-23):** `MunicipalityEstimateScenarioProvider` sincroniza Cenário do mapa com overview + células da lista; seletor Cenário visível em qualquer ano (hint quando ≠2026); labels `Média nas Praças filtradas` + `Pessimista · Otimista`; `sem estimativa` sob o hero. Critique: `.impeccable/critique/2026-07-23T21-29-20Z__src-components-campaign-municipalitylistoverview-tsx.md`. - **Cenário no overview (2026-07-23):** seletor relocado para a visão geral; no mapa só com Ano=2026; disclaimer inline → `CampaignInfoHint` (Popover `?`); flash no overview ao trocar. Critique: `.impeccable/critique/2026-07-23T21-52-39Z__src-components-campaign-municipalitymappanel-tsx-cenario.md`. **capture-review-debts:** 0 IDs no roadmap; flash compartilhado adiado no plano A10 (gatilho: 2º flash client-side em `/campanha`). - **Programa WhatsApp interno D3–D5 registrado (2026-07-23):** comunicação 1:1 staff↔lideranças via bridge não oficial (não WABA / não massa); **número pessoal** de cada assessor/CG com pareamento QR no app (parece chat pessoal — pedido produto); fatias D3 multi-sessão → D4 envio pela sessão do ator → D5 inbox da própria sessão→rascunhos; baixa/média prioridade; Consent `campanha-whatsapp-canal`. Plano-mestre: [`whatsapp-interno-campanha.md`](../../../docs/plans/whatsapp-interno-campanha.md). -- **Discovery "Inteligência de campanha" aprovado (2026-07-21):** ciclo literatura→persona→entrevista sintética concluído e colaborado; embasamento canônico em [`docs/research/`](../../../docs/research/) (compêndio ~67 fontes, persona, relatório com kernel Rumelt + playbook de 25 padrões dado→decisão P/T/I/J/K + anti-goals). Programa de produto registrado no roadmap como **E8–E15 + B13 + C12** ([plano-mestre `inteligencia-campanha.md`](../../../docs/plans/inteligencia-campanha.md)) — conta da cadeira, fila de alocação, registro-fundação (trajetória de pledge/sinais/decisões), classificação relativa, escala relativa no mapa, níveis N0–N4, motor de sugestões (humano no loop), camada TI, planejador de giros, backtest. Decisões de produto travadas: % estadual absoluto é anti-métrica; denominadores usam válidos projetados (nunca aptos); sugestão nunca decide sozinha; nível de envolvimento não vaza para fora do staff. +- **Discovery "Inteligência de campanha" aprovado (2026-07-21):** ciclo literatura→persona→entrevista sintética concluído e colaborado; embasamento canônico em [`docs/research/`](../../../docs/research/) (compêndio ~67 fontes, persona, relatório com kernel Rumelt + playbook de 25 padrões dado→decisão P/T/I/J/K + anti-goals). Programa de produto registrado no roadmap como **E8–E16 + B13 + C12** (+ adjacentes A11/E17 desde 2026-07-24; [plano-mestre `inteligencia-campanha.md`](../../../docs/plans/inteligencia-campanha.md)) — conta da cadeira, fila de alocação, registro-fundação (trajetória de pledge/sinais/decisões), classificação relativa, escala relativa no mapa, níveis N0–N4, motor de sugestões (humano no loop), camada TI, planejador de giros, backtest. Decisões de produto travadas: % estadual absoluto é anti-métrica; denominadores usam válidos projetados (nunca aptos); sugestão nunca decide sozinha; nível de envolvimento não vaza para fora do staff. - **B12 entregue em código (2026-07-21; deploy pendente com remodelagem):** `fitToKeys` + `interactiveKeys` no Mapa das Praças (`municipalitiesByIbgeCode`); `canonicalMapKeysKey`; hover stroke-only (fill inalterado); clear síncrono no mouseout; fit ao footprint filtrado sem re-zoom em Ano/Escala. Plano: [`aproximar-mapa-pracas.md`](../../../docs/plans/aproximar-mapa-pracas.md). - **B6 entregue em código (2026-07-21; deploy pendente com remodelagem):** `BahiaMap` setStyle incremental — layer GeoJSON estável entre troca de ano/métrica/escala; `pathByKeyRef` + restyle O(2) no hover/select; `fitBounds` só em `mode`/`highlightKeys`; helpers puros em `bahiaMapStyle.ts` + unit `bahiaMapStyle.unit.spec.ts`. Plano: [`escala-dry-pos-b3.md`](../../../docs/plans/escala-dry-pos-b3.md). Fecha débitos de perf de **B10** e **B11**. - **B8 F1 entregue em código (2026-07-21; deploy pendente com remodelagem):** catálogo estático `municipalityZoneNeighborhoods` (21 Praças-zona SSA/CMS; Salvador = TRE-BA RA 02/2017; Camaçari = locais de votação + curadoria) + fixture `municipality-zone-neighborhoods.official.json` + int `municipalityZoneNeighborhoods.int.spec.ts`; card **Bairros desta Praça** na aba Visão geral de `/campanha/municipios/[slug]` quando `kind === 'zona'` (staff e líder). Plano: [`poligonos-pracas-zona.md`](../../../docs/plans/poligonos-pracas-zona.md). **F2 pendente** (dissolve IBGE → TopoJSON + mapa). diff --git a/AGENTS.md b/AGENTS.md index 0a0673c0..bbc9bbe9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -55,7 +55,7 @@ Backlog consolidado (bloqueadores, site, campanha, white-label): [`docs/roadmap. **Recently resolved (2026-07-21):** **B12** Aproximar mapa ao footprint filtrado + correção hover — `fitToKeys` / `interactiveKeys` em `BahiaMap` (`municipalitiesByIbgeCode`); `canonicalMapKeysKey`; hover stroke-only; clear síncrono no mouseout; fit só quando footprint muda. Plano: `docs/plans/aproximar-mapa-pracas.md`. -**Recently resolved (2026-07-21):** **Discovery "Inteligência de campanha"** — ciclo literatura→persona→entrevista sintética aprovado. Embasamento canônico de produto/domínio em **`docs/research/`** (compêndio de ~67 fontes sobre campanha de DF na Bahia; relatório com kernel, playbook de 25 padrões dado→decisão, anti-goals): agentes devem consultá-lo antes de propor métricas/análises territoriais novas. Programa registrado no roadmap: **E8–E15 + B13 + C12** (plano-mestre `docs/plans/inteligencia-campanha.md`) — conta da cadeira (metas derivadas sobre válidos projetados), fila de alocação, registro-fundação (versions em `votePledge`, sinais tipados, `allocationDecision`), classificação relativa (substitui limiares 35/20/10 p/ DF), escala relativa no mapa (quantis/LQ/rank + símbolo proporcional), níveis N0–N4 staff-only, motor de sugestões com humano no loop, camada TI (salvaguardas MAUP), planejador de giros, backtest pós-eleição. Anti-metas travadas: % estadual absoluto como KPI, contagem bruta de cadastros, cartograma, "caça ao alienado", sugestão automática sem decisão humana. +**Recently resolved (2026-07-21):** **Discovery "Inteligência de campanha"** — ciclo literatura→persona→entrevista sintética aprovado. Embasamento canônico de produto/domínio em **`docs/research/`** (compêndio de ~67 fontes sobre campanha de DF na Bahia; relatório com kernel, playbook de 25 padrões dado→decisão, anti-goals): agentes devem consultá-lo antes de propor métricas/análises territoriais novas. Programa registrado no roadmap: **E8–E16 + B13 + C12** (+ adjacentes A11/E17; sessão real com o CG em 2026-07-23 validou/calibrou o programa — `docs/CUSTOMER.md`; plano-mestre `docs/plans/inteligencia-campanha.md`) — conta da cadeira (metas derivadas sobre válidos projetados), fila de alocação, registro-fundação (versions em `votePledge`, sinais tipados, `allocationDecision`), classificação relativa (substitui limiares 35/20/10 p/ DF), escala relativa no mapa (quantis/LQ/rank + símbolo proporcional), níveis N0–N4 staff-only, motor de sugestões com humano no loop, camada TI (salvaguardas MAUP), planejador de giros, backtest pós-eleição. Anti-metas travadas: % estadual absoluto como KPI, contagem bruta de cadastros, cartograma, "caça ao alienado", sugestão automática sem decisão humana. **Recently resolved (2026-07-23):** **Export CSV de assinaturas e contatos** no admin Payload — `@payloadcms/plugin-import-export@3.82.0` habilitado em `signature` e `contact` (`import: false`, export CSV síncrono); flatten de `contact`/`petition` via `toCSV` em [`src/utilities/signatureExport.ts`](src/utilities/signatureExport.ts) (só assinaturas); `consent` excluído do export de assinaturas; collections `exports`/`imports` com access `isPayloadAdmin`; migration `20260723_025513_add_import_export_plugin`. Plano: [`docs/plans/exportar-csv-assinaturas.md`](docs/plans/exportar-csv-assinaturas.md). diff --git a/docs/plans/conta-da-cadeira.md b/docs/plans/conta-da-cadeira.md index 1fce20f4..b2dc4741 100644 --- a/docs/plans/conta-da-cadeira.md +++ b/docs/plans/conta-da-cadeira.md @@ -1,7 +1,7 @@ # E8 — Conta da cadeira (metas derivadas, potencial por município, cobertura) Status: rascunho -Atualizado em: 2026-07-24 (refs sincronizadas pós-remodelagem Municípios + hardening; **A10 entregue e remodelagem em produção — dependências duras satisfeitas**) +Atualizado em: 2026-07-24 (refs sincronizadas pós-remodelagem Municípios + hardening; **A10 entregue e remodelagem em produção — dependências duras satisfeitas**; meta inicial ancorada na sessão de campo de 2026-07-23; `voteGoals` seedados por E4R) Item do roadmap: [docs/roadmap.md](../roadmap.md) (seção "Inteligência de campanha", E8; plano-mestre [inteligencia-campanha.md](inteligencia-campanha.md); A10 ✓) Impeccable: B — cobertura/metas encaixadas no dashboard, na lista de municípios e no detalhe (sem rota nova) Appetite: ~2 dias eng; migration pequena (global `campaignGoals`) + utilities derivadas + encaixes de UI @@ -21,7 +21,7 @@ Na implementação (`implement-roadmap-item`): craft compacto → critique → p ## Contexto -O relatório de discovery ([§5.1](../research/relatorio-entrevista-persona-campanha.md)) fixou a OMTM da campanha até 16/08: **cobertura da meta por compromissos auditáveis** (Σ pledges ÷ meta, com delta semanal como número da reunião), decomposta por município. Hoje `municipality.voteGoals` (Bom/Regular/Mínimo) e `municipality.expectedVotes` existem (E1/A9), mas: não há meta estadual nem decomposição de cima para baixo; não há noção de potencial derivado (válidos projetados, captura do campo, roll-off); denominadores baseados em `aptos` inflariam municípios de abstenção estrutural (relatório §6.6/I-B); e não existe "campo" definido por ano para share intracampo. Os dados TSE necessários já estão em `electionTally` (válidos/brancos/nulos/comparecimento por cargo×ano×cidade×zona) e `electionCandidateVote`; desde o hardening 2026-07-23, a série de válidos federais T1 + votos Solla por município já existe pré-agregada no artefato commitado `src/lib/bahiaElectionAggregates.ts` — preferir o artefato para válidos projetados; teto do campo, share intracampo e roll-off continuam derivando das collections (envolver em `unstable_cache` com a tag `election-tse`, padrão dos loaders de eleições). +O relatório de discovery ([§5.1](../research/relatorio-entrevista-persona-campanha.md)) fixou a OMTM da campanha até 16/08: **cobertura da meta por compromissos auditáveis** (Σ pledges ÷ meta, com delta semanal como número da reunião), decomposta por município. Hoje `municipality.voteGoals` (Bom/Regular/Mínimo) e `municipality.expectedVotes` existem (E1/A9), mas: não há meta estadual nem decomposição de cima para baixo; não há noção de potencial derivado (válidos projetados, captura do campo, roll-off); denominadores baseados em `aptos` inflariam municípios de abstenção estrutural (relatório §6.6/I-B); e não existe "campo" definido por ano para share intracampo. Com **E4R** ([import-planilha-projecao.md](import-planilha-projecao.md)), `voteGoals`/`priority` chegam seedados da planilha da coordenação (~240 municípios com metas em 3 cenários): a decomposição meta→município nasce **reconciliando** a sugestão derivada com as metas reais da mesa — a meta manual continua vencendo a sugerida, e divergências grandes viram aviso, não sobrescrita. Os dados TSE necessários já estão em `electionTally` (válidos/brancos/nulos/comparecimento por cargo×ano×cidade×zona) e `electionCandidateVote`; desde o hardening 2026-07-23, a série de válidos federais T1 + votos Solla por município já existe pré-agregada no artefato commitado `src/lib/bahiaElectionAggregates.ts` — preferir o artefato para válidos projetados; teto do campo, share intracampo e roll-off continuam derivando das collections (envolver em `unstable_cache` com a tag `election-tse`, padrão dos loaders de eleições). ## Objetivos @@ -41,7 +41,7 @@ O relatório de discovery ([§5.1](../research/relatorio-entrevista-persona-camp ## Questões em aberto -- **Meta estadual inicial?** Opções: QE cheio (~190–200 mil) | faixa da cadeira 2022 (~80–150 mil) + margem. **Recomendação:** faixa da cadeira + margem do coordenador — o QE cheio superestima (maioria das cadeiras sai bem abaixo). _(validar com produto)_ +- **Meta estadual inicial?** **Resolvido em campo (2026-07-23 — [CUSTOMER.md](../CUSTOMER.md)):** piso projetado do coordenador = **150 mil** (2022: 129 mil) — topo da faixa da cadeira. Valor inicial do global `campaignGoals` = 150 mil, editável (margem continua dele); o QE cheio segue rejeitado como default. - **Projeção de válidos: média simples da série ou tendência?** Opções: média 3 eleições | último ano | regressão simples. **Recomendação:** média ponderada (2022 peso 2) — barata, estável, sem cheiro de "previsão estatística" (fora de escopo). ## Abordagem proposta diff --git a/docs/plans/dossie-municipio.md b/docs/plans/dossie-municipio.md new file mode 100644 index 00000000..47a0a8db --- /dev/null +++ b/docs/plans/dossie-municipio.md @@ -0,0 +1,91 @@ +# E16 — Dossiê do município (pré-agenda) + +Status: rascunho (O6 — pedido explícito da sessão de campo de 2026-07-23) +Atualizado em: 2026-07-24 +Item do roadmap: [docs/roadmap.md](../roadmap.md) (Inteligência de campanha, E16; plano-mestre [inteligencia-campanha.md](inteligencia-campanha.md), gap G11) +Impeccable: B — visão nova dentro do detalhe do município existente (tab/rota `dossie` + print), sem sistema novo +Appetite: ~1 dia eng; sem migration na v1 (compõe dados existentes; campo de emendas é fase G11 manual) +Responsável: — + +## Design (Impeccable) + +Âncoras: `PRODUCT.md` (princípios 2 "clarity under pressure" e 6) / `DESIGN.md` (register `product`; Flat desk, print sóbrio) · detalhe do município existente (tabs, cards). + +Na implementação: craft compacto → critique → polish. + +- **Persona / contexto:** CG/assessor a caminho de uma agenda: "chego no território sem dossiê" (O6 — prefeito aliado?, economia, disputa local, emendas aportadas; caso Nazaré das Farinhas). Hoje esses dados existem espalhados em tabs. +- **Job principal:** uma tela (e um print/PDF de 1–2 páginas) com tudo que se lê antes de entrar na cidade. +- **Estratégia de cor:** Restrained; dossiê é leitura, não dashboard — tipografia e hierarquia, zero heat. +- **Anti-goals:** formulário novo para o assessor preencher (anti-goal E1 do relatório: extração sem devolução); duplicar os cards das tabs em vez de compô-los; virar relatório de 10 páginas. + +## Contexto + +A sessão real de 2026-07-23 ([CUSTOMER.md](../CUSTOMER.md)) trouxe O6 como o único pedido explícito de produto: dossiê de território pré-agenda com conjuntura local (prefeito aliado/oposição, disputa local "Jacu × Beija-Flor"), economia e **emendas aportadas** por município. O produto já tem quase tudo: série/captura (`MunicipalityBaselineCard` + artefato), pledges/estimativas (`MunicipalityPledgesPanel`, A10), lideranças (`MunicipalityLeadershipsPanel`), estratégia/conjuntura (`MunicipalityStrategyCard`: `politicalTrend`, `strengths`/`risks`, `dobradinhaNotes`, `nextSteps`, dobradinhas M4), sinais/atualizações (`municipalityUpdate`), demografia IBGE (A8 — `bahiaMunicipalityDemographics.ts`: população, faixas etárias). O que falta é a **composição** em visão única imprimível — e o único dado realmente novo é emendas (gap **G11** do plano-mestre: manual-first; fonte estruturada Câmara/Transparência adiada com gatilho). + +## Objetivos + +- **Tab/rota "Dossiê"** no detalhe do município (staff-only): seções na ordem de leitura de campo — capa (nome, TI, prioridade, nível quando existir), conta local (série 2014/2018/2022, % da própria votação via A11, captura/cobertura quando E8 existir), rede (lideranças ativas + responsável + frescor), conjuntura (`politicalTrend` + forças/riscos + disputa local), dobradinhas (M4 + notas), agenda/planos recentes e sinais recentes (últimos `municipalityUpdate`), perfil (demografia A8), emendas (quando houver dado — v1 mostra a seção só se preenchida). +- **Visão print:** `print:`-friendly (CSS print da mesma rota; sem lib de PDF) — 1–2 páginas A4, cabeçalho com data de geração ("dossiê de " — dado envelhece). +- **Emendas manual-first (G11):** nota staff curta no `MunicipalityStrategyCard` existente se couber em campo atual (`nextSteps`/`dobradinhaNotes`) **ou** campo `budgetNotes` staff-only novo — decidir na implementação com custo de migration à vista (questão em aberto abaixo). +- Access: tudo staff (coordinator/advisor/candidate); leader não acessa (lockdown); campos staff-only seguem a redaction dos view models. + +## Decisões travadas + +- **Dossiê compõe, não duplica.** As seções renderizam os mesmos loaders/componentes das tabs (ou variantes compactas), nunca uma segunda fonte. **Rejeitado:** collection `dossier` ou snapshot persistido (o dossiê é leitura derivada; snapshot ex-ante é papel do `allocationDecision`/C12). +- **Print via CSS, não gerador de PDF.** 1 rota, `@media print`, sem dependência. **Rejeitado:** lib de PDF server-side (peso e manutenção por um ganho nulo — o uso real é imprimir/compartilhar do navegador). +- **Emendas entram manual-first (G11).** Fonte estruturada (Câmara/Portal da Transparência) só com gatilho (E16 em uso + pedido de dado sistemático). **Rejeitado:** import automático de emendas na v1 (pipeline novo por uma seção que começa vazia). +- **i18n e naming:** rota/tab `dossie` (valor de URL em pt, como os demais), `MunicipalityDossier*`, `municipalityDossierData.ts`; labels pt-BR ("Dossiê", "Conjuntura", "Emendas aportadas"). + +## Questões em aberto + +- **Onde mora o dado de emendas na v1?** Opções: reusar campo texto existente (`nextSteps`) com convenção | campo staff-only novo `budgetNotes` (migration pequena). **Recomendação:** campo novo `budgetNotes` na migration de C12 se E16 for aprovado antes dela rodar (mesma janela 2 — zero migration extra); senão, convenção em `nextSteps` até C12. +- **Dossiê mostra nível N0–N4 (E14)?** **Recomendação:** sim quando existir, staff-only — mas nunca no print destinado a circulação além da coordenação (vocabulário duplo). v1: print omite nível por default. + +## Abordagem proposta + +```mermaid +flowchart LR + Data["municipalityDossierData.ts
(compõe loaders existentes)"] + Baseline["baseline/A11
(série, rank, share)"] + Rede["lideranças + frescor"] + Conj["politicalTrend + forças/riscos
+ dobradinhas + emendas (G11)"] + Sinais["municipalityUpdate recentes"] + Perfil["demografia A8"] + View["tab Dossiê + @media print"] + Baseline --> Data + Rede --> Data + Conj --> Data + Sinais --> Data + Perfil --> Data + Data --> View +``` + +Componentes: `src/utilities/municipalityDossierData.ts` (novo — orquestra loaders existentes em uma passada), `src/components/campaign/MunicipalityDossier.tsx` (+ seções compactas reusando cards atuais), entrada na `MunicipalityTabNav` (`dossie`), CSS print na própria rota. + +## Dependências + +- Nenhuma dura — compõe o que existe (baseline, pledges, lideranças, estratégia, updates, A8). Melhora com: **A11** (rank/share), **E8** (captura/cobertura/meta na "conta local"), **C12** (sinais tipados na seção de sinais; migration compartilhada para `budgetNotes`), **E14** (nível), **A6** (dobradinha 2026 real). +- Alimenta: **E13** (o compositor de giros linka o dossiê de cada município do giro — preparação de visita). + +## Não escopo + +- Import estruturado de emendas (G11 adiado com gatilho); dossiê por TI (leitura regional é E17/E12); snapshot histórico de dossiê (C12/E15 cobrem o ex-ante); dossiê para `leader` (lockdown). + +## Rabbit holes + +- **Virar relatório infinito.** 1–2 páginas A4; cada seção tem teto de itens (ex.: 5 sinais recentes, 8 lideranças) com link "ver tudo" na tab correspondente. +- **Economia local virar pesquisa.** v1 usa o que A8 tem (demografia); "economia" além disso é nota de conjuntura manual — não construir base socioeconômica nova. +- **Print pixel-perfect.** CSS print razoável basta; não é material de campanha, é documento de trabalho. + +## Adiado com gatilho + +- **G11 fonte estruturada de emendas** (Câmara/Portal da Transparência → valor por município/ano). Gatilho: E16 em uso real + coordenação pedindo o dado sistemático. +- **Compartilhamento do dossiê via WhatsApp (kit).** Gatilho: pedido de campo; hoje o print resolve. + +## Referências + +- `docs/roadmap.md` (E16) · [plano-mestre](inteligencia-campanha.md) (G11) · [planejador-de-giros.md](planejador-de-giros.md) (E13 — consome) +- `docs/CUSTOMER.md` — O6 (pedido explícito; Nazaré das Farinhas), restrição "perna" (dossiê barateia a preparação da agenda) +- `src/app/(campaign)/campanha/(app)/municipios/[slug]/page.tsx` (tabs), `src/components/campaign/MunicipalityBaselineCard.tsx`, `MunicipalityStrategyCard.tsx`, `MunicipalityLeadershipsPanel.tsx`, `MunicipalityPledgesPanel.tsx` +- `src/lib/bahiaMunicipalityDemographics.ts` (A8), `src/lib/bahiaElectionAggregates.ts`, [ranking-votos-municipio.md](ranking-votos-municipio.md) (A11) +- `PRODUCT.md`/`DESIGN.md` — clareza sob pressão; print sóbrio; AGENTS.md — access, naming diff --git a/docs/plans/fila-de-alocacao.md b/docs/plans/fila-de-alocacao.md index 569087d8..fc424179 100644 --- a/docs/plans/fila-de-alocacao.md +++ b/docs/plans/fila-de-alocacao.md @@ -25,8 +25,8 @@ O relatório definiu a "fila de trabalho" com 7 colunas ranqueadas (FU3): votos ## Objetivos -- Novas ordenações na URL (`?sort=deficit|risco-frescor|votos-em-jogo`) via `campaignListUrl.ts`, preservando filtros existentes. -- Colunas derivadas na linha do município: déficit (meta − comprometido), cobertura %, votos em jogo (válidos projetados), LQ/captura, frescor (dias desde último pledge/sinal/atualização), badge "sem responsável". +- Novas ordenações na URL (`?sort=deficit|risco-frescor|votos-em-jogo`) via `campaignListUrl.ts`, preservando filtros existentes — estende o `?sort=votos` que nasce em **A11** ([ranking-votos-municipio.md](ranking-votos-municipio.md)). +- Colunas derivadas na linha do município: déficit (meta − comprometido), cobertura %, votos em jogo (válidos projetados), **% da própria votação** (rank/share via helper de A11 — a âncora de prioridade da mesa, sessão 2026-07-23), LQ/captura, frescor (dias desde último pledge/sinal/atualização), badge "sem responsável". - Visão do assessor: mesma fila filtrada às seus municípios (access já garante o escopo — `getAccessibleMunicipalityIds`). - Overview da lista ganha "coluna da vergonha": contagem de municípios priorizados sem responsável (link filtrado). - `leader` não vê a fila (colunas staff-only seguem o padrão de redaction dos view models `municipalityViewModels.ts`). @@ -34,6 +34,7 @@ O relatório definiu a "fila de trabalho" com 7 colunas ranqueadas (FU3): votos ## Decisões travadas - **A fila é a própria lista de municípios com ordenações/colunas novas — não uma rota nova.** Evita segundo sistema de listas e mantém filtros/URL/paginação existentes (C6 shells). **Rejeitado:** rota `/campanha/fila` dedicada (duplicaria filtros e navegação; a fila de _sugestões_ do E11 é outra superfície e outro item); tabela full-row editável (anti-goal PRODUCT.md). +- **Default de ordenação para staff = `deficit` quando houver metas** (revisão 2026-07-24). A regra de adoção do discovery é "registro no fluxo de poder — a reunião de recurso começa pela lista"; fila opt-in atrás de um select esconde a inteligência. Sem metas (pré-E8/E4R), default continua nome. **Rejeitado:** fila opt-in permanente (contradiz E1/O-B do relatório); forçar o default também para ordenações de risco (essas seguem opt-in). - **Déficit usa meta efetiva = `voteGoals.regular ?? suggestedGoal`** (mesma regra de E8), e municípios N0/N1 (pós-E14) saem do topo por meta mínima — até E14, municípios sem meta ficam no fim, não no topo. **Rejeitado:** ordenar por déficit bruto sem tratamento (município perdido com meta lixo polui o topo — FU3). - **i18n e naming:** `municipalityQueueSort`, `deficit`, `freshnessDays`, `unassignedPriorityCount`; labels pt-BR ("Déficit", "Frescor", "Sem responsável"). @@ -61,7 +62,7 @@ Componentes: - **`src/utilities/municipalityQueue.ts`**: composição das linhas (déficit, cobertura, frescor, flags) sobre o bundle existente — uma passada, sem N+1; ordenações puras testáveis em unit. - **`MunicipalityList.tsx` / `MunicipalityListOverview.tsx`**: colunas/badges novas; select de ordenação em `MunicipalityFilters.tsx` (mesmo padrão auto-aplicado do fill-in filtros-auto). -- **URL**: `campaignListUrl.ts` ganha `sort`; default continua o atual (nome) até o usuário escolher — a fila é opt-in via select "Ordenar por". +- **URL**: `campaignListUrl.ts` ganha `sort` (nasce em A11 com `votos`); com metas presentes (E8/E4R), o default do staff passa a `deficit` (decisão travada acima); sem metas, default nome. Select "Ordenar por" continua para as demais ordenações. - **Sem migration, sem collection, sem server action nova** (edição em contexto reusa `municipalityStaffFormActions`). ## Dependências @@ -84,7 +85,8 @@ Componentes: ## Referências -- `docs/roadmap.md` (Inteligência de campanha, E9) · [plano-mestre](inteligencia-campanha.md) (fila canônica) +- `docs/roadmap.md` (Inteligência de campanha, E9) · [plano-mestre](inteligencia-campanha.md) (fila canônica) · [ranking-votos-municipio.md](ranking-votos-municipio.md) (A11 — helper de rank/share e `?sort=votos`) - `docs/research/relatorio-entrevista-persona-campanha.md` FU3 (colunas e ordenações), §6.2 (triagem), D3 ("a alocação se decide na lista") +- `docs/CUSTOMER.md` — "Salvador cobrado 10×" (coluna da vergonha), âncora % da própria votação (sessão 2026-07-23) - `src/utilities/municipalityPageData.ts`, `src/utilities/votePledgeData.ts`, `src/components/campaign/MunicipalityList.tsx`, `src/components/campaign/MunicipalityListOverview.tsx`, `src/components/campaign/MunicipalityFilters.tsx`, `src/utilities/campaignListUrl.ts` - AGENTS.md — access por papel, view models com redaction, URLs com chaves em inglês diff --git a/docs/plans/import-planilha-projecao.md b/docs/plans/import-planilha-projecao.md new file mode 100644 index 00000000..9833e972 --- /dev/null +++ b/docs/plans/import-planilha-projecao.md @@ -0,0 +1,97 @@ +# E4R — Import único da planilha de projeção (seed de estratégia, local e produção) + +Status: rascunho (aprovado como item em 2026-07-24 — reabre o corte E4 por decisão de produto) +Atualizado em: 2026-07-24 +Item do roadmap: [docs/roadmap.md](../roadmap.md) (Demais itens abertos, E4R; Onda 0 §4 — seed do onboarding) +Impeccable: n/a — script/CLI sem superfície de UI (relatório em stdout) +Appetite: ~1 dia eng (parser + mapeamento + dry-run + runbook de produção) +Responsável: — + +## Contexto + +A sessão real com o Coordenador Geral (2026-07-23 — [CUSTOMER.md](../CUSTOMER.md)) confirmou O5 com força máxima: a planilha de projeção é a fonte de verdade da mesa ("eu acordo todos os dias lendo o nosso mapa") e ele se comprometeu a enviá-la — as versões mais recentes já estão versionadas em `docs/sheets/`. O corte E4 (2026-07-19, [mapa-projecao-municipios.md](mapa-projecao-municipios.md): "dados estratégicos via UI, sem script") foi decidido **antes** dessa evidência; preencher ~240 municípios com metas à mão na semana de onboarding não acontece, e o Big Hire depende de "seeded picture" (CUSTOMER.md). Decisão de produto de 2026-07-24: **seed único e assistido via script**; import automático/recorrente continua fora de escopo (atualização contínua segue via UI — o produto é o registro vivo, não espelho de planilha). + +### Forense dos arquivos (2026-07-24) + +Mesma linhagem (criados 2026-05-05, ambos modificados 2026-07-14 por "joao vitor pitanga"); **se complementam**: + +| Arquivo | Abas | Papel | +| ---------------------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `Mapa projeção de votos Solla 2026.xlsx` | MAPA GERAL · PRIORITÁRIAS · RESUMO (salvo 15:11) | **Canônico para estratégia** — o MAPA GERAL é mais fresco nas 9 linhas divergentes (ex.: Jaguaquara "Bom: 10.000", que bate com o "investiu pesado em Jaguaquara ~10k" da entrevista) | +| `Mapa_projecao_votos_Solla_2026.xlsx` | + SALVADOR (bairros) · SALVADOR (ZONAS) · DE-PARA (2014-2022) · LISTAS (salvo 14:52) | **Referência de Salvador** — zonas só têm votos históricos (sem estratégia; ZE 20ª extinta anotada); DE-PARA reconcilia rótulos TSE→bairro. PRIORITÁRIAS e RESUMO são idênticas nos dois arquivos | + +Divergências A×B no MAPA GERAL (9 linhas: Brejões, Catu, Iaçu, Ipiaú, Jaguaquara, Manoel Vitorino, Santa Cruz Cabrália, Tanquinho, Contendas do Sincorá) — o dry-run imprime as duas versões; importa-se a do arquivo A. + +## Objetivos + +- Script `scripts/import-projecao.mjs` que lê o xlsx canônico e escreve nos `municipality` do catálogo: `voteGoals.good/regular/minimum` (coluna EXPECTATIVA 2026) e `priority` (coluna PRIORIDADE) — **v1 só números/enums, zero PII**. +- Parser tolerante aos 3 formatos observados de EXPECTATIVA: `Bom: 350 | Regular: 250 | Minimo: 150` (com/sem espaços), `Bom:350`, `800/500/400`; separador de milhar `10.000`. +- Matching de nome via `canonicalizeMunicipalityName` + catálogo (`municipalityCatalog.ts`); sanity check duplo: VOTOS 2014/18/22 da planilha × `bahiaElectionAggregates` (valida o match), REGIÃO × `bahiaTerritories` (diverge = warning, não bloqueia). +- `--dry-run` com relatório completo: linhas casadas/não casadas, campos que seriam escritos, 9 divergências A×B, Salvador pulado, totais. +- Idempotente: por default escreve **só campos vazios** (`voteGoals` sem valor, `priority` ausente); `--overwrite` para re-runs conscientes. +- Runbook de produção documentado no próprio script (header) e neste plano. + +## Decisões travadas + +- **v1 importa apenas números/enums (`voteGoals`, `priority`).** Colunas com nomes (LIDERANÇAS, ASSESSOR RESPONSÁVEL, DOBRADINHAS, ENCAMINHAMENTOS, OBSERVAÇÃO) ficam para fase 2, pós-lote jurídico — nome de ator político é dado pessoal que revela opinião política (LGPD art. 11); destino (CRM `leadership` × nota staff-only) se decide com a assessoria. **Rejeitado:** importar tudo com access staff-only (antecipa exatamente o risco que a Onda 0 segura); campo novo `networkNotes` (schema por dado que ainda não pode entrar). +- **SITUAÇÃO e VOTOS não são importados.** Tendência é derivada do TSE (`computeVoteTrend` — decisão E2 mantida); votos históricos vêm de `electionTally`/artefato. A planilha só manda no que é julgamento humano: metas e prioridade. **Rejeitado:** persistir a SITUAÇÃO da planilha (duplicaria a derivação com dado defasado). +- **Salvador (linha única, metas 30k/27k/25k) é pulado com relatório explícito.** O catálogo tem 19 Municípios-zona e a planilha não distribui; inventar rateio viola "não estimar o que não tem dado" (falsa precisão). Metas de zona entram via UI pelo coordenador. **Rejeitado:** rateio proporcional ao voto por zona (número inventado com cara de número). +- **Arquivo A é o canônico de estratégia; B é referência.** Mais recente nos 9 conflitos e corroborado pela entrevista (Jaguaquara). O script aceita `--file` para apontar planilha nova se chegar versão mais atual. +- **`priority`: `alta` → `alta`; `Baixa`/vazio → `normal`.** Cross-check com a aba PRIORITÁRIAS (conjunto alfabético de 50, não ranking): prioritária sem `alta` no MAPA GERAL = warning no relatório. +- **Escrita via Payload Local API em transação única** (`withPayloadTransaction`-equivalente de CLI), `overrideAccess: true` (processo CLI, mesmo padrão de `seed-tse-results.mjs`); guarda de banco da família dos seeds (`assertLocalDatabase`; produção só com `ALLOW_REMOTE_DB=true`). +- **i18n e naming:** identificadores em inglês (`importProjectionSheet`, `parseExpectationCell`, `--dry-run`/`--overwrite`/`--file`); relatório em pt-BR. + +## Questões em aberto + +- **Parser xlsx: dependência ou zip+XML mínimo?** Opções: devDependency SheetJS CE (`xlsx`, Apache-2.0) | parser próprio (zipfile + `fast-xml-parser`/DOM, ~80 linhas para o subset usado). **Recomendação:** SheetJS CE como devDependency — formato consolidado, menos código nosso; remover se incomodar no knip (é usado só pelo script). +- **Atualizar `lastUpdateAt` dos municípios seedados?** **Recomendação:** não — seed não é sinal de campo; frescor (E9/G8) não pode nascer inflado. + +## Abordagem proposta + +```mermaid +flowchart LR + XLSX["docs/sheets/*.xlsx
(A canônico, B referência)"] + Parse["parseExpectationCell
(3 formatos + milhar)"] + Match["canonicalizeMunicipalityName
+ municipalityCatalog"] + Sanity["sanity: votos × artefato
REGIÃO × bahiaTerritories"] + Write["Payload Local API
(voteGoals, priority; só vazios)"] + Report["relatório stdout
(dry-run = só relatório)"] + XLSX --> Parse --> Match --> Sanity --> Write --> Report + Sanity -.warnings.-> Report +``` + +### Runbook (ordem obrigatória) + +1. Local: `pnpm db:start` → `node scripts/import-projecao.mjs --dry-run` → revisar relatório → rodar sem `--dry-run` → conferir 3 municípios na UI (`/campanha/municipios`). +2. Produção (após smoke pós-deploy): `ALLOW_REMOTE_DB=true DATABASE_URL= node scripts/import-projecao.mjs --dry-run` → revisar → aplicar. Sem revalidate (páginas de campanha são dinâmicas). +3. Registrar no notebook a data do seed e o arquivo usado (proveniência). + +## Dependências + +- Nenhuma dura (schema `municipality` em produção desde 2026-07-23; `voteGoals`/`priority` existem desde E1). Consome: `municipalityCatalog.ts`, `bahiaElectionAggregates.ts`, `bahiaTerritories.ts`, guard family dos seeds. +- Alimenta: **E8** (metas iniciais da decomposição), **A11/E17** (quadro com prioridade real), onboarding (Onda 0 §4). + +## Não escopo + +- Import recorrente/automático (fora de escopo do roadmap — atualização contínua via UI); colunas com nomes (fase 2 pós-lote jurídico); Salvador por zona/bairro (UI manual; drill de bairro segue como E5 futuro); criação de `leadership`/`Contact` a partir da planilha (CRM real só com Consent). + +## Rabbit holes + +- **"Já que estamos, importar as lideranças como texto."** É exatamente o dado segurado pela Onda 0; qualquer coluna textual espera o parecer. Mitigação: v1 whitelist de 2 campos, hard-coded. +- **Normalizar a planilha inteira (RESUMO, LISTAS, DE-PARA).** Só MAPA GERAL (+ PRIORITÁRIAS para cross-check) tem dado importável; o resto é referência humana. +- **Resolver os 9 conflitos A×B "automaticamente com merge inteligente".** A é canônico; o relatório imprime os dois valores para o coordenador conferir depois. Merge por célula é engenharia para um problema de 9 linhas. + +## Adiado com gatilho + +- **Fase 2 (colunas com nomes).** Gatilho: lote jurídico final + decisão de destino com a assessoria. +- **Metas de Salvador por zona via planilha.** Gatilho: coordenação produzir metas por ZE (hoje não existem em nenhuma aba). +- **`--file` apontando planilha atualizada.** Gatilho: coordenador enviar versão mais nova que as de `docs/sheets/` (o script já nasce parametrizado; o gatilho é só o re-run consciente com `--overwrite`). + +## Referências + +- `docs/roadmap.md` (E4R; Onda 0 §4; Fora de escopo — import recorrente) · [mapa-projecao-municipios.md](mapa-projecao-municipios.md) (E1–E5; corte E4 original) +- `docs/CUSTOMER.md` — O5 confirmada ✚✚, compromisso da tabela, Big Hire = seeded picture +- `docs/sheets/Mapa projeção de votos Solla 2026.xlsx` (canônico estratégia) · `docs/sheets/Mapa_projecao_votos_Solla_2026.xlsx` (referência Salvador) +- `scripts/seed-tse-results.mjs` (padrão CLI: guard de banco, transação, idempotência por escopo) · `scripts/guard-dev-db.mjs` +- `src/lib/municipalityCatalog.ts`, `src/lib/bahiaElectionAggregates.ts`, `src/lib/bahiaTerritories.ts`, `src/collections/Municipality.ts` +- AGENTS.md — guarda de banco local/produção, `ALLOW_REMOTE_DB`, naming diff --git a/docs/plans/inteligencia-campanha.md b/docs/plans/inteligencia-campanha.md index 289b80f7..b5e5d1be 100644 --- a/docs/plans/inteligencia-campanha.md +++ b/docs/plans/inteligencia-campanha.md @@ -1,9 +1,9 @@ -# Inteligência de campanha — do relatório de discovery ao produto (programa E8–E15 + B13 + C12) +# Inteligência de campanha — do relatório de discovery ao produto (programa E8–E16 + B13 + C12; adjacentes A11/E17) Status: rascunho (plano-mestre; itens fatiados individualmente) -Atualizado em: 2026-07-24 (referências sincronizadas pós-remodelagem Municípios + hardening; remodelagem em produção desde 2026-07-23) +Atualizado em: 2026-07-24 (referências sincronizadas pós-remodelagem Municípios + hardening; remodelagem em produção desde 2026-07-23; validação de campo 2026-07-23 incorporada — G11, E16, âncora % da própria votação) Item do roadmap: [docs/roadmap.md](../roadmap.md) (seção "Inteligência de campanha"; trilhas E/B/C) -Impeccable: misto por item — E15 = A; E8/E9/C12/E10/B13/E14/E12 = B; E11/E13 = C (classe final no plano de cada fatia) +Impeccable: misto por item — E15 = A; E8/E9/C12/E10/B13/E14/E12/E16 = B; E11/E13 = C (classe final no plano de cada fatia) Appetite: programa ~2–3 semanas eng no total, fatiado em slices de ~1–3 dias Responsável: — @@ -13,6 +13,8 @@ Fonte de produto: [`docs/research/relatorio-entrevista-persona-campanha.md`](../ O discovery (literatura → persona → entrevista sintética, 2026-07-21) fixou o diagnóstico: a disputa de DF na Bahia é uma conta de quociente fragmentada (cadeira ~80–150 mil votos; topo estadual 4,71% dos válidos), o gargalo é converter lealdade de campo em voto nominal município a município via rede de mediação, e a informação dessa rede hoje é oral e sem série. O Teqo já tem o **sistema de registro** (Municípios, lideranças, pledges, planos, demandas, baseline TSE 2014–2022 com brancos/nulos/abstenção por cargo). O que falta é a **camada de inteligência**: metas derivadas dos dados, leitura relativa do território, fila de decisão priorizada, sugestões dado→decisão com humano no loop, e o registro ex-ante que torna tudo auditável (backtest). Anti-goal do programa inteiro: "planilha chique" — KPI absoluto, cadastro bruto, mapa 0–100%. +**Validação de campo (2026-07-23 — [CUSTOMER.md](../CUSTOMER.md)):** a sessão real com o Coordenador Geral confirmou as apostas centrais da persona (ameaça nº 1 = fogo amigo intra-PT, nomeada com caso e número; canal = ZAP/ligação sem registro datado — Cairu −300 "ontem" só na memória/planilha; agenda = A decisão semanal; nenhuma métrica calculada além de voto) e **derrubou/calibrou duas**: a restrição dominante é **"perna"/estrutura**, não dinheiro (reprioriza E13 e o dossiê E16 na fila de corte), e a leitura relativa da mesa é **% da própria votação** (concentração da captura própria — ele manteve o critério mesmo confrontado com o % do eleitorado local), âncora a expor em E8/E9/E10/B13 ao lado de LQ/captura e entregue já em **A11** ([ranking-votos-municipio.md](ranking-votos-municipio.md)). O7 (registro datado de ganhos/perdas) confirma **C12 como não cortável** por evidência de campo; "Salvador cobrado 10×" valida a coluna da vergonha de **E9**; o piso projetado de **150 mil** (2022: 129 mil) resolve a meta inicial de **E8**; o pedido explícito de dossiê pré-agenda com emendas (Nazaré das Farinhas) vira **E16** + gap **G11**. + ## Gaps de estrutura de dados (o que não estamos preparados para coletar/usar) Auditoria relatório × schema atual (2026-07-21). O que **já existe e cobre**: `electionTally` tem `aptos/comparecimento/abstencoes/votosValidos/votosBranco/votosNulo` por cargo×ano×cidade×zona (roll-off e válidos projetados são deriváveis); `electionCandidate` tem `party`/`coalition`/`runningAgain2026` (P3/herança aberta); `municipality` tem `voteGoals` (bom/regular/mínimo), `expectedVotes`, `priority`, `politicalTrend`, `dobradinhaNotes`, `advisors`, `lastUpdateAt`; `votePledge` tem declarado×estimado com autores/datas; `municipalityUpdate` é imutável; `bahiaTerritories.ts` já mapeia município→Território de Identidade; `actionPlan` registra eventos com município/status/presença do deputado. Desde o hardening 2026-07-23 existe também o artefato commitado `src/lib/electionAggregates/bahia-federal-baseline.json` (loader `bahiaElectionAggregates.ts`): série Solla + válidos federais 2014/2018/2022 por município, já cortada na geografia do catálogo — insumo direto para válidos projetados/captura sem re-query de `electionTally`. @@ -29,6 +31,7 @@ Auditoria relatório × schema atual (2026-07-21). O que **já existe e cobre**: | G8 | **Frescor como sinal de 1ª classe** | derivável (`declaredAt`, `lastUpdateAt`) mas não exposto/normatizado | Utilities de frescor + exposição na fila/lista ("coluna da vergonha": município priorizado sem responsável/sem sinal) — sem migration | E9 | | G9 | Perfil de abstenção por município (sexo/idade/escolaridade, TSE Eleitorado) | não importado | **Adiado com gatilho** (só se I-C virar prática): extensão do seed TSE | — | | G10 | Malha Regiões Imediatas IBGE (teste de sensibilidade MAUP) | só TI (`bahiaTerritories.ts`) | **Adiado com gatilho** (decisão cara sustentada por análise regional divergente): variante do `build:geometries` | — | +| G11 | **Emendas parlamentares por município** (dossiê pré-agenda — O6 de campo: "emendas aportadas", caso Nazaré das Farinhas) | não coletado | **Manual-first**: valor/nota staff no dossiê (E16); fonte estruturada (Câmara/Portal da Transparência) **adiada com gatilho** (E16 em uso + pedido de dado sistemático) | E16 | Migrations do programa: uma consolidada por fatia que toca schema (C12: versions+kinds+origin+`allocationDecision`; E8: global `campaignGoals`; E14: `engagementLevel`). Todas antes do congelamento (~20/09). @@ -46,18 +49,19 @@ Regras transversais (de §6.2/§6.4, viram comportamento do produto): **barato-a ## Fatias (funcionalidades faltantes) -| ID | Fatia (plano) | Entrega | OST | Classe | Appetite | Janela | -| --- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | ------ | -------- | --------- | -| E8 | **[Conta da cadeira](conta-da-cadeira.md)** | Global `campaignGoals`; potencial por município (válidos projetados, captura do campo, share intracampo via `campoParties.ts`, roll-off, headroom); decomposição meta estadual→município com sanity por TI; cobertura Σ(estimativa do cenário — default média via **A10**)÷meta no dashboard/lista; denominadores sempre válidos projetados (I-B) | O-A | B | ~2d | 2 | -| E9 | **[Fila de alocação](fila-de-alocacao.md)** | Lista de decisão com as 7 colunas (§5.2 do relatório: votos em jogo, meta/cobertura/delta, classe, LQ/captura, rede+frescor, competição, tendência); ordenação default por déficit descoberto; ordenação de risco (defesa por frescor); "coluna da vergonha" | O-B, O-F | B | ~1,5d | 2 | -| C12 | **[Registro-fundação](registro-fundacao.md)** | Versions em `votePledge`; sinais tipados no `municipalityUpdate`; `actionPlan.origin`+`resourceCost`; collection `allocationDecision`; garantia "nunca sobrescrever" | O-E, O-G(base) | B | ~2d | 2 | -| E10 | **[Classificação territorial relativa](classificacao-territorial-relativa.md)** | Recalibra defesa/ataque/indecisa/perdida com âncoras relativas (LQ vs. share estadual próprio; múltiplos do share da cadeira marginal), multi-eixo (dominância+importância+competição+teto do campo); substitui limiares 35/20/10 de `electionInsights.ts` para DF | O-D | B | ~1d | 3 | -| B13 | **[Escala relativa no mapa](escala-relativa-mapa.md)** | Modo de escala novo: quantis do próprio candidato (5 classes) default, LQ e rank no município; símbolo proporcional por votos em jogo sobre o polígono; % dos válidos mantido como opção; roll-off como métrica adicional | O-C | B | ~2d | 3 | -| E14 | **[Níveis de envolvimento N0–N4](niveis-de-envolvimento.md)** | `engagementLevel` com critérios, promoção/rebaixamento com histerese e sinais de reversão (via `allocationDecision`); vocabulário duplo (nível nunca exposto fora do staff — access) | O-K | B | ~1d | 3 | -| E11 | **[Motor de sugestões v1](motor-de-sugestoes.md)** | Computa subconjunto dos padrões (P1, P2, P3, P5, P6, P7, K-A/K-B; demais por fase 2) por município; sugestão com menu/estatuto/leitura-alternativa; aceitar/descartar grava `allocationDecision`; triagem 1–5 + histerese; "pauta do silêncio" mensal | O-H | C | ~3d | 3 | -| E12 | **[Camada TI](camada-territorios-identidade.md)** | Rollups por Território de Identidade com salvaguardas MAUP (razão de agregados, mediana+amplitude+município crítico, Metropolitano decomposto); benchmark intra-TI (T4); gatilhos T1–T3/T5 no motor = fase 2 de E11 | O-I | B | ~1,5d | 3 | -| E13 | **[Planejador de presença/giros](planejador-de-giros.md)** | Elegibilidade (5 condições ✓/—), fase do calendário (construção/consolidação/ativação), compositor de giro (J-C âncora+satélites+semente), visita pedida×justificada (J-B, usa `actionPlan.origin`) | O-J | C | ~1,5d | 3 | -| E15 | **[Backtest pós-eleição](backtest-pos-eleicao.md)** | Pledge (trajetória) vs. voto realizado por zona; calibração de limiares E10/E14; relatório de aprendizado | O-G | A | ~1d | pós-04/10 | +| ID | Fatia (plano) | Entrega | OST | Classe | Appetite | Janela | +| --- | ------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | ------ | -------- | --------- | +| E8 | **[Conta da cadeira](conta-da-cadeira.md)** | Global `campaignGoals`; potencial por município (válidos projetados, captura do campo, share intracampo via `campoParties.ts`, roll-off, headroom); decomposição meta estadual→município com sanity por TI; cobertura Σ(estimativa do cenário — default média via **A10**)÷meta no dashboard/lista; denominadores sempre válidos projetados (I-B) | O-A | B | ~2d | 2 | +| E9 | **[Fila de alocação](fila-de-alocacao.md)** | Lista de decisão com as 7 colunas (§5.2 do relatório: votos em jogo, meta/cobertura/delta, classe, LQ/captura, rede+frescor, competição, tendência) + **% da própria votação** (âncora da mesa, via A11); ordenação default por déficit descoberto (default do staff quando houver metas); ordenação de risco (defesa por frescor); "coluna da vergonha" | O-B, O-F | B | ~1,5d | 2 | +| C12 | **[Registro-fundação](registro-fundacao.md)** | Versions em `votePledge`; sinais tipados no `municipalityUpdate`; `actionPlan.origin`+`resourceCost`; collection `allocationDecision`; garantia "nunca sobrescrever" | O-E, O-G(base) | B | ~2d | 2 | +| E16 | **[Dossiê do município](dossie-municipio.md)** | Dossiê pré-visita/print compondo série+captura, pledges/cobertura, lideranças, conjuntura (`politicalTrend`), forças/riscos, dobradinhas, sinais recentes e perfil A8; emendas quando houver dado (G11) — pedido explícito de campo (O6 do CUSTOMER.md) | O6 (campo) | B | ~1d | 2 | +| E10 | **[Classificação territorial relativa](classificacao-territorial-relativa.md)** | Recalibra defesa/ataque/indecisa/perdida com âncoras relativas (LQ vs. share estadual próprio; múltiplos do share da cadeira marginal), multi-eixo (dominância+importância+competição+teto do campo); substitui limiares 35/20/10 de `electionInsights.ts` para DF | O-D | B | ~1d | 3 | +| B13 | **[Escala relativa no mapa](escala-relativa-mapa.md)** | Modo de escala novo: quantis do próprio candidato (5 classes) default, LQ e rank no município; símbolo proporcional por votos em jogo sobre o polígono; % dos válidos mantido como opção; roll-off como métrica adicional | O-C | B | ~2d | 3 | +| E14 | **[Níveis de envolvimento N0–N4](niveis-de-envolvimento.md)** | `engagementLevel` com critérios, promoção/rebaixamento com histerese e sinais de reversão (via `allocationDecision`); vocabulário duplo (nível nunca exposto fora do staff — access) | O-K | B | ~1d | 3 | +| E11 | **[Motor de sugestões v1](motor-de-sugestoes.md)** | Computa subconjunto dos padrões (P1, P2, P3, P5, P6, P7, K-A/K-B; demais por fase 2) por município; sugestão com menu/estatuto/leitura-alternativa; aceitar/descartar grava `allocationDecision`; triagem 1–5 + histerese; "pauta do silêncio" mensal | O-H | C | ~3d | 3 | +| E12 | **[Camada TI](camada-territorios-identidade.md)** | Rollups por Território de Identidade com salvaguardas MAUP (razão de agregados, mediana+amplitude+município crítico, Metropolitano decomposto); benchmark intra-TI (T4); gatilhos T1–T3/T5 no motor = fase 2 de E11; **estende a tabela TI do Início (E17 — [tabela-ti-inicio.md](tabela-ti-inicio.md), primeira fatia já sem E8)** | O-I | B | ~1,5d | 3 | +| E13 | **[Planejador de presença/giros](planejador-de-giros.md)** | Elegibilidade (5 condições ✓/—), fase do calendário (construção/consolidação/ativação), compositor de giro (J-C âncora+satélites+semente), visita pedida×justificada (J-B, usa `actionPlan.origin`) | O-J | C | ~1,5d | 3 | +| E15 | **[Backtest pós-eleição](backtest-pos-eleicao.md)** | Pledge (trajetória) vs. voto realizado por zona; calibração de limiares E10/E14; relatório de aprendizado | O-G | A | ~1d | pós-04/10 | ## Decisões travadas @@ -71,7 +75,7 @@ Regras transversais (de §6.2/§6.4, viram comportamento do produto): **barato-a ## Questões em aberto - **Quais padrões entram no motor v1?** Opções: todos os 25 | subconjunto de maior confiança. **Recomendação:** os 8 listados em E11 (dados 100% disponíveis pós-E8/C12, menor risco de falso positivo); T/I/J na fase 2 após 2 semanas de uso real. -- **Meta estadual inicial?** Opções: QE cheio | faixa da cadeira 2022 (~80–150 mil) com margem. **Recomendação:** faixa da cadeira + margem definida pelo coordenador no global `campaignGoals` _(validar com produto na sessão Wed)_. +- **Meta estadual inicial?** **Resolvido em campo (2026-07-23):** piso projetado do coordenador = **150 mil** (2022: 129 mil) — topo da faixa da cadeira. Valor inicial do global `campaignGoals` = 150 mil, editável pelo coordenador (margem continua dele). - **Limiar de broker único (P4)?** **Recomendação:** iniciar em 50% dos pledges do município / top-3 ≥ 40% do total, marcado calibrável; revisar no primeiro mês. ## Abordagem proposta (visão de programa) @@ -88,6 +92,10 @@ flowchart TD E12["E12 Camada TI"] E13["E13 Planejador de giros"] E15["E15 Backtest pós-eleição"] + E16["E16 Dossiê do município"] + E8 -.derivados.-> E16 + C12 -.sinais.-> E16 + E16 -.dossiê pré-visita.-> E13 E8 --> E9 --> E11 C12 --> E11 C12 --> E14 diff --git a/docs/plans/mapa-projecao-municipios.md b/docs/plans/mapa-projecao-municipios.md index 16a91c3f..cfbd3cbb 100644 --- a/docs/plans/mapa-projecao-municipios.md +++ b/docs/plans/mapa-projecao-municipios.md @@ -1,6 +1,6 @@ # 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 — preenchimento manual via E1+E3); E5 pendente +Status: E1, E2 e E3 implementados e mesclados em `main` (2026-07-19); **E4 cortado** (2026-07-19 — preenchimento manual via E1+E3) e **reaberto como E4R em 2026-07-24** (decisão de produto pós-sessão de campo, evidência O5 — seed único via script: [import-planilha-projecao.md](import-planilha-projecao.md)); E5 pendente Atualizado em: 2026-07-19 (revisão entrega E1+E3 — marcar como entregues no roadmap; access E1/E3 sem `canSetDerivedNucleusField`; escrita via `canUpdateElectoralNucleus` + selects/VMs; **E4 cortado** — decisão de produto: sem `pnpm db:seed:mapa`, dados via UI manual) Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha E — itens E1–E5, "Ciclo 3") Responsável: — diff --git a/docs/plans/onda-0.md b/docs/plans/onda-0.md index 567244f7..75f78c0f 100644 --- a/docs/plans/onda-0.md +++ b/docs/plans/onda-0.md @@ -85,8 +85,9 @@ curl -X POST "https:///api/revalidate?tag=global_privacy-policy" \ ## Onboarding staff (sem PII de campo) -- Criar `campaignUser` `geral`/`coordenador` via `/admin`. +- Criar `campaignUser` `coordinator`/`advisor` (e `candidate`) via `/admin`. - Estruturar municípios de teste, agenda, baseline TSE. +- **Seed da planilha de prioridades via E4R** ([import-planilha-projecao.md](import-planilha-projecao.md)) — só estratégia sem PII (`voteGoals`/`priority`); colunas com nomes (lideranças/assessores/dobradinhas) ficam para depois do lote jurídico. - Treinamento de campo com titulares reais **após** revisão jurídica. ## RIPD diff --git a/docs/plans/planejador-de-giros.md b/docs/plans/planejador-de-giros.md index a65e5740..4f827954 100644 --- a/docs/plans/planejador-de-giros.md +++ b/docs/plans/planejador-de-giros.md @@ -23,7 +23,7 @@ Brief compacto: ## Contexto -Relatório §6.7: visita tem efeito modesto e o canal é mobilização do núcleo — "a visita vale o que a rede local converte dela; agenda é multiplicador de estrutura". Elegibilidade = 5 condições (volume, headroom, rede de recepção, janela política, encaixe em giro); o calendário muda o produto (construção jul–ago / consolidação set / ativação última semana); há "não vá" explícitos e intermediários ("mande o coordenador/vídeo/dobradinha"); padrões J-A (município elegível maduro sem visita), J-B (pedida vs. justificada — `actionPlan.origin` de C12), J-C (composição do giro: âncora+satélites+semente, por contiguidade de TI). O `/campanha/planos` (C3) já tem eventos com município/advisors/status; falta a camada de decisão de agenda. +Relatório §6.7: visita tem efeito modesto e o canal é mobilização do núcleo — "a visita vale o que a rede local converte dela; agenda é multiplicador de estrutura". Elegibilidade = 5 condições (volume, headroom, rede de recepção, janela política, encaixe em giro); o calendário muda o produto (construção jul–ago / consolidação set / ativação última semana); há "não vá" explícitos e intermediários ("mande o coordenador/vídeo/dobradinha"); padrões J-A (município elegível maduro sem visita), J-B (pedida vs. justificada — `actionPlan.origin` de C12), J-C (composição do giro: âncora+satélites+semente, por contiguidade de TI). O `/campanha/planos` (C3) já tem eventos com município/advisors/status; falta a camada de decisão de agenda. A sessão de campo de 2026-07-23 reforçou o item por dois lados: a restrição dominante nomeada é **"perna"/agenda** ("não posso marcar um compromisso que eu não possa cumprir" — o roadmap rebaixou E13 na fila de corte), e o pedido O6 de **dossiê pré-agenda** virou **E16** ([dossie-municipio.md](dossie-municipio.md)) — o compositor de giro linka o dossiê de cada município do giro como preparação da visita. ## Objetivos @@ -69,7 +69,7 @@ Componentes: ## Dependências -- Duras: **E8** (volume/headroom), **C12** (`origin`; `allocationDecision` para overrides). Suaves: **E12** (contiguidade/agrupamento TI — usa `bahiaTerritories.ts` direto se E12 não estiver pronto), **A6** dobradinha (janela política ganha dado real pós-15/08), E14 (níveis alimentam o "não vá"). +- Duras: **E8** (volume/headroom), **C12** (`origin`; `allocationDecision` para overrides). Suaves: **E16** dossiê ([dossie-municipio.md](dossie-municipio.md) — preparação da visita, link por município do giro), **E12** (contiguidade/agrupamento TI — usa `bahiaTerritories.ts` direto se E12 não estiver pronto), **A6** dobradinha (janela política ganha dado real pós-15/08), E14 (níveis alimentam o "não vá"). - Reusa: `/campanha/planos` inteiro (C3), `actionPlan` actions, `bahiaTerritories.ts`. ## Não escopo diff --git a/docs/plans/ranking-votos-municipio.md b/docs/plans/ranking-votos-municipio.md new file mode 100644 index 00000000..635cbb21 --- /dev/null +++ b/docs/plans/ranking-votos-municipio.md @@ -0,0 +1,83 @@ +# A11 — Posição em votos do município (rank + % da própria votação) e ordenação por votação + +Status: rascunho +Atualizado em: 2026-07-24 +Item do roadmap: [docs/roadmap.md](../roadmap.md) (Demais itens abertos, A11; E9 absorve como coluna/ordenação da fila) +Impeccable: B — encaixes em superfícies existentes (detalhe do município + lista), sem rota nova +Appetite: ~0,5–1 dia eng; sem migration, sem collection, sem server action nova +Responsável: — + +## Design (Impeccable) + +Âncoras: `PRODUCT.md` (princípio 6 — métricas relativas e locais) / `DESIGN.md` (register `product`) · `MunicipalityBaselineCard` e `MunicipalityList` existentes. + +Na implementação: craft compacto → critique → polish. + +- **Persona / contexto:** a mesa prioriza por **% do voto do próprio deputado** (sessão 2026-07-23 — o coordenador manteve o critério mesmo desafiado com o % do eleitorado local). Hoje essa lente não existe no produto: nem rank, nem share, nem ordenação por votação. +- **Job principal:** abrir um município e saber na hora "que fatia da nossa votação ele é e em que posição está"; abrir a lista e vê-la na ordem da mesa. +- **Estratégia de cor:** Restrained — rank/share são texto/badge neutros, sem heat. +- **Anti-goals:** ranking gamificado; % do eleitorado local como âncora (é a lente que a mesa rejeitou); KPI estadual absoluto. + +## Contexto + +A leitura "posição em votos absolutos + % da própria votação" é a **concentração da própria votação** do compêndio (§8.1 — Carvalho 2003; "de onde vem meu voto?"), e é exatamente o critério rígido de prioridade da coordenação (CUSTOMER.md, surpresa da sessão: prioridade por % do voto do deputado, não % local). O artefato commitado `bahiaElectionAggregates.ts` já tem a série Solla + válidos federais 2014/2018/2022 por slug do catálogo (435 unidades; Salvador por zona, Camaçari inteiro) — rank e share são deriváveis em memória, sem query. Este item entrega a lente da mesa **antes** de E8/E9; a fila (E9) depois absorve a coluna/ordenação como uma das suas. + +## Objetivos + +- **Detalhe do município:** no `MunicipalityBaselineCard` (ou card irmão compacto), por ano da série (default 2022): posição em votos absolutos (rank denso entre as 435 unidades do catálogo, ex.: "12º de 435") e **% da própria votação estadual** (votos no município ÷ Σ votos do candidato no ano, ex.: "3,1% da votação"). +- **Lista de municípios:** ordenação nova `?sort=votos` (ano default 2022) em `municipalityUi.ts`/`campaignListUrl.ts`, preservando filtros/paginação; coluna/linha secundária com votos 2022 + % da própria votação. +- **Helper puro** `src/utilities/municipalityVoteRank.ts`: computa uma vez por request (rank map + shares por ano sobre `federalBaselineMunicipalitySlugs()`), testável em unit; consumido pelo detalhe, pela lista e depois por E9/E17. +- Access: superfícies staff (coordinator/advisor/candidate); leader segue em lockdown (sem páginas de município) — nada muda. + +## Decisões travadas + +- **Rank sobre as 435 unidades do catálogo** (zonas de Salvador ranqueiam individualmente — é a malha operacional; a soma de Salvador aparece em E17/TI, não aqui). **Rejeitado:** rank sobre 417 municípios IBGE (mistura malhas e não bate com a lista). +- **Denominador do share = Σ votos do candidato no ano no artefato** (recorte BA). É a concentração da própria votação — não % dos válidos locais (dominância, outra métrica, já existe no mapa) nem % do eleitorado. **Rejeitado:** % do eleitorado local como âncora (a mesa rejeitou explicitamente). +- **Rank denso** (empates dividem posição) e formatação pt-BR compacta. **Rejeitado:** rank ordinal com desempate arbitrário. +- **Sem persistência** — derivado do artefato em memória (435 chaves; O(n log n) por ano, cacheável por request). **Rejeitado:** coluna materializada/migration (dado imutável já commitado). +- **i18n e naming:** `municipalityVoteRank.ts`, `computeVoteRankByYear`, `voteShareOfCandidate`, `?sort=votos`; labels pt-BR ("Posição em votos", "% da votação"). + +## Questões em aberto + +- **Ano na URL da ordenação (`?sort=votos&ano=2018`)?** **Recomendação:** v1 só 2022 (ano da lente atual da mesa); seletor de ano entra se a mesa pedir série na lista (o detalhe já mostra a série). +- **Mostrar variação de rank 2018→2022 no detalhe?** **Recomendação:** não na v1 — tendência já existe (`politicalTrend` + série do card); rank delta é açúcar que pode confundir com a classificação (E10). + +## Abordagem proposta + +```mermaid +flowchart LR + Artifact["bahiaElectionAggregates.ts
(votesByYear por slug)"] + Rank["municipalityVoteRank.ts
(rank denso + share por ano)"] + Card["MunicipalityBaselineCard
(posição + % da votação)"] + List["MunicipalityList + sort=votos
(municipalityUi.ts)"] + Artifact --> Rank --> Card + Rank --> List +``` + +Componentes tocados: `src/utilities/municipalityVoteRank.ts` (novo, puro + unit tests), `municipalityUi.ts` (`MunicipalityListState.sort`), `MunicipalityFilters.tsx` (select "Ordenar por" — padrão auto-aplicado dos filtros), `MunicipalityList.tsx` (linha com votos/% quando ordenado por votos), `MunicipalityBaselineCard.tsx` (posição/share por ano), `municipalityPageData.ts` (ordenar o conjunto já carregado — o bundle A9+ carrega o conjunto filtrado inteiro; sem paginação SQL nova). + +## Dependências + +- Nenhuma dura (artefato em produção desde o hardening 2026-07-23). Relação: **E9** absorve `sort`/coluna como parte da fila (o select de ordenação nasce aqui e E9 adiciona `deficit`/`risco-frescor`); **E17** reusa o helper para o rollup por TI; **E4R** deixa `priority` real para o cruzamento visual. + +## Não escopo + +- Fila de decisão completa (E9); classificação/âncoras relativas (E10 — LQ/captura ficam lá); mudanças no mapa (B13); rank por TI (E17). + +## Rabbit holes + +- **Virar "tabela de ranking" gamificada.** Rank é atributo do município, não leaderboard: sem página própria, sem medalhas, sem delta de posição na v1. +- **Recalcular share com denominadores alternativos** (válidos locais, eleitorado, campo). Cada um é outra métrica com outro dono (mapa/E8/E10); aqui é só concentração da própria votação. + +## Adiado com gatilho + +- **Seletor de ano na ordenação da lista.** Gatilho: pedido da mesa por leitura de série na lista. +- **Coluna de rank na fila do E9.** Entra junto com E9 (a fila referencia este helper). + +## Referências + +- `docs/roadmap.md` (A11) · [fila-de-alocacao.md](fila-de-alocacao.md) (E9 — absorve) · [tabela-ti-inicio.md](tabela-ti-inicio.md) (E17 — reusa helper) +- `docs/CUSTOMER.md` — surpresa da sessão 2026-07-23 (prioridade por % do voto do deputado) +- `docs/research/literatura-campanha-deputado-federal-ba.md` §8.1 (concentração da própria votação) +- `src/lib/bahiaElectionAggregates.ts`, `src/utilities/municipalityUi.ts`, `src/utilities/campaignListUrl.ts`, `src/components/campaign/MunicipalityBaselineCard.tsx`, `src/components/campaign/MunicipalityList.tsx`, `src/components/campaign/MunicipalityFilters.tsx` +- AGENTS.md — naming (identificadores em inglês, URL keys em inglês com valores pt onde forem dado) diff --git a/docs/plans/registro-fundacao.md b/docs/plans/registro-fundacao.md index f6fffe42..a0c996da 100644 --- a/docs/plans/registro-fundacao.md +++ b/docs/plans/registro-fundacao.md @@ -31,6 +31,7 @@ FU4 do relatório: backtest compara o que se sabia antes com o que aconteceu dep - **G4 — decisões ex-ante:** collection `allocationDecision` (admin group `Campanha`, `admin.hidden` como `supporterImportBatch`): `municipality`, `patternId` (texto — P1…K-C ou `manual`), `suggestion` (texto), `decision` (`aceita | descartada | adiada`), `alternativeReading` (obrigatório ao descartar), `snapshot` (json — classificação/números no momento), `decidedBy` (derivado), `note`. Imutável após criação. - Access: tudo staff; `leader` não lê sinais staff-only (`triangulated`), não lê `allocationDecision`, não lê `origin`/`resourceCost` — mesmo padrão `canReadCampaignStaffField`. - Garantia transversal: nenhuma action nova sobrescreve histórico; escritas multi-collection com `withPayloadTransaction` + `req`. +- **Aceite de campo (O7/O4 — [CUSTOMER.md](../CUSTOMER.md)):** registrar o delta do dia (ex.: "Cairu: ex-prefeito ligou, 1.000 → 300–500") em **≤30 segundos no celular**, a partir do detalhe/lista — o pledge atualizado vira versão (G1) e/ou sinal de 1 linha (G2). É o momento Orolândia da sessão de 2026-07-23: ele edita o mapa em viagem; se o registro custar mais que a edição da planilha, o dado continua morrendo no ZAP. ## Decisões travadas diff --git a/docs/plans/tabela-ti-inicio.md b/docs/plans/tabela-ti-inicio.md new file mode 100644 index 00000000..6d21d3d4 --- /dev/null +++ b/docs/plans/tabela-ti-inicio.md @@ -0,0 +1,92 @@ +# E17 — Tabela comparativa dos Territórios de Identidade no Início + +Status: rascunho (pedido direto do candidato — overview regional ao lado do mapa do Início) +Atualizado em: 2026-07-24 +Item do roadmap: [docs/roadmap.md](../roadmap.md) (Demais itens abertos, E17; primeira fatia da camada TI — E12 estende) +Impeccable: B — painel novo no dashboard staff existente (`/campanha`), sem rota nova +Appetite: ~1 dia eng; sem migration, sem collection +Responsável: — + +## Design (Impeccable) + +Âncoras: `PRODUCT.md` (princípios 2 e 6) / `DESIGN.md` (register `product`; "Priority lift" para painéis de overview) · dashboard staff existente (`MunicipalityMapPanel`, `CampaignMetricStrip`). + +Na implementação: craft compacto → critique → polish. + +- **Persona / contexto:** o candidato e o CG lendo o estado por região — a língua regional que o campo já fala é o TI (PGP "nos territórios"; Lula venceu nos 27, Jerônimo em 22/27). Pedido explícito do deputado (2026-07-24). +- **Job principal:** comparar de relance os 27 TIs — onde está minha votação, onde há estimativa 2026, onde falta responsável — e clicar para a lista filtrada. +- **Estratégia de cor:** Restrained; tabela densa e sóbria (Field Desk), sem heatmap; badge âmbar discreta para "sem responsável". +- **Anti-goals:** média regional que decide alocação municipal (MAUP); heat/ranking gamificado; segundo mapa. + +## Contexto + +O relatório de discovery (§6.5) fixou o TI como **default operacional das decisões regionais** (carteiras de assessor, giros, dobradinha regional, diálogo com o estado) com regra de bolso "gente e logística regional → TI; voto e mediação → município", e listou as análises que só estabilizam nesse nível (balanço de portfólio, gap regional, leitura da majoritária). O produto ainda não tem **nenhuma** superfície regional. Este item entrega a primeira: tabela comparativa dos 27 TIs no Início, ao lado do mapa (que segue municipal). A camada TI completa — benchmark intra-TI (T4), mediana/amplitude/município crítico, padrões T no motor — é **E12**, que estende esta tabela; aqui entram apenas agregados que não mentem (somas e razões de agregados). + +## Objetivos + +- **Painel "Territórios de Identidade"** no dashboard staff (`/campanha`, junto ao mapa): uma linha por TI com colunas v1 — municípios (n) · votos 2022 (com série 2014/2018 em tooltip/disclosure) · **% da própria votação** (Σ votos do TI ÷ Σ estadual) · válidos 2022 (peso) · Σ estimativa 2026 (cenário default `central` via A10) · municípios com assessor (n/n, badge quando 0). +- **Ordenação default: % da própria votação desc** (a lente da mesa — CUSTOMER.md); cabeçalhos clicáveis para ordenar pelas demais colunas (client-side, 27 linhas). +- **Metropolitano sempre decomposto** (salvaguarda do relatório): a linha "Metropolitano de Salvador" abre em duas sub-linhas — Salvador (19 zonas agregadas) × demais municípios da RMS. +- **Linha → lista filtrada:** cada TI linka `/campanha/municipios?region=` (filtro `region` já existe em `MunicipalityFilters`). +- Access: painel staff (coordinator/advisor/candidate); advisor vê a tabela completa (leitura regional é contexto, não gestão); leader segue em lockdown. + +## Decisões travadas + +- **Só somas e razões de agregados; nenhuma média de razões.** Captura/LQ regional, mediana, amplitude e município crítico ficam para E12 (salvaguardas MAUP são requisito de aceite lá). A tabela v1 não induz decisão municipal — nota fixa no rodapé: "leitura regional; alocação decide-se por município". **Rejeitado:** coluna "captura média" (média que mente — anti-goal 9 do relatório). +- **Malha = `bahiaTerritories.ts` congelada no ciclo** (27 TIs; município→TI estático). **Rejeitado:** Regiões Imediatas (G10 — adiado com gatilho); malha administrável (limites mudam por PPA, não por CRUD). +- **Metropolitano decomposto na própria tabela** (sub-linhas), não em página à parte. É a exceção permanente do relatório (24,4% do eleitorado). **Rejeitado:** tratar Metropolitano como uma linha (esconde Salvador). +- **Σ estimativa via agregação A10 existente** (`aggregatePledgesByMunicipality`/`municipality.expectedVotes`, cenário default `central`) — mesma regra `estimated ?? declared` de `votePledgeData.ts`; sem seletor de cenário na v1 (o mapa já tem o dele). +- **i18n e naming:** `territoryOverview.ts`, `computeTerritoryRollup`, `TerritoryOverviewTable`; labels pt-BR ("Territórios de Identidade", "% da votação", "Com assessor"). + +## Questões em aberto + +- **Posição no Início: acima ou abaixo do mapa?** **Recomendação:** abaixo do mapa em desktop (mapa segue o hero espacial; a tabela é a leitura comparativa), colapsável em mobile. Validar com o candidato na primeira demo. +- **Coluna de tendência (série 2014→2022 por TI)?** **Recomendação:** v1 tooltip/disclosure com a série; seta de tendência só se a mesa pedir (evitar poluir 27×7 células). + +## Abordagem proposta + +```mermaid +flowchart LR + Terr["bahiaTerritories.ts
(município→TI, 27)"] + Artifact["bahiaElectionAggregates.ts
(votos + válidos por slug)"] + Pledges["votePledgeData/expectedVotes
(cenário default A10)"] + Advisors["municipality.advisors
(cobertura n/n)"] + Rollup["territoryOverview.ts
(Σ por TI; Metropolitano decomposto)"] + Table["TerritoryOverviewTable
(Início, ordenável, linha→lista filtrada)"] + Terr --> Rollup + Artifact --> Rollup + Pledges --> Rollup + Advisors --> Rollup + Rollup --> Table +``` + +Componentes: `src/utilities/territoryOverview.ts` (novo, puro sobre o bundle do dashboard + artefato; unit tests com fixture dos 27 TIs), `src/components/campaign/TerritoryOverviewTable.tsx` (novo; tabela shadcn densa, sub-linhas do Metropolitano), encaixe em `campaignDashboardData.ts`/página do Início ao lado de `MunicipalityMapPanel`. Reusa `municipalityVoteRank.ts` (A11) para shares quando existir. + +## Dependências + +- Nenhuma dura: `bahiaTerritories.ts` (B2), `bahiaElectionAggregates.ts` (hardening), A10 ✓ (cenários), filtro `region` da lista. Suaves: **A11** (helper de share — se A11 ainda não tiver entrado, o rollup calcula o share localmente e A11 depois unifica); **E4R** (estimativas/prioridade reais deixam a coluna Σ estimativa viva); **E8** (quando existir, E12 adiciona cobertura de meta por TI — não nesta v1). +- **E12 estende** esta tabela (benchmark intra-TI, mediana/amplitude/município crítico, gap regional de captura, padrões T no motor) — mesma superfície, mais colunas/salvaguardas. + +## Não escopo + +- Captura/LQ/cobertura de meta por TI (E12, pós-E8); mapa por TI (o mapa segue municipal — polígonos dos TIs existem em B2 mas trocar a malha do mapa é decisão de E12/B13); padrões T1–T5 (E11 fase 2); seletor de cenário próprio. + +## Rabbit holes + +- **"Só mais uma coluna" até virar planilha regional.** 6–7 colunas é o teto da v1; qualquer métrica derivada nova (captura, gap, LQ) espera E12 com as salvaguardas. +- **Sub-linhas viram drill-down infinito** (TI → município → zona). A tabela para nas duas sub-linhas do Metropolitano; drill é a lista filtrada (link). +- **Rollup no client sobre o bundle inteiro.** 435 municípios × 27 TIs é trivial no server; computar em `territoryOverview.ts` no RSC e mandar as 27 linhas prontas. + +## Adiado com gatilho + +- **Cobertura de meta por TI (Σ metas × participação histórica).** Gatilho: E8 entregue (o sanity check por TI de E8 já nasce lá; a coluna na tabela vem com E12). +- **Leitura da majoritária por TI** (os 5 TIs onde o campo perdeu o governador). Gatilho: E12 (precisa de agregado do majoritário por TI — hoje só existe por município nas collections). + +## Referências + +- `docs/roadmap.md` (E17) · [camada-territorios-identidade.md](camada-territorios-identidade.md) (E12 — estende) · [inteligencia-campanha.md](inteligencia-campanha.md) (plano-mestre) +- `docs/research/relatorio-entrevista-persona-campanha.md` §6.5 (três camadas, salvaguardas MAUP, Metropolitano decomposto) +- `docs/research/literatura-campanha-deputado-federal-ba.md` §8.3 (TI, IBGE 2017, MAUP) +- `src/lib/bahiaTerritories.ts`, `src/lib/bahiaElectionAggregates.ts`, `src/utilities/votePledgeData.ts`, `src/utilities/campaignDashboardData.ts`, `src/components/campaign/MunicipalityMapPanel.tsx`, `src/components/campaign/MunicipalityFilters.tsx` (filtro `region`) +- `PRODUCT.md`/`DESIGN.md` — painel de overview (priority lift), anti-goals de dashboard SaaS +- AGENTS.md — access por papel, naming diff --git a/docs/roadmap.md b/docs/roadmap.md index fb94a697..cc5b4672 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -1,6 +1,6 @@ # Roadmap — Teqo -Atualizado em: 2026-07-24 (janela 1 vigente — convenções até 05/08; foco: smoke pós-deploy, R6, E8; último compile) +Atualizado em: 2026-07-24 (janela 1 vigente — convenções até 05/08; foco: smoke pós-deploy, E4R→A11/E17 na semana de onboarding, R6, E8; sincronizado com a sessão de campo de 2026-07-23 — [CUSTOMER.md](CUSTOMER.md)) Registro canônico dos **próximos** planos e débitos. Histórico de entregas: resumo abaixo + planos em [`docs/plans/`](plans/) + notebook [`.cursor/rules/projects/nucleos-eleitorais.mdc`](../.cursor/rules/projects/nucleos-eleitorais.mdc). @@ -31,7 +31,7 @@ Textos provisórios de Consent + `/privacidade` auto-provisionados ([onda-0.md]( 1. **Lote jurídico único** _(externo)_ — textos finais (substituem provisórios) + base LGPD art. 11: `lideranca-autopreenchimento`, `apoiador-cadastro`, `apoiador-intencao-voto`, `campanha-notificacoes-push`, **`campanha-whatsapp-canal`** (D3 — armazenamento/processamento do canal interno staff↔lideranças; se o lote já tiver fechado sem esta chave, entra no próximo passe com a assessoria — não abrir rodada jurídica só por ela), Aviso de Privacidade, avaliação RIPD. 2. **Smoke pós-deploy** _(desbloqueado — deploy da remodelagem aplicado em produção em 2026-07-23)_ — `NEXT_PUBLIC_SITE_URL` HTTPS, login `/campanha`, município de teste; checklist no AGENTS.md. 3. **Ativação com dados reais** assim que (1) liberar — lideranças/apoiadores reais e import em massa. -4. **Onboarding do time** — usuários `coordinator`/`advisor`, primeiros municípios assumidos, treino de campo. +4. **Onboarding do time** — usuários `coordinator`/`advisor`, primeiros municípios assumidos, treino de campo; **seed da planilha de prioridades via E4R** (estratégia sem PII: metas/prioridade — [plano](plans/import-planilha-projecao.md)); rede/lideranças (nomes) só após o lote jurídico. **O0+** (escala/DRY pós-Onda 0) não bloqueia jurídico nem smoke fictício — [plano](plans/escala-dry-pos-onda0.md). @@ -48,35 +48,39 @@ Textos provisórios de Consent + `/privacidade` auto-provisionados ([onda-0.md]( ## Próximos — Campanha (`/campanha`) -### Programa Inteligência de campanha (E8–E15, B13, C12) +### Programa Inteligência de campanha (E8–E16, B13, C12 · adjacentes A11/E17) -O discovery literatura→persona→entrevista ([relatório aprovado](research/relatorio-entrevista-persona-campanha.md); compêndio com ~67 fontes em [docs/research/](research/)) fixou o kernel: a disputa de DF é conta de quociente fragmentada; o gargalo é converter lealdade de campo em voto nominal município a município via rede; % estadual absoluto é anti-métrica. O produto deve entregar **inteligência, não planilha chique**: metas derivadas, leitura relativa, fila de decisão priorizada, sugestões dado→decisão com humano no loop, registro ex-ante auditável. Plano-mestre: [inteligencia-campanha.md](plans/inteligencia-campanha.md) (incl. gaps de dados G1–G10 e desenho canônico da fila). +O discovery literatura→persona→entrevista ([relatório aprovado](research/relatorio-entrevista-persona-campanha.md); compêndio com ~67 fontes em [docs/research/](research/)) fixou o kernel: a disputa de DF é conta de quociente fragmentada; o gargalo é converter lealdade de campo em voto nominal município a município via rede; % estadual absoluto é anti-métrica. A **sessão real com o Coordenador Geral (2026-07-23 — [CUSTOMER.md](CUSTOMER.md))** confirmou as apostas centrais (fogo amigo intra-PT como ameaça nº 1; canal = ZAP sem registro datado; "coluna da vergonha" validada — Salvador cobrado 10×) e calibrou as âncoras: a leitura relativa da mesa é **% da própria votação** (concentração da captura própria — critério rígido de prioridade dele), não % do eleitorado local; a restrição dominante é **"perna"/estrutura**, não dinheiro; piso projetado **150 mil** (2022: 129k). O produto deve entregar **inteligência, não planilha chique**: metas derivadas, leitura relativa, fila de decisão priorizada, sugestões dado→decisão com humano no loop, registro ex-ante auditável. Plano-mestre: [inteligencia-campanha.md](plans/inteligencia-campanha.md) (incl. gaps de dados G1–G11 e desenho canônico da fila). -| ID | Fatia | Plano | Entrega essencial | Classe | Appetite | Janela | Depende de | -| --- | ---------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------ | -------- | --------- | ------------------------------ | -| E8 | Conta da cadeira | [detalhes](plans/conta-da-cadeira.md) | Global `campaignGoals`; potencial por município (válidos projetados, captura do campo, roll-off, headroom); decomposição meta→município; cobertura Σpledges÷meta (cenário default = média) | B | ~2d | 1–2 | — (A10 ✓, remodelagem em prod) | -| E9 | Fila de alocação | [detalhes](plans/fila-de-alocacao.md) | Lista de decisão (7 colunas), ordenação por déficit descoberto + risco por frescor, "coluna da vergonha" | B | ~1,5d | 2 | E8 | -| C12 | Registro-fundação | [detalhes](plans/registro-fundacao.md) | Versions em `votePledge`; sinais tipados no `municipalityUpdate`; `actionPlan.origin`+custo; collection `allocationDecision` | B | ~2d | 2 | — (paralelo a E8) | -| E10 | Classificação territorial relativa | [detalhes](plans/classificacao-territorial-relativa.md) | Âncoras relativas (LQ / share da cadeira marginal), multi-eixo; substitui 35/20/10 para DF | B | ~1d | 3 | E8 | -| B13 | Escala relativa no mapa | [detalhes](plans/escala-relativa-mapa.md) | Quantis do candidato (default) + LQ + rank; símbolo proporcional por votos em jogo; % válidos mantido | B | ~2d | 3 | E8, E10 | -| E14 | Níveis de envolvimento N0–N4 | [detalhes](plans/niveis-de-envolvimento.md) | `engagementLevel` com histerese e sinais de reversão; staff-only (vocabulário duplo) | B | ~1d | 3 | C12 | -| E11 | Motor de sugestões v1 | [detalhes](plans/motor-de-sugestoes.md) | 8 padrões (P1/P2/P3/P5/P6/P7/K-A/K-B), menu com estatuto, aceitar/descartar → `allocationDecision`, triagem 1–5 | C | ~3d | 3 | E8, E9, E10, C12 | -| E12 | Camada TI | [detalhes](plans/camada-territorios-identidade.md) | Rollups com salvaguardas MAUP, benchmark intra-TI (gatilhos T no motor = fase 2 de E11) | B | ~1,5d | 3 | E8 | -| E13 | Planejador de presença/giros | [detalhes](plans/planejador-de-giros.md) | Elegibilidade (5 condições ✓/—), fases construção/consolidação/ativação, compositor de giro, visita pedida×justificada | C | ~1,5d | 3 | E8, C12 | -| E15 | Backtest pós-eleição | [detalhes](plans/backtest-pos-eleicao.md) | Pledge (trajetória) vs. resultado por zona; calibração de limiares | A | ~1d | pós-04/10 | C12, TSE 2026 | +| ID | Fatia | Plano | Entrega essencial | Classe | Appetite | Janela | Depende de | +| --- | ---------------------------------- | ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | -------- | --------- | ------------------------------ | +| E8 | Conta da cadeira | [detalhes](plans/conta-da-cadeira.md) | Global `campaignGoals`; potencial por município (válidos projetados, captura do campo, roll-off, headroom); decomposição meta→município; cobertura Σpledges÷meta (cenário default = média); meta inicial ancorada em campo: piso 150 mil (2022: 129k) | B | ~2d | 1–2 | — (A10 ✓, remodelagem em prod) | +| E9 | Fila de alocação | [detalhes](plans/fila-de-alocacao.md) | Lista de decisão (7 colunas + % da própria votação — absorve A11), ordenação por déficit descoberto (default do staff quando houver metas — registro no fluxo de poder) + risco por frescor, "coluna da vergonha" | B | ~1,5d | 2 | E8 | +| C12 | Registro-fundação | [detalhes](plans/registro-fundacao.md) | Versions em `votePledge`; sinais tipados no `municipalityUpdate`; `actionPlan.origin`+custo; collection `allocationDecision` | B | ~2d | 2 | — (paralelo a E8) | +| E16 | Dossiê do município (pré-agenda) | [detalhes](plans/dossie-municipio.md) | Dossiê pré-visita compondo dados existentes (série/captura, pledges/cobertura, lideranças, conjuntura, forças/riscos, dobradinhas, sinais, perfil A8) + emendas quando houver dado (G11); visão print — pedido explícito de campo (O6) | B | ~1d | 2 | — (melhora com E8/C12) | +| E10 | Classificação territorial relativa | [detalhes](plans/classificacao-territorial-relativa.md) | Âncoras relativas (LQ / share da cadeira marginal), multi-eixo; substitui 35/20/10 para DF | B | ~1d | 3 | E8 | +| B13 | Escala relativa no mapa | [detalhes](plans/escala-relativa-mapa.md) | Quantis do candidato (default) + LQ + rank; símbolo proporcional por votos em jogo; % válidos mantido | B | ~2d | 3 | E8, E10 | +| E14 | Níveis de envolvimento N0–N4 | [detalhes](plans/niveis-de-envolvimento.md) | `engagementLevel` com histerese e sinais de reversão; staff-only (vocabulário duplo) | B | ~1d | 3 | C12 | +| E11 | Motor de sugestões v1 | [detalhes](plans/motor-de-sugestoes.md) | 8 padrões (P1/P2/P3/P5/P6/P7/K-A/K-B), menu com estatuto, aceitar/descartar → `allocationDecision`, triagem 1–5 | C | ~3d | 3 | E8, E9, E10, C12 | +| E12 | Camada TI | [detalhes](plans/camada-territorios-identidade.md) | Rollups com salvaguardas MAUP, benchmark intra-TI (gatilhos T no motor = fase 2 de E11) | B | ~1,5d | 3 | E8 | +| E13 | Planejador de presença/giros | [detalhes](plans/planejador-de-giros.md) | Elegibilidade (5 condições ✓/—), fases construção/consolidação/ativação, compositor de giro, visita pedida×justificada | C | ~1,5d | 3 | E8, C12 | +| E15 | Backtest pós-eleição | [detalhes](plans/backtest-pos-eleicao.md) | Pledge (trajetória) vs. resultado por zona; calibração de limiares | A | ~1d | pós-04/10 | C12, TSE 2026 | ### Demais itens abertos +- **E4R** import único da planilha de projeção (`docs/sheets/`) → `voteGoals`/`priority` (reabre o corte E4 — decisão de produto 2026-07-24 + evidência O5: a planilha é a fonte de verdade da mesa) · script idempotente com dry-run + runbook de produção · Janela 1 · **não cortável** (seed do Big Hire; insumo de E8/A11/E17) · [plano](plans/import-planilha-projecao.md) +- **A11** posição em votos do município (rank absoluto + % da própria votação) + ordenação da lista por votação · a lente de prioridade atual da mesa (sessão 2026-07-23) · ~0,5–1d · Janela 1–2 · E9 absorve como coluna/ordenação da fila · [plano](plans/ranking-votos-municipio.md) +- **E17** tabela comparativa dos Territórios de Identidade no Início (pedido do candidato) · somas/razões apenas, Metropolitano decomposto (salvaguardas MAUP) · ~1d · Janela 1–2 · primeira fatia de E12 · [plano](plans/tabela-ti-inicio.md) - **A6** dobradinha 2026 automática quando o TSE publicar candidaturas · gatilho externo: pós-15/08 · camada de insight sobre o registro operacional `stateDeputy` (M4) · alimenta **E13** · [plano](plans/insight-dobradinha-2026.md) - **B5 F2–F3** cache CLI compartilhado + factory mun/TI (scripts continuam) · [plano](plans/escala-dry-pos-b2.md) - **B8 F2** polígonos dos Municípios-zona de Salvador (ZE 1–19; Camaçari saiu do escopo na M1 — município inteiro): F1 catálogo zona→bairros entregue (hoje Salvador-only); falta prep do catálogo (~½ dia) + dissolve IBGE/malha → TopoJSON no mapa · Janela 3 · cortável · [plano](plans/poligonos-pracas-zona.md) - **C5** operação dia D / GOTV _(validar com produto)_ · design [`Dia-D-GOTV`](design-refs/latest/Dia-D-GOTV.png) · depende de C2 dados reais - **D2** push + sino in-app · soft: chave `campanha-notificacoes-push` (Onda 0) · [plano](plans/notifications.md) -- **Programa WhatsApp interno (D3–D5)** · baixa/média prioridade · 1:1 staff↔lideranças via bridge **não oficial** · **número pessoal** de cada assessor/CG (QR no app, parece chat pessoal) · não Business API / não massa / não chip institucional · fecha Little Hire · plano-mestre [whatsapp-interno-campanha.md](plans/whatsapp-interno-campanha.md) +- **Programa WhatsApp interno (D3–D5)** · baixa/média prioridade · 1:1 staff↔lideranças via bridge **não oficial** · **número pessoal** de cada assessor/CG (QR no app, parece chat pessoal) · não Business API / não massa / não chip institucional · fecha Little Hire · gatilho honesto (2026-07-24): "ZAP é o campo" é fato confirmado — D3 entra se os deltas do ZAP **não** estiverem sendo absorvidos pelo fluxo sede-digita (C12) após uso real · plano-mestre [whatsapp-interno-campanha.md](plans/whatsapp-interno-campanha.md) - **D3** fundação multi-sessão (QR por `campaignUser` + log + Consent `campanha-whatsapp-canal`) · [plano](plans/whatsapp-canal-fundacao.md) - **D4** envio 1:1 pela sessão do ator · depende de D3 · [plano](plans/whatsapp-envio-liderancas.md) - **D5** inbox da própria sessão → rascunhos (`municipalityUpdate`/demanda) com humano no loop · depende de D3 · [plano](plans/whatsapp-sugestao-atualizacoes.md) -- **R6** critique/polish visual da vertical remodelada (ciclo /impeccable completo por superfície; smoke visual coordenador feito em 2026-07-21) · gatilho: antes de 16/08 +- **R6** critique/polish visual da vertical remodelada (ciclo /impeccable completo por superfície; smoke visual coordenador feito em 2026-07-21) · absorve os débitos de produto/UX remanescentes de FD2 ([field-desk-ux-pos-critique.md](plans/field-desk-ux-pos-critique.md)): glossário inline (O3 — hipótese ainda sem evidência), triagem em lote, empty states de coordenador · gatilho: antes de 16/08 ### Fill-ins abertos @@ -89,6 +93,8 @@ O discovery literatura→persona→entrevista ([relatório aprovado](research/re ### A validar (assumptions) - **C5** GOTV — validar com produto antes de planejar (hoje só design-ref; depende de C2 dados reais). +- **O8** "ilhas isoladas" — fluxo de informação entre política/comunicação/território (pedido de ajuda direto na sessão 2026-07-23; adjacente ao produto) · discovery antes de qualquer build; candidato natural se virar build: D2 sino/resumo semanal. +- **O3** jargão — resgatar o Bloco B (uso observado) das notas dos entrevistadores e/ou 2ª sessão observada antes de investir em glossário além do R6 ([IMPROVE-APP-PLAN.md](IMPROVE-APP-PLAN.md)). ### Grafo de dependências (abertos + predecessores mínimos) @@ -105,11 +111,15 @@ flowchart TD D3 --> D5["D5 WA → rascunhos"] D4 -.thread.-> D5 B8F2["B8 F2 Polígonos
zonas de Salvador"] + E4R["E4R Import planilha
(seed estratégia)"] + A11n["A11 Posição em votos"] + E17n["E17 Tabela TI no Início"] subgraph Intel["Inteligência de campanha"] E8i["E8 Conta da cadeira"] E9i["E9 Fila de alocação"] C12i["C12 Registro-fundação"] + E16i["E16 Dossiê do município"] E10i["E10 Classificação relativa"] B13i["B13 Escala relativa no mapa"] E14i["E14 Níveis N0–N4"] @@ -119,6 +129,9 @@ flowchart TD E15i["E15 Backtest pós-eleição"] end + E4R -.metas seedadas.-> E8i + A11n -.coluna/ordenação.-> E9i + E17n -.primeira fatia.-> E12i E8i --> E9i --> E11i C12i --> E11i C12i --> E14i @@ -130,28 +143,31 @@ flowchart TD E14i -.níveis.-> E11i C12i --> E13i E8i --> E13i + E8i -.derivados.-> E16i + C12i -.sinais.-> E16i + E16i -.dossiê pré-visita.-> E13i A6 -.dobradinha.-> E13i C2prod -.dados reais.-> E11i E11i -.decisões acumuladas.-> E15i ``` -Paralelizáveis agora: **E8** (A10 ✓ e remodelagem em produção), **C12** (paralelo a E8), fill-ins (O0+, RS+). **D3** só após smoke + folga (não compete com E8/E9). +Paralelizáveis agora: **E4R → A11/E17** (semana de onboarding, sem deps), **E8** (A10 ✓ e remodelagem em produção), **C12** (paralelo a E8), **E16** (compõe o existente), fill-ins (O0+, RS+). **D3** só após smoke + folga e só se o fluxo sede-digita (C12) não absorver os deltas do ZAP (não compete com E8/E9). ### Sequência por janela (só pendentes) -**Janela 1 — agora → 05/08 (convenções):** smoke pós-deploy em produção + onboarding do time (Onda 0 §2/§4); **R6** critique/polish; **E8** pode começar em paralelo; Onda 0 jurídica em paralelo (externa). +**Janela 1 — agora → 05/08 (convenções):** smoke pós-deploy em produção + onboarding do time (Onda 0 §2/§4), com **E4R → A11/E17** na semana de onboarding (quadro seedado + a lente da mesa prontos quando o time logar); gate de adoção: sinal Little Hire — ≥1 update espontâneo até 30/07, cobrança da tabela dispensada (planilhas já em `docs/sheets/`) — acompanhamento em [IMPROVE-APP-PLAN.md](IMPROVE-APP-PLAN.md); **R6** critique/polish; **E8** pode começar em paralelo; Onda 0 jurídica em paralelo (externa). -**Janela 2 — 05/08 → 16/08 (pré-propaganda):** C2 dados reais assim que o jurídico liberar; **E8** conta da cadeira → **E9** fila de alocação, com **C12** registro-fundação em paralelo (migrations cedo, longe do congelamento); D2 se sobrar folga. +**Janela 2 — 05/08 → 16/08 (pré-propaganda):** C2 dados reais assim que o jurídico liberar; **E8** conta da cadeira → **E9** fila de alocação, com **C12** registro-fundação em paralelo (migrations cedo, longe do congelamento); **E16** dossiê do município (pedido O6 — compõe o existente, melhora com E8); D2 se sobrar folga. -**Janela 3 — 16/08 → set:** A6 dobradinha (pós-TSE 15/08); **E10** classificação relativa → **B13** escala relativa no mapa; **E14** níveis N0–N4; **E11** motor de sugestões v1; **E12** camada TI; **E13** planejador de giros; **B8 F2** polígonos das zonas de Salvador; débitos sobreviventes. Se houver folga **e** Little Hire ainda vazar para o ZAP: começar **D3**. +**Janela 3 — 16/08 → set:** A6 dobradinha (pós-TSE 15/08); **E10** classificação relativa → **B13** escala relativa no mapa; **E14** níveis N0–N4; **E11** motor de sugestões v1; **E12** camada TI; **E13** planejador de giros; **B8 F2** polígonos das zonas de Salvador; débitos sobreviventes. Se houver folga **e** os deltas do ZAP não estiverem sendo absorvidos pelo fluxo sede-digita (C12): começar **D3**. **Janela 4 — set → 04/10:** C5 GOTV _(validar)_, congelamento ~20/09 (só bugfix/dados). **D4** envio 1:1 e **D5** inbox→rascunhos só se D3 estiver estável **antes** do congelamento (senão adiar pós-04/10). **E15** backtest pós-eleição (após 04/10). ### Cortes seguros / não cortáveis -**Não cortáveis:** Onda 0 (jurídico/Consent); C2 dados reais; assimetria declarado×estimado (relação de campo); **E8**+**E9**+**C12** (a conta da cadeira, a fila e o registro ex-ante são o mínimo de "inteligência, não planilha" — e C12 é irrecuperável se não registrar durante a campanha). +**Não cortáveis:** Onda 0 (jurídico/Consent); **E4R** seed da planilha (Big Hire — o quadro nasce vazio sem ele); C2 dados reais; assimetria declarado×estimado (relação de campo); **E8**+**E9**+**C12** (a conta da cadeira, a fila e o registro ex-ante são o mínimo de "inteligência, não planilha" — e C12 é irrecuperável se não registrar durante a campanha). -**Cortes seguros** (se o prazo apertar, nesta ordem): **D5** inbox→rascunhos (manter registro manual + `wa.me`); **D4** envio bridge (manter `wa.me`); **D3** fundação do canal (atalho `wa.me` continua); **E13** planejador de giros (agenda segue manual com J-A/J-B como guia); **E12** camada TI (rollup manual por lista); **E15** backtest (pós-eleição por definição — cortar = perder o aprendizado 2030); **E11** motor v1 (manter fila E9 sem sugestões); **B13** símbolo proporcional (manter quantis/LQ como escala); **E14** níveis (manter `priority` alta/normal); **B8 F2** polígonos (mapa continua agregado no município; manter F1 bairros); **D2** push (adiar); **A6**; débitos/fill-ins. +**Cortes seguros** (se o prazo apertar, nesta ordem): **D5** inbox→rascunhos (manter registro manual + `wa.me`); **D4** envio bridge (manter `wa.me`); **D3** fundação do canal (atalho `wa.me` continua); **E12** camada TI (rollup manual por lista; **E17** já dá a leitura regional básica no Início); **E13** planejador de giros (rebaixado na fila de corte em 2026-07-24: "perna"/agenda é a restrição dominante nomeada em campo — cortar só depois de E12; agenda segue manual com J-A/J-B como guia); **E15** backtest (pós-eleição por definição — cortar = perder o aprendizado 2030); **E11** motor v1 (manter fila E9 sem sugestões); **B13** símbolo proporcional (manter quantis/LQ como escala); **E14** níveis (manter `priority` alta/normal); **B8 F2** polígonos (mapa continua agregado no município; manter F1 bairros); **D2** push (adiar); **A6**; **A11**/**E17** (baratos e sem deps — cortar só em último caso); **E16** dossiê (último dos extras — pedido explícito de campo); débitos/fill-ins. ## Bloqueadores atuais @@ -194,7 +210,7 @@ Paralelizáveis agora: **E8** (A10 ✓ e remodelagem em produção), **C12** (pa - PWA do site/`/admin`; PostGIS sem query espacial real - WhatsApp Business API / disparo em massa / blast a apoiadores ou eleitores (Res. TSE 23.610 art. 33; Meta veda WABA político). **Exceto** o programa **D3–D5**: bridge não oficial **1:1 interno** staff↔lideranças já no CRM (ver [whatsapp-interno-campanha.md](plans/whatsapp-interno-campanha.md)) — risco ToS/operacional explícito nos planos; não é substituto de WABA - Previsão estatística de votos neste ciclo -- Import automático de planilhas de projeção (decisão E4 mantida: dados estratégicos via UI) +- Import **automático/recorrente** de planilhas de projeção como feature de produto (atualização contínua segue via UI). O seed **único** E4R foi aprovado em 2026-07-24 (evidência O5 — a planilha é a fonte de verdade da mesa) — [plano](plans/import-planilha-projecao.md) ## Fontes @@ -203,4 +219,5 @@ Paralelizáveis agora: **E8** (A10 ✓ e remodelagem em produção), **C12** (pa - [`README.md`](../README.md) — missão - [`docs/plans/*.md`](plans/) — planos por item ([remodelagem-municipios.md](plans/remodelagem-municipios.md) e [inteligencia-campanha.md](plans/inteligencia-campanha.md) são os planos-mestre vigentes) - [`docs/research/`](research/) — embasamento de produto/domínio (compêndio de literatura + relatório de discovery aprovado 2026-07-21) +- [`docs/CUSTOMER.md`](CUSTOMER.md) — job, OST e evidência de campo (sessão real do CG em 2026-07-23; O1–O8) · [`docs/IMPROVE-APP-PLAN.md`](IMPROVE-APP-PLAN.md) — journey de discovery/UX (fases, próximas ações) - Res. TSE 23.760/2026 · Res. TSE 23.610/2019 art. 33 · Politipédia AVM