teqo-backup/docs/plans/motor-de-sugestoes.md
Francisco Ignácio Fontoura Solla adc0f31f5b Sync all project docs with the deployed remodel and hardening; compile roadmap
- Compile docs/roadmap.md into a next-steps document (277→206 lines): R0–R5/M1–M5
  tables and the delivered map/list item paragraphs compressed into 'Já entregue';
  janelas, grafo and cortes list only open IDs; C10/C11/C8 fill-ins keep their
  original triggers; C5 moved under 'A validar'.
- Record the production deploy of the destructive remodel chain (2026-07-23 Vercel
  builds) across roadmap, AGENTS.md, the project notebook and plans — supersedes
  every 'deploy pendente'; smoke pós-deploy + onboarding remain the open blockers.
- Fix AGENTS.md internal contradictions: Município locked-decisions bullet (435,
  Camaçari whole, candidate role), campaignUser role enum, deploy-checklist item 7
  as historical, access/* structure note, election-aggregates artifact +
  election-tse revalidate runbook.
- Sync open plans to real identifiers (plaza*/nucleus* → municipality*): the
  intelligence program (master + E8–E15/B13/C12 with artifact/caching context and
  satisfied deps), B8 rescoped to Salvador ZE 1–19 only, WhatsApp D3–D5, D2, A6
  (now the insight layer over stateDeputy), RS+, onda-0 smoke step.
- Close items verified done in code: C8 F4 (DRY de forms) complete, filtros-auto
  shared pending shell, petition generateStaticParams select/depth; ledger the
  surviving user-visible 'Praça' copy in src as new P2 debt.
- Banner delivered praça-era plans (production note + rename pointer); mark
  remodelagem-pracas.md superseded and remodelagem-municipios.md delivered.
- TECH-DEBT smell statuses, TESTING RBAC row, README quality gates, campanha
  UI rules updated to current component names.
2026-07-24 02:04:45 -03:00

8.8 KiB
Raw Blame History

E11 — Motor de sugestões v1 (dado → decisão com humano no loop)

Status: rascunho Atualizado em: 2026-07-24 (refs sincronizadas pós-remodelagem Municípios + hardening) Item do roadmap: docs/roadmap.md (seção "Inteligência de campanha", E11; plano-mestre inteligencia-campanha.md) Impeccable: C — superfície nova de sugestões (painel/fila em /campanha), sem design-ref Appetite: ~3 dias eng; sem migration própria (lê E8/E10/E14 e grava via allocationDecision de C12) Responsável: —

Design (Impeccable)

Âncoras: PRODUCT.md (princípios 2, 5 e 7) / DESIGN.md (register product) · CampaignPageShell/CampaignMetricStrip · precedente de stack de insights (Alert + badge) do detalhe do município.

Na implementação: shape → craft → critique → polish (classe C).

Brief compacto:

  • Persona / contexto: coordenador abrindo o Teqo segunda de manhã ("o que mudou? onde ajo?"); assessor com 20 municípios recebendo as checagens baratas da semana.
  • Job principal: o grosso da análise chega pronto — municípios com gatilho, diagnóstico diferencial e menu de ações; o humano escolhe, e o descarte também é registro.
  • Estratégia de cor: Restrained; urgência via posição na triagem (níveis 1–5), não via alarme colorido — só o nível 1 usa o vermelho de campanha.
  • Edit where you see: sim — aceitar/adiar/descartar direto no card da sugestão; descarte exige leitura alternativa (1 campo).
  • Anti-goals: sugestão executada automaticamente; feed infinito estilo rede social; score numérico com falsa precisão ("87% de chance"); expor limiares exatos na UI (gaming — G4).

Contexto

Relatório §6: catálogo de 25 padrões dado→decisão (P/T/I/J/K) com triagem (§6.2), divisão análise×julgamento (§6.3 — máquina detecta e diagnostica o estrutural; humano escolhe/executa) e modos de falha com mitigação (§6.4 — falso positivo de registro, viés da base, gaming, oráculo, churn). Pós-E8/E10/E14/C12, todos os insumos dos 8 padrões de maior confiança existem: P1 (reduto dormente/ameaçado), P2 (ataque campo forte), P3 (correligionário na frente — runningAgain2026 dá a herança aberta), P5 (expansão acima do padrão), P6 (município grande sem rede), P7 (pledges estagnados), K-A (não responde a investimento), K-B (perdida vestida de oportunidade). A "pauta do silêncio" (municípios sem gatilho nenhum, revisados como pergunta mensal) integra a v1 por ser barata e antídoto do viés da base.

Objetivos

  • Avaliador de gatilhos puro e determinístico: município + derivados (E8/E10/E14) + sinais/frescor (C12) → lista de TriggeredPattern (patternId, fatores, nível de triagem 1–5, leitura provável, descartes, menu de ações com estatuto/fonte, contraindicação) — conteúdo textual vem de um catálogo versionado em código espelhando o relatório §6.
  • Superfície: painel "Sugestões" no dashboard (top-N por triagem) + seção no município (gatilhos dela); visão do assessor filtrada às seus municípios com as checagens baratas ("checar rede em 48h") destacadas.
  • Ações: aceitar (vira nota/encaminhamento e grava allocationDecision com snapshot), adiar (some por X dias), descartar (exige alternativeReading); tudo transacional.
  • Regras anti-falha (§6.4): gatilho de ausência gera "auditar registro", nunca ação de campo; histerese — sugestão de realocação cara só aparece com persistência de 2 avaliações semanais (persistência via snapshot em allocationDecision de avaliações, sem tabela nova: recomputa e compara com a última decisão/adiamento registrado); limiares nunca renderizados com número exato.
  • Pauta do silêncio: lista mensal dos municípios N2+ sem nenhum gatilho e sem sinal — "silêncio é pergunta, não conforto".
  • Access: staff; visão do assessor escopada (getAccessibleMunicipalityIds); leader não vê nada disso.

Decisões travadas

  • Motor é avaliador em leitura, não job persistido. Roda no load (dados de 435 municípios já vêm no bundle A9+); persistência só de DECISÕES (C12). Rejeitado: cron + tabela de sugestões (estado duplicado que dessincroniza; nada aqui exige assincronia); LLM gerando sugestões em runtime (indeterminístico, não auditável — o catálogo é curado em código a partir do relatório).
  • 8 padrões na v1, catálogo fechado. Rejeitado: 25 padrões de uma vez (metade depende de semanas de sinais C12; falso positivo mata a confiança na primeira semana — E1 do relatório).
  • Descartar exige leitura alternativa. É o antídoto metodológico do oráculo (§6.4) e o dado que o backtest precisa. Rejeitado: descarte de 1 clique sem motivo.
  • i18n e naming: evaluateMunicipalityTriggers, TriggeredPattern, triageLevel, suggestionCatalog; patternIds estáveis (P1…K-B); textos pt-BR citando estatuto ("hipótese de literatura — relatório §6.1").

Questões em aberto

  • Onde mora a superfície principal? Opções: painel no dashboard | rota /campanha/sugestoes | aba na lista de municípios. Recomendação: painel no dashboard (top-5) + seção completa no detalhe do município; rota dedicada só se o volume real pedir (adiado com gatilho). (shape na implementação decide o layout final)
  • Adiamento por quanto tempo? Recomendação: 7 dias default, 14 para nível 4–5; registrado como decision: adiada.

Abordagem proposta

flowchart LR
    Bundle["loadMunicipalityListPageBundle + derivados E8/E10/E14"]
    Signals["sinais + frescor (C12)"]
    Cat["suggestionCatalog<br/>(8 padrões, texto curado)"]
    Eval["evaluateMunicipalityTriggers<br/>(puro, determinístico)"]
    Triage["triagem 1–5 + histerese<br/>(vs. última allocationDecision)"]
    UI["Dashboard painel + seção do município<br/>(aceitar/adiar/descartar)"]
    Dec["allocationDecision (C12)"]
    Bundle --> Eval
    Signals --> Eval
    Cat --> Eval
    Eval --> Triage --> UI --> Dec
    Dec -.histerese/adiamentos.-> Triage

Componentes:

  • src/lib/suggestionCatalog.ts: os 8 padrões com gatilho (função pura sobre derivados), textos (leitura/descartes/menu/contraindicação com estatuto) e nível de triagem — espelho versionado do relatório §6.1.
  • src/utilities/municipalityTriggers.ts: avaliação por município + agregação/ordenação por triagem; consulta últimas allocationDecision para histerese/adiamento (1 query por load, where em municipality in […]).
  • UI: SuggestionCard/SuggestionPanel em src/components/campaign/ (compõe Alert/Badge existentes do stack de insights); painel no dashboard (campaignDashboardData.ts estende o load); seção no detalhe do município.
  • Action: recordAllocationDecision (C12) reusada; wrapper resolveSuggestion valida alternativeReading no descarte.
  • Sem migration.

Dependências

  • Duras: E8, E10, C12; E9 (a fila de colunas é onde o coordenador confere o contexto da sugestão). Suaves: E14 (nível no snapshot e em K-B→N0/N1), dados reais da Onda 0 (motor é inútil com municípios vazios — smoke com dados fictícios apenas).
  • Reusa: bundle A9+, campaignDashboardData.ts, campaignAccess.ts, withPayloadTransaction.

Não escopo

  • Padrões T (TI — E12), I/J (alienação/agenda — fase 2), G2-hierarquia completa entre municípios de níveis diferentes além da triagem 1–5; notificação push (D2); qualquer automação de ação.

Rabbit holes

  • Catálogo virar motor de regras genérico (DSL, editor de regras). 8 funções puras nomeadas bastam; DSL é plataforma white-label, não este ciclo.
  • Histerese exigir tabela de avaliações. Comparar com a última allocationDecision/adiamento cobre a v1; se falhar na prática, aí sim revisitar persistência.
  • Explicabilidade virar relatório longo. Card mostra 2 fatores + estatuto + link "por quê" (relatório §6.1); não gerar texto por município.

Adiado com gatilho

  • Fase 2 do catálogo (T1–T5, I-A/I-B/I-C, J-A/J-B/J-C, P4/P8–P12, K-C). Gatilho: 2 semanas de uso real + primeiras 20 allocationDecision.
  • Rota dedicada /campanha/sugestoes. Gatilho: >15 sugestões ativas recorrentes tornando o painel do dashboard insuficiente.
  • Digest semanal (e-mail/push). Gatilho: D2 entregue.

Referências

  • docs/roadmap.md (Inteligência de campanha, E11) · plano-mestre (fila canônica, G4-mitigações)
  • docs/research/relatorio-entrevista-persona-campanha.md §6.1 (padrões), §6.2 (triagem), §6.3 (divisão análise×julgamento), §6.4 (modos de falha)
  • src/utilities/municipalityPageData.ts, src/utilities/campaignDashboardData.ts, src/utilities/campaignAccess.ts, src/collections/ElectionCandidate.ts (runningAgain2026)
  • PRODUCT.md princípio 5 / DESIGN.md — âncoras da superfície
  • AGENTS.md — access, transações, naming