diff --git a/docs/plans/salvador-pagina-detalhe-cidade.md b/docs/plans/salvador-pagina-detalhe-cidade.md new file mode 100644 index 00000000..b81784ce --- /dev/null +++ b/docs/plans/salvador-pagina-detalhe-cidade.md @@ -0,0 +1,92 @@ +# B178 — Salvador: cidade agregada no /campanha (página virtual + linha na lista, sem dupla contagem) + +Status: plano — registrado (blocked até plano em main) +Atualizado em: 2026-08-09 +Issue: #461 +Priority: P2 +Model: composer-2.5 +Model-local: deepseek-v4-flash-high +Impeccable: C — fluxo novo (página de leitura agregada da capital + linha na lista de municípios + busca) +Canvas UI: /home/fsolla/.cursor/projects/home-fsolla-cursor-worktrees-teqo-plans-plan-issue-7/canvases/plan-b178-ui-draft.canvas.tsx +Appetite: ~2–3 dias eng; agregação virtual sobre as 19 ZEs; sem migration / collection / Consent +Responsável: — + +## Intenção + +O Sollinha (chat IA em `/campanha`) alucinou um link para a “página de detalhe de Salvador” que **não existe** — e o link fazia sentido: “Salvador” é uma cidade real que o time e o candidato citam como cidade. O problema de fundo: desde o remodel, o catálogo representa Salvador como 19 unidades operacionais (`salvador-ze-1…19`) — correto para operar, mas a capital ficou **sem leitura agregada** (`/campanha/municipios/salvador` → 404) e sem representação como cidade nas superfícies de município. Produto quer que **Salvador apareça como cidade** em três pontos, mantendo as ZEs como operação: (1) página de detalhe virtual (rollup eleitoral + demandas/atividades); (2) **linha agregada na lista de municípios**; (3) busca mostrando agregado e zonas. Com um guardrail central: **nunca somar a cidade e as zonas juntas** em qualquer total/média/TI/mapa. + +## Persona e fluxo + +- **Persona / contexto:** coordenador, candidato ou assessor raciocinando sobre a capital como um todo (votação, posição, comparação com o interior, demandas, agenda) — ou recebendo um link da Sollinha apontando “Salvador”. +- **Job principal:** abrir/achar “Salvador” como cidade (link do chat, busca da lista, digitação do slug), ler o desempenho agregado da capital e navegar para as ZEs quando quiser operar. +- **Fluxo desejado:** + 1. Busca na lista de municípios por “Salvador” → aparecem a **linha agregada da cidade** e as **19 ZEs**. + 2. Clicar na linha/página da cidade → `/campanha/municipios/salvador` resolve para a página virtual read-only. + 3. Página: header “Salvador”, nota “ZE 1–19 · unidade de operação é a zona”; rollup eleitoral (votos por ano, % da própria votação, **posição por cidade**); entradas demandas/agenda/19 ZEs/mapa. + 4. Quem quer operar vai para a ZE; a cidade é leitura. +- **Anti-goals de produto:** + - Salvador **não** vira nova unidade operacional (o remodel fixou ZE como unidade — operação, pledges e lideranças continuam por zona). + - **Dupla contagem proibida:** o agregado da cidade **substitui** suas 19 zonas em qualquer somatória (totais da lista, médias, rollup por Território de Identidade, mapa, “conta da cadeira”) — nunca soma cidade + zonas. + - Não “engole” as 19 ZEs: cada ZE mantém sua ficha, dossiê e linha. + - Não reforma o mapa (polígono da cidade segue como base não-interativa). + - A cidade não vira um 436º município no catálogo de operação (agregação virtual, derivada). + +## Objetivo e aceite + +- `/campanha/municipios/salvador` resolve para uma página **read-only** da cidade, coerente com o shell das páginas de município (header + abas/seções). +- A página mostra o **rollup eleitoral da capital** (série de votos do candidato por ano + % da própria votação + **posição por cidade**, ex.: “12º de 663 em 2022”) e as seções de **demandas e agenda/atividades** da cidade (como nas páginas de município, que apontam para Demandas/Agenda). +- A **lista de municípios** ganha uma **linha agregada da cidade** (nome “Salvador – cidade” / “Salvador (agregado)”, mesmos números do rollup), **mantendo as 19 ZEs**. +- A **busca da lista** por “Salvador” retorna **agregado + zonas** (e, no detalhe, a página da cidade é o destino do link). +- **Guardrail duro (aceite):** em todos os agregados que consomem a lista (totais, médias, TI, mapa, rollups descendentes) a cidade agregada e as suas ZEs **nunca somam juntas** — presentes os dois, vale a cidade OU as ZEs, uma única vez por município “Salvador”. Isso é testável com contagem invariante (cada voto do catálogo entra no máximo uma vez em qualquer soma). +- O link da Sollinha para “Salvador” passa a ser canônico e não quebra mais. +- Leader lockdown: página/linha staff-only (mesmo acesso das demais páginas de município). +- Sem migration, sem collection nova (cidade é agregação virtual), sem Consent, sem persistência. + +## Dados (intenção) + +- **Vou apresentar dados?** Sim — superfície neste item (página + linha + busca). +- **Decisões desbloqueadas:** staff decide **prioridade/comparação da capital** (maior caixa de votos do estado) e navega às zonas para agir; leitura relativa/local (posição entre candidatos votados na cidade, % da própria votação) — nunca % estadual absoluto como âncora. +- **Forma:** adiada ao plano de implementação; restrições de produto: cidade = **agregação virtual** sobre as 19 ZEs (mesmo princípio do rollup por Território de Identidade / padrão MAUP); posição por cidade = uma colocação para a capital inteira. + +## Direção no codebase (hipótese) + +- **Áreas prováveis:** rota de detalhe `/campanha/municipios/[slug]` reconhecendo o caso “cidade” (Salvador) no shell existente; loader da lista de municípios estendido para inserir a **linha agregada** e a busca (agregado + zonas); reuso dos loaders de baseline (baseline por zona, somados) e da fonte de rank por cidade — **sem** paralelo novo de query. +- **Precedente:** [`ranking-votos-municipio.md`](ranking-votos-municipio.md) (A11 — posição por município); [`camada-territorios-identidade.md`](camada-territorios-identidade.md) (E12 — rollup/TI resolveu o mesmo problema de “não somar duas vezes” com salvaguardas MAUP); [`dossie-municipio.md`](dossie-municipio.md) (E16 — dossiê por ZE). +- **Risco de acoplamento:** o guardrail de dupla contagem atravessa **todos** os consumidores de agregados de municípios (lista, TI, mapa, conta da cadeira) — é o risco central do item; não reabrir o remodel (ZE = unidade operacional); cutover do detalhe (B152/B155+) segue valendo para as ZEs; leader lockdown. + +## Dependências + +- Nenhuma dura. +- Soft: **B162** (Sollinha URL tool, ready) — apontar município “Salvador” respira a página da cidade; **B177** — quando o contexto é a capital, o link pode levar à página da cidade. +- Atenção: o guardrail de dupla contagem conversa com rollups já entregues (E12/TI, A11, mapa B13) — o executor deve auditá-los, sem os reabrir (não são dep aberta; é invariante a preservar). + +## Fora de escopo + +- Operação por cidade no v1 (pledges, lideranças, níveis, avaliação de assessor) — item sucessor se a mesa pedir. +- Substituir ou redesenhar as 19 páginas de ZE, seus dossiês e linhas. +- Mudanças no mapa (polígono/legenda/interação da cidade). +- O catálogo continua com **435** unidades operacionais; a cidade é derivada, nunca seed. + +## Rabbit holes de produto + +- **“Salvador vira um 436º município operacional.”** Quebra remodel, snapshot e contagem. **Corte:** cidade é leitura derivada; operação segue por zona. +- **“Somar a cidade + as zonas só porque ‘aparecem juntas’.”** É exatamente o bug que o user vetou. **Corte:** guardrail testável de “cada voto entra uma única vez”; sempre cidade **ou** zonas. +- **“Espelhar todas as abas do detalhe de município.”** Amplicaria para coisas sem decisão clara por cidade. **Corte:** abas com dono de dado para a cidade (eleitoral, demandas, agenda); o resto espera pedido. +- **Rollup por cidade de pledges/lideranças (mesma pessoa em 2 zonas).** Decisão de dados não deste item. **Corte:** fora do v1. + +## Questões em aberto (produto) + +Resolvidas no gate (2026-08-09): + +- **Página real, redirecionamento ou só-tool?** **Decisão:** página virtual read-only + manter (e não substituir) as ZEs. +- **Linha na lista de municípios?** **Decisão:** sim — linha agregada da cidade **além** das 19 ZEs (viável: agregação virtual sobre as ZEs; não é 436º no catálogo). +- **Busca da lista?** **Decisão:** “Salvador” retorna agregado + zonas. +- **O que o v1 mostra além do eleitoral?** **Decisão:** + demandas e agenda/atividades da cidade (não inclui conta da cadeira/benchmark por cidade no v1). +- **Dupla contagem?** **Decisão:** guardrail duro — cidade e zonas nunca somam juntas em qualquer total/média/TI/mapa (invariante testável). + +## Referências + +- Canvas UI (gate): [plan-b178-ui-draft.canvas.tsx](/home/fsolla/.cursor/projects/home-fsolla-cursor-worktrees-teqo-plans-plan-issue-7/canvases/plan-b178-ui-draft.canvas.tsx) +- `src/lib/municipalityCatalog.ts` (Salvador = 19 ZEs), `src/lib/bahiaElectionAggregates.ts` (série + rank por cidade), `src/app/(campaign)/campanha/(app)/municipios/[slug]/page.tsx` (shell do detalhe), loader da lista de municípios (`src/utilities/municipality/…`) +- [`ranking-votos-municipio.md`](ranking-votos-municipio.md) (A11) · [`camada-territorios-identidade.md`](camada-territorios-identidade.md) (E12/MAUP) · [`dossie-municipio.md`](dossie-municipio.md) (E16) · [`sollinha-tool-urls-navegacao.md`](sollinha-tool-urls-navegacao.md) (B162) +- AGENTS.md — Municípios model (Salvador por ZE), leader lockdown diff --git a/docs/plans/sollinha-cidades-mais-votado.md b/docs/plans/sollinha-cidades-mais-votado.md new file mode 100644 index 00000000..e9287a1d --- /dev/null +++ b/docs/plans/sollinha-cidades-mais-votado.md @@ -0,0 +1,88 @@ +# B177 — Sollinha: responder “em quais cidades X foi o deputado mais votado” (rank competitivo reverso, flexível) + +Status: plano — registrado (blocked até plano em main) +Atualizado em: 2026-08-09 +Issue: #460 +Priority: P1 +Model: composer-2.5 +Model-local: deepseek-v4-flash-high +Impeccable: A — N/A (resposta em texto no chat existente; sem superfície nova) +Canvas UI: N/A — sem UI +Appetite: ~1 dia eng; uma tool de leitura flexível + orientação no prompt; sem migration / Consent / collection +Responsável: — + +## Intenção + +O candidato testou a plataforma e perguntou ao Sollinha (chat IA em `/campanha`) **“em quais cidades eu fui o deputado mais votado em 2022”** — e o Sollinha não conseguiu responder porque não tem uma ferramenta para isso. A pergunta é a **leitura reversa** da “posição no município” que já existe no mapa: em vez de “o que se passa neste município”, perguntar “em quais municípios o candidato ficou na frente”. Decisão de produto no gate: a tool deve ser **flexível** — além do candidato da campanha, deve responder para **qualquer deputado federal** (por número ou nome), mantendo o nosso candidato como default. Hoje nada disso existe como resposta conversacional. + +## Persona e fluxo + +- **Persona / contexto:** candidato (e depois coordenador/assessor) conversando com a Sollinha; quer saber onde um deputado é forte — normalmente o da campanha (“de onde vem meu voto”, “onde eu ganho”), mas também comparação com outro candidato. +- **Job principal:** perguntar em linguagem natural e receber a lista de cidades onde um deputado federal foi o **mais votado** (ou ficou entre os top-N) em um ano da série — com contagem e contexto relativo. +- **Fluxo desejado:** + 1. Pergunta do tipo “em quais cidades {ele, o candidato, o deputado X} foi o deputado mais votado?” (2022 default, ou ano explícito; defeito = candidato da campanha). + 2. Sollinha chama a tool (faz a leitura do rank reverso do candidato pedido). + 3. Resposta: lista das cidades onde ficou em 1º (ou top-N) + contagem + contexto relativo (ex.: “de N cidades com voto” / “Xº lugar em Salvador entre Y candidatos”). + 4. Quando útil, link para a leitura visual (mapa/posição, página da cidade) — padrão das tools de navegação. +- **Anti-goals de produto:** + - Não virar “leaderboard de vitórias” gamificado (cidades são dados, não medalhas). + - Cargo: fica **deputado federal** (Câmara); não estende para deputado estadual/senador neste item (a fonte e a chance do mapa são federais). + - Não misturar malhas: a resposta é **por cidade** (417); as 19 ZEs de Salvador **compartilham a colocação da cidade** e não podem aparecer como 19 linhas. + +## Objetivo e aceite + +- Existe uma tool de leitura (nome livre) que responde, para um **deputado federal** (identificado por número ou nome; default = candidato da campanha): “em quais municípios ficou em 1º entre os deputados federais” em um ano (2014/2018/2022; 2022 default) — com contagem. +- A mesma tool aceita “até a posição X” (ex.: “top 5 em” / “entre os 3 mais votados”) — é extensão da mesma pergunta, não pedido separado. +- “Mais votado” = colocação 1 (empates no topo dividem a colocação — mesma semântica do mapa), e o texto deixa isso claro se houver empate. +- Salvador entra como **uma** cidade (nunca 19), com sua colocação por cidade. +- A resposta usa nomes canônicos e contagem; nunca inventa número (fail-safe: “sem dados para o ano/candidato” quando não houver). +- Leader lockdown: liderança não tem essa conversa sobre municípios (mesmo padrão das outras tools staff). +- Sem migration, sem collection, sem Consent, sem persistência de histórico. + +## Dados (intenção) + +- **Vou apresentar dados?** Sim — superfície no chat (lista + contagem + contexto relativo). +- **Decisões desbloqueadas:** staff/candidato decidem **onde a base de um deputado está concentrada** (roteiro de investimento, comparação capital vs interior, leitura do adversário) — leitura da “concentração da própria votação” (compêndio §8.1), nunca % estadual absoluto como âncora. +- **Forma:** adiada ao plano de implementação; restrições de produto: leitura relativa/local, densa de contexto (“de N candidatos votados”), um lugar para cada cidade; sem KPI estadual absoluto. + +## Direção no codebase (hipótese) + +- **Áreas prováveis:** `src/utilities/ai/tools/` (nova tool de leitura + registro em `index.ts`); `src/utilities/ai/systemPrompt.ts` (quando usar); fonte de dados: para o **candidato da campanha**, o rank por cidade já é commitado e imutável (leitura instantânea); para **qualquer outro candidato**, derivar das coleções de votos (mesma semântica de empate/por cidade do mapa) — **sem** segunda fonte paralela desalinhada. +- **Precedente:** [`ranking-votos-municipio.md`](ranking-votos-municipio.md) (A11 — posição por município); [`sollinha-tool-urls-navegacao.md`](sollinha-tool-urls-navegacao.md) (B162 — padrão das tools do chat); o mapa usa a mesma “posição no município”. +- **Risco de acoplamento:** manter a semântica de rank exatamente como a do mapa (uma leitura, duas superfícies); para terceiros candidatos, não sobrescrever o artefato do nosso candidato; leader lockdown. + +## Dependências + +- Soft: **B162** (Issue #383, ready) — link de navegação quando ajudar; ferramentas do chat vivem no mesmo lar. +- Soft: **B178** — se a página da cidade nascer, o link “ver Salvador” pode apontar para ela quando o contexto é a capital. +- Nenhuma dependência dura de Issue aberta. + +## Fora de escopo + +- Tela nova / UI no `/campanha` para “cidades mais votadas” (chat basta; superfície → item separado). +- Cargos além de deputado federal (estadual, senador) — extensão futura. +- Embelezamento de ranking / medalhas / série em chart. +- Web search / grounding externo. + +## Rabbit holes de produto + +- **“Vira análise completa de vitórias.”** A tool responde uma família de perguntas curtas; tudo além (mapa de vitória, tabelão) é outro item. **Corte:** escopo = “onde ficou em 1º / top-N” para um deputado federal. +- **“Consistência com o mapa de um terceiro candidato.”** O mapa só conhece a posição do nosso candidato; para terceiros a tool é a única leitura — não obrigar o mapa. **Corte:** tool autônoma, sem espelhar superfície nova. +- **“Responder por ZE de Salvador.”** Malha errada — a colocação é por cidade. **Corte:** 1 linha por cidade, com nota quando o ranking for da capital. + +## Questões em aberto (produto) + +Resolvidas no gate (2026-08-09): + +- **Escopo da resposta:** só 1º lugar + “até a posição X” (top-N) como parâmetro. **Decisão:** 1º default, `top-N` aceito. +- **Flexibilidade de candidato:** só o nosso vs **qualquer deputado federal**. **Decisão:** qualquer deputado federal (número/nome), default = candidato da campanha. +- **Links na resposta:** híbrido (proativo em entidade singular + sempre sob pedido), seguindo o padrão B162. **Decisão:** híbrido. +- **Menção de Salvador:** citar a colocação da capital como cidade quando a pergunta for comparativa/sobre ela; caso contrário só aparece se estiver na lista. **Decisão:** menção contextual, como cidade única. + +## Referências + +- Canvas UI (gate): N/A +- `src/lib/bahiaElectionAggregates.ts` (rank competitivo por cidade, do candidato) e `src/lib/bahiaMunicipalityCodes.ts` (IBGE↔nome) +- [`ranking-votos-municipio.md`](ranking-votos-municipio.md) · [`sollinha-tool-urls-navegacao.md`](sollinha-tool-urls-navegacao.md) · [`ai-chat-sollinha.md`](ai-chat-sollinha.md) +- `src/utilities/ai/tools/`, `src/utilities/ai/systemPrompt.ts` +- AGENTS.md — leader lockdown, leitura relativa/local (docs/research kernel) diff --git a/docs/plans/sollinha-genero-masculino.md b/docs/plans/sollinha-genero-masculino.md new file mode 100644 index 00000000..b770e962 --- /dev/null +++ b/docs/plans/sollinha-genero-masculino.md @@ -0,0 +1,75 @@ +# B179 — Sollinha: persona no gênero masculino (prompt + referências .md) + +Status: plano — registrado (blocked até plano em main) +Atualizado em: 2026-08-09 +Issue: #462 +Priority: P2 +Model: composer-2.5 +Model-local: deepseek-v4-flash-high +Impeccable: A — N/A (édito de texto/persona; sem superfície visual tocada) +Canvas UI: N/A — sem UI +Appetite: ~0,25–0,5 dia eng; texto de persona; sem migration / Consent / collection +Responsável: — + +## Intenção + +O usuário observou que o Sollinha **às vezes se refere a si mesmo no feminino** — e deveria falar de si no masculino (nome é diminutivo de Jorge Solla; persona percebida como masculina). A causa provável está na definição da voz (o prompt de sistema abre como “Você é **a** Sollinha, assistente virtual…”) e em documentos .md que descrevem a persona no feminino (“**a** Sollinha”). Decisão de produto no gate: corrigir o **texto** (runtime + referências editáveis), sem guarda automatizada — basta o édito de conteúdo. + +## Persona e fluxo + +- **Persona / contexto:** não é nova persona — o Sollinha existente; o “fluxo” é a própria conversa (qualquer pergunta em `/campanha`). +- **Job principal:** o Sollinha se apresentar e se referir a si **no masculino** (ex.: “sou o Sollinha”, “estou à disposição”) — sem renomear o produto. +- **Fluxo desejado:** abrir o chat → primeira mensagem/tom → o Sollinha usa flexões masculinas de primeira pessoa de forma estável. +- **Anti-goals de produto:** + - Não renomear o produto (o nome “Sollinha” continua — só o gênero da voz muda). + - Não reescrever planos entregues/frozen (histórico imutável — ver referências). + - Não mudar tom/formalidade (a entrega é só gênero). + - Sem guarda automatizada (decidido no gate). + +## Objetivo e aceite + +- O **prompt de sistema** (fonte real da voz) define “Você é **o** Sollinha” e inclui regra explícita: referir-se a si no masculino na primeira pessoa, nunca flexionando adjetivos/particípio no feminino sobre si. +- As referências .md **editáveis** (rascunhos/ready desta família) usam “o Sollinha”; novos planos/blocos também. +- Verificação: conversa de exemplo apresenta “sou o Sollinha” / flexões masculinas; sem regressão das tools/RBAC (nenhuma lógica muda). + +## Dados (intenção) + +- **Vou apresentar dados?** N/A — sem números; é persona/édito de texto. + +## Direção no codebase (hipótese) + +- **Áreas prováveis:** `src/utilities/ai/systemPrompt.ts` (a voz nasce aqui — não é .md, mas é onde a mudança tem efeito real no runtime); docs/planos da família Sollinha **não-frozen** (ex.: [`sollinha-tool-urls-navegacao.md`](sollinha-tool-urls-navegacao.md), [`sollinha-cidades-mais-votado.md`](sollinha-cidades-mais-votado.md)); sem tocar teste de convenção (fora do escopo decidido). +- **Precedente:** [`ai-chat-sollinha.md`](ai-chat-sollinha.md) (v1 — frozen, não editar). +- **Risco de acoplamento:** não “caçar” “a Sollinha” em planos entregues (frozen — imutáveis); o que é **delivered/histórico** fica como está e conta como referência de época. + +## Dependências + +- Nenhuma. Soft: chore independente da família Sollinha (B162/B177/B178 não dependem dele). + +## Fora de escopo + +- Renomear o produto / mudar identidade visual. +- Reescrever planos e documentos **entregues** (“a Sollinha” histórico fica como está; o que vale é a voz no runtime e o que se escreve daqui pra frente). +- Mudanças de tom, formalidade, humor ou demais traços de voz. +- Guarda automatizada / teste de convenção (decidido no gate: só édito de texto). +- Tocar tools, RBAC, rate limit ou fluxo do chat. + +## Rabbit holes de produto + +- **“Reescrever todo histórico de docs da Sollinha.”** Planos entregues são congelados (regra do repo). **Corte:** runtime + docs editáveis; histórico permanece. +- **“Ajustar o modelo/empresa de LLM para mudar o gênero.”** A voz vem do prompt. **Corte:** prompt basta; se ainda houver deriva, reavaliar com exemplo gravado (registro no plano, sem projeto). + +## Questões em aberto (produto) + +Resolvidas no gate (2026-08-09): + +- **Além do gênero, ajustar outro traço de voz agora?** **Decisão:** só gênero (foco no relato observado). +- **Vale guarda automatizada contra regressão do feminino?** **Decisão:** **não** — só édito de texto (conforme pedido). +- **O nome visível do produto muda?** **Decisão:** não — só o gênero da voz. + +## Referências + +- Canvas UI (gate): N/A +- `src/utilities/ai/systemPrompt.ts` (linha 1: “Você é a Sollinha…”) +- Planos da família: [`ai-chat-sollinha.md`](ai-chat-sollinha.md) (frozen), [`sollinha-tool-urls-navegacao.md`](sollinha-tool-urls-navegacao.md) (ready/editable), [`sollinha-cidades-mais-votado.md`](sollinha-cidades-mais-votado.md) (novo) +- AGENTS.md — convenções de naming/idioma (rótulos visíveis em pt, identificadores em inglês); regra de planos frozen