Sync roadmap and intel plans with the 2026-07-23 field session; add E4R/A11/E16/E17 plans

This commit is contained in:
Francisco Ignácio Fontoura Solla 2026-07-24 02:54:28 -03:00
parent adc0f31f5b
commit e468f4192e
14 changed files with 447 additions and 54 deletions

View file

@ -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<T>`), 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).

View file

@ -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).

View file

@ -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

View file

@ -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 <data>" — 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<br/>(compõe loaders existentes)"]
Baseline["baseline/A11<br/>(série, rank, share)"]
Rede["lideranças + frescor"]
Conj["politicalTrend + forças/riscos<br/>+ 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

View file

@ -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

View file

@ -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<br/>(A canônico, B referência)"]
Parse["parseExpectationCell<br/>(3 formatos + milhar)"]
Match["canonicalizeMunicipalityName<br/>+ municipalityCatalog"]
Sanity["sanity: votos × artefato<br/>REGIÃO × bahiaTerritories"]
Write["Payload Local API<br/>(voteGoals, priority; só vazios)"]
Report["relatório stdout<br/>(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=<prod> 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

View file

@ -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

View file

@ -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: —

View file

@ -85,8 +85,9 @@ curl -X POST "https://<prod-domain>/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

View file

@ -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

View file

@ -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<br/>(votesByYear por slug)"]
Rank["municipalityVoteRank.ts<br/>(rank denso + share por ano)"]
Card["MunicipalityBaselineCard<br/>(posição + % da votação)"]
List["MunicipalityList + sort=votos<br/>(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)

View file

@ -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

View file

@ -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=<TI>` (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<br/>(município→TI, 27)"]
Artifact["bahiaElectionAggregates.ts<br/>(votos + válidos por slug)"]
Pledges["votePledgeData/expectedVotes<br/>(cenário default A10)"]
Advisors["municipality.advisors<br/>(cobertura n/n)"]
Rollup["territoryOverview.ts<br/>(Σ por TI; Metropolitano decomposto)"]
Table["TerritoryOverviewTable<br/>(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

View file

@ -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<br/>zonas de Salvador"]
E4R["E4R Import planilha<br/>(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