No ar · coleta até 01/11 dados de 30/10/2026, 16:02
PROJETO · HISTÓRICO DE VERSÕES

Versões

Todas as mudanças importantes do projeto, em ordem reversa cronológica. Formato segue Keep a Changelog; versões em SemVer.

Histórico de versões

Registro cronológico de tudo que mudou no painel — dados, metodologia e interface —, da versão mais recente para a mais antiga.

Página 10 de 14 · 167 versões no total.

  1. v4.11.1 2026-07-24 Painel muito mais rápido

    Fixed

    • Páginas que demoravam segundos agora abrem instantaneamente. O Cenário Geral levava cerca de 6 segundos em toda visita (não só na primeira), e o Comparativo chegava a quase um minuto depois de cada coleta. Agora as páginas do painel respondem em frações de segundo — na prática, a navegação deixa de ter espera perceptível.
    • Fim da lentidão logo após cada atualização de dados. O painel guarda os resultados prontos em memória para não repetir o mesmo trabalho a cada visita, e essa memória é naturalmente descartada quando entram dados novos (3× por dia). Quem visitasse primeiro pagava a conta. Agora o próprio processo de coleta já deixa as páginas preparadas ao terminar, então esse custo não recai mais sobre quem acessa.
    • A Home não depende mais do blog para carregar. A lista de análises publicadas era buscada no site a cada visita, o que somava tempo e travava a página quando o blog estava lento. Passa a ser atualizada junto com os dados do painel.

    Notes

    • Medições na LXC (acesso público, via Cloudflare): Cenário Geral 6,3s → 0,38s; Comparativo até 58s → 0,48s; Pré-candidatos 2,3s → 0,30s; Espectro 2,4s → 0,39s; Home 1,1s → 0,39s.
    • As melhorias são de infraestrutura: nenhum número, gráfico ou critério de análise mudou.

  2. v4.11.0 2026-07-20 Renan Santos: primeiro candidato oficializado

    Changed

    • Renan Santos (Missão) promovido de pre-candidate para candidate — oficializado em convenção partidária (formato online, antecipada por razões de segurança) em 20/07/2026, tornando-se o primeiro candidato à Presidência com candidatura homologada da corrida de 2026. Empresário e ativista, fundador do Movimento Brasil Livre (MBL) em 2014. Anunciou chapa "puro-sangue" (candidato e vice do mesmo partido); lançamento oficial da campanha marcado para 01/08/2026. Fontes: O Tempo, Correio Braziliense e Gazeta do Povo, 20/07/2026. status_changed_at = 20/07/2026. Registros históricos preservados; segue aparecendo em todos os grids (o filtro de dados é status != 'dropped').

    Added

    • Selo "Candidato" nas páginas de pré-candidatos — o grid /pre-candidatos passa a exibir um badge verde "Candidato" nos cards de quem já teve a candidatura oficializada, e a página individual troca o rótulo do cabeçalho de PAINEL · PRÉ-CANDIDATO para PAINEL · CANDIDATO. A Metodologia já distinguia os três status (Pré-candidato / Candidato / Descontinuado). Preparação para o período de convenções (agosto), quando mais nomes migram de pré-candidato para candidato.

  3. v4.10.1 2026-07-10 Aécio Neves dropped

    Changed

    • Aécio Neves (PSDB) marcado como status='dropped' no banco — desistiu da pré-candidatura à Presidência em 09/07/2026, citando isolamento e ausência de alianças ("Não vou fazer uma travessia solitária"). A pré-candidatura havia sido aprovada pela federação PSDB/Cidadania/Solidariedade em convenção de junho; o PSDB não lançará candidato próprio e tende à neutralidade entre Lula e Flávio Bolsonaro, mirando projeto de terceira via em 2030. Fontes: g1 e O Globo, 09/07/2026. Status alterado em 10/07/2026 (status_changed_at retroativo à data do anúncio).
    • Coletores (Brave/Google News/Tavily) passam a ignorar Aécio automaticamente a partir da coleta das 14h de 10/07 (registros históricos preservados no banco).
    • README atualizado: Aécio movido para "Descontinuados"; lista de candidatos sincronizada com o estado real do banco (Ciro Gomes constava como ativo desde o drop de 09/05; Michelle Bolsonaro constava como descontinuada apesar da reativação de 13/05; Edmilson Costa e Joaquim Barbosa não constavam).
    • 📝 Metodologia: 14 pré-candidatos ativos + 5 dropped (Aécio Neves, Ciro Gomes, Eduardo Leite, Leonardo Péricles, Tarcísio de Freitas).

  4. v4.10.0 2026-07-07 Alinhamento ao Design System (DS 1.6.0)

    Fixed

    • Tipografia dos títulos restaurada — os valores em destaque (KPIs, nomes de card) referenciavam um token de fonte inexistente e caíam em Archivo Narrow em vez de Antonio. Agora os números grandes voltam à fonte editorial condensada correta.
    • Números e datas em formato brasileiro — placares de sentimento e percentuais que apareciam com ponto decimal (+0.35, 12.3%) passam a usar vírgula (+0,35, 12,3%); contagens grandes usam ponto de milhar de forma consistente.
    • Linha "Total" da timeline visível no tema escuro — era desenhada na mesma cor do fundo e sumia; agora usa um cinza neutro legível nos dois temas.

    Changed

    • Cores unificadas com a frota SapiensLabs — o espectro editorial e as cores de sentimento passam a vir dos tokens compartilhados do Design System (mesma paleta dos monitores da Câmara e do Senado), com variantes de contraste próprias para o tema claro. O azul de marca deixa de ser reaproveitado como cor de "direita".
    • Gráficos na camada Plotly canônica — fonte Archivo Narrow, separadores numéricos brasileiros e paletas alinhadas ao DS; os gráficos passam a acompanhar a troca de tema claro/escuro.

    Notes

    • Página de Privacidade ajustada para descrever o comportamento real do tema (segue o sistema operacional e o horário; nada é gravado no navegador por conta do painel).

  5. v4.9.0 2026-06-30 Consistência de contagem e endpoints de agregação

    Fixed

    • Janela temporal unificada com DATE() — published_at é ISO com T e timezone (2026-06-07T14:00+00:00); comparar a borda superior por string (<= to) excluía o dia final inteiro porque 'T' (0x54) > ' ' (0x20). /api/v1/articles e a busca de artigos relacionados passam a usar DATE(published_at), incluindo o dia final. Antes, /articles retornava menos artigos que a série de sentimento para a mesma janela (ex.: Lula 01–07/06: 1378 vs 1478 — os 100 artigos do dia 07).
    • Série de sentimento respeita from/to — /api/v1/candidates/{slug}/sentiment aceitava os parâmetros mas os ignorava (devolvia sempre a série desde 25/03). Agora filtra a janela com DATE().
    • Tópicos do Comparativo filtram menção verificada — as agregações de tópicos em /comparativo (grafo bipartite + colunas por candidato) contavam tópicos de artigos não-verificados, divergindo do resto do dashboard. Agora exigem article_candidates.verified = 1.

    Added

    • GET /api/v1/candidates/{slug}/topics — top tópicos sobre um candidato na janela (from, to, limit), artigos únicos verificados.
    • GET /api/v1/candidates/{slug}/sources — top veículos sobre um candidato na janela. Tornam verificáveis publicamente as afirmações dos resumos sobre tópicos e fontes por período.

    Notes

    • Métrica oficial de volume: artigo único (COUNT(DISTINCT a.id)) com verified = 1, janela por DATE(published_at). Os produtores de resumo (semanal/mensal, no projeto blog-automation) foram alinhados a essa métrica — antes contavam pares (COUNT(*)) sem verified, o que inflava os números publicados frente à API que citam como fonte.

  6. v4.8.1 2026-05-22 Aécio Neves (Cidadania)

    Added

    • 🗳️ Novo pré-candidato: Aécio Neves (Cidadania) — Ex-governador de Minas Gerais (2003-2010, PSDB), ex-senador, deputado federal pela quinta vez (eleito em 2022). Candidato à Presidência em 2014 pelo PSDB, perdeu o segundo turno para Dilma Rousseff. Migrou para o Cidadania, que aprovou sua pré-candidatura à Presidência em reunião da Executiva Nacional em 22/05/2026. Coleta dedicada começa na próxima rodada do pipeline. Patterns + queries (Brave, Tavily, Google News) adicionados em src/candidate_utils.py e src/coletores/google_news.py.

  7. v4.8.0 2026-05-17 Fonte mais quente da semana + categoria Agregador

    Duas mudanças que se sustentam mutuamente. O card "KPI da Semana" na Home ganha uma quarta linha noticiosa — qual veículo publicou mais sobre a eleição na janela; e a taxonomia editorial ganha uma sexta categoria, Agregador, pra distinguir fontes não-redacionais das redações com linha editorial humana.

    Added

    • 🏷️ Card "Fonte mais quente" na Home — quarta estatística do bloco semanal, mostrando o veículo com maior volume na janela atual (segunda a domingo) e variação vs. semana anterior. Filtra explicitamente para fontes editorialmente classificadas (editorial_lean_detail IN (extrema-esquerda, esquerda, centro, direita, extrema-direita)); agregadores e fontes não mapeadas ficam fora do ranking por desenho. Backend: _top_source_for_window() em data.py, integrado ao retorno de cenario_semanal_kpis().
    • 🆕 Categoria editorial "Agregador" (agregador detail / aggregator broad) — nova dimensão paralela ao eixo esquerda↔direita, descrevendo a natureza da fonte (não-redacional, reescrita automatizada / IA, sem linha editorial humana atribuível). Critérios objetivos documentados em CLASSIFICACAO.md: sem expediente, sem bylines, volume incompatível com redação humana, maioria das matérias sem atribuição, indicadores linguísticos de LLM sem disclosure. Primeira fonte classificada: Notícias do Brasil (noticias-do-brasil.news).

    Changed

    • 🔁 noticias-do-brasil recategorizado — antes classificado como center/centro (default arbitrário do cadastro RSS), agora marcado como aggregator/agregador. Aplicado retroativamente a 1280 artigos no banco (privado + público) e à entrada do feed em config/rss_feeds.json pra próximas coletas. Fonte continua sendo coletada — só sai das KPIs editoriais.
    • 🔧 source_lean_map.py: DETAIL_TO_BROAD ganha entrada agregador → aggregator; nova seção "AGREGADOR" no SOURCE_LEAN_MAP_DETAIL com as variantes de nome/host de Notícias do Brasil.
    • 🔧 refresh_editorial_lean_detail.py: BROAD_TO_DETAIL_FALLBACK ganha aggregator → agregador.

    Notes

    • Por que o critério "Agregador" descreve natureza, não alinhamento. As 5 categorias do eixo editorial (extrema-esquerda → extrema-direita) pressupõem decisão humana de linha. Aplicar uma delas a um site que reescreve via LLM matérias de terceiros era um erro categorial: rotulava como "centro" qualquer agregador que cobrisse o noticiário consensual sem viés panfletário. A categoria nova é o vocabulário que faltava.
    • Critérios formais do eixo esquerda↔direita continuam como pendência aberta. Esta versão adiciona uma 6ª categoria, mas não revisa as 5 originais. O fato de o rss_feeds.json ter campos lean atribuídos manualmente sem documentação é o sintoma mais visível dessa dívida.

  8. v4.7.0 2026-05-16 Ficha por matéria + Joaquim Barbosa (DC)

    Duas adições estruturais. Cada link de matéria nas listas do dashboard agora abre uma ficha analítica interna que mostra tudo que o banco tem sobre ela; e Joaquim Barbosa entra como pré-candidato do Democracia Cristã, em movimento que disputa internamente com a pré-candidatura de Aldo Rebelo no mesmo partido.

    Added

    • 📰 /cobertura/{id}/{slug} — ficha analítica por matéria. Cada link de matéria nas listas (Cobertura mais recente, Artigos da semana/mês, listagem na página de pré-candidato) passa a abrir uma página interna com alinhamento editorial do veículo, gênero jornalístico, cluster Zeitgeist, tópicos identificados, sentimento por pré-candidato (tabela) e até 5 matérias relacionadas (janela ±3 dias, score por mesmo cluster + co-ocorrência de candidatos). CTA proeminente "Ler matéria completa em [veículo]" no topo e <link rel="canonical" href="{url_original}"> no head pra respeitar a fonte. Open Graph completo pra compartilhamento. Backend: data.article_detail(id) + slugify NFKD ASCII (kebab, max 60 chars cortado na palavra).
    • 🗳️ Novo pré-candidato: Joaquim Barbosa (DC) — Ex-ministro do STF (2003–2014), primeiro negro a presidir o Supremo. Relator da AP 470 (Caso Mensalão). Aposentou-se voluntariamente em 2014. Filiou-se ao Democracia Cristã em maio/2026; cotado pelo partido como nome para disputar a Presidência em 2026, em movimento que se sobrepõe à pré-candidatura de Aldo Rebelo lançada pelo mesmo DC em 07/05/2026 (disputa interna documentada por Carta Capital, Poder360, Revista Fórum e DCM). Coleta dedicada começou na rodada das 21:00 BRT de 16/05.
    • 🔍 10 artigos retroativos sobre JB que já estavam no banco (coletados por menção cruzada em queries do Aldo) foram associados ao novo candidato e classificados pelo sentimento.py (10/10 positive — tom favorável da cobertura sobre a filiação ao DC). Tópicos e Zeitgeist já estavam preenchidos.

    Changed

    • 🔄 Listas de artigos refatoradas pra usar partial reutilizável (partials/article_list_item.html) — 9 blocos duplicados em 5 templates consolidados em 1 só. Listas passam a linkar pra ficha interna (internal_url) em vez de direto pro veículo; link original preservado dentro da ficha.
    • 📊 5 funções de listagem em data.py retornam id + internal_url — recent_articles, _articles_for_window (semanal/mensal), candidate_detail.articles, emerging_topics.examples. Esquema consistente: toda lista de matérias do banco entra na navegação interna.
    • 📝 Metodologia ganhou seção dedicada à ficha por matéria.

    Notes

    • Lacuna nas queries dos coletores corrigida no mesmo dia — descobriu-se que adicionar um pré-candidato exige 5 mudanças (INSERT em candidates + patterns em CANDIDATE_PATTERNS + queries em BRAVE_QUERIES/TAVILY_QUERIES/GNEWS_QUERIES), não só o INSERT. Comentário inline em src/candidate_utils.py já documentava isso desde sempre. Procedimento agora rodado integralmente pro JB; checklist registrado em memória curada.
    • rel=canonical das fichas aponta pra fonte original — decisão editorial de respeito ao trabalho dos veículos cobertos. O painel não disputa SEO da palavra-chave; a ficha agrega valor analítico próprio.

  9. v4.6.1 2026-05-12 Migração Gemini 3.1 Flash Lite para GA

    Changed

    • gemini_client.py: model ID atualizado de gemini-3.1-flash-lite-preview para gemini-3.1-flash-lite (GA). Mesmo modelo, só o identificador mudou — sem alteração de comportamento nem de custos. Preview descontinuado pelo Google em 25/05/2026.

  10. v4.6.0 2026-05-10 Edmilson Costa (PCB) entra no monitoramento

    Adição tardia de pré-candidato presidencial: Edmilson Costa, lançado pelo Comitê Central do PCB em 24/02/2026 com slogan "Por um governo do Poder Popular". A imprensa já vinha tratando-o como pré-candidato em listas de pesquisa eleitoral (Meio/Ideia, Real Time Big Data) — a inclusão regulariza o registro no painel.

    Added

    • 🗳️ Novo pré-candidato: Edmilson Costa (PCB) — Doutor em Economia pela Unicamp, com pós-doutorado no IFCH/Unicamp. Militante histórico do PCB, perseguido na ditadura militar como líder estudantil.
    • 🔍 Busca retroativa: 11 artigos existentes vinculados ao novo candidato na tabela article_candidates (todos como menção em listas de pesquisa eleitoral; nenhum dedicado).
    • ⚙️ Coleta futura configurada: 3 patterns regex, 5 queries Brave e 3 Tavily em candidate_utils.py. Patterns conservadores (sem \bcosta\b solto — sobrenome muito comum).

    Changed

    • 📝 Metodologia: 12 pré-candidatos ativos (era 11) + 5 dropped (Ciro Gomes, Eduardo Leite, Leonardo Péricles, Michelle Bolsonaro, Tarcísio de Freitas).

  11. v4.5.0 2026-05-10 Bundle Plotly custom (1.2MB vs 4.6MB)

    Bundle Plotly trocado do full distribution (4.6MB) para bundle custom (1.2MB, -73%) com apenas os 5 trace types que o dashboard de fato usa: Bar, Heatmap, Scatter, Scatterpolar, Treemap. Constructed via esbuild sob frontend/.

    Added

    • frontend/ — toolchain JS isolada do projeto Python:
      • package.json — declara plotly.js@3.0.1 + esbuild@^0.24.0 como devDeps
      • plotly-bundle.js — entry point que importa plotly.js/lib/core + traces individuais via Plotly.register([Bar, Heatmap, Scatter, Scatterpolar, Treemap])
      • global-shim.js — shim ESM injetado no bundle pra polyfillar globalThis.global (fallback se runtime shim falhar)
      • npm run build → esbuild gera app/static/js/plotly-custom-3.0.1.min.js
    • Runtime shim em base.html — <script>window.global = window;</script> ANTES do bundle. Plotly v3 tem código que referencia bare global dentro do IIFE, fora do alcance do --define/--banner/--inject do esbuild. O shim no escopo window resolve qualquer global via scope chain. 1-linha, robusto, dispensa re-build a cada mudança.

    Changed

    • base.html — referência muda de plotly-3.0.1.min.js (4.6MB) pra plotly-custom-3.0.1.min.js (1.2MB).
    • app/static/js/plotly-3.0.1.min.js removido do repo (não é mais usado).

    Notes

    • Validação no Chrome: 6 traces em /comparativo (bar + scatter), 1 em /radar (scatterpolar), 4 em /cenario-geral (scatter + heatmap + treemap). Zero erros console.
    • Manutenção pós-upgrade Plotly: bumpar versão em frontend/package.json, cd frontend && npm install && npm run build, atualizar nome do arquivo em base.html (carrega versão no nome pra cache-bust). Documentado em frontend/plotly-bundle.js.
    • Tentativas falhadas antes do shim runtime: --define:global=globalThis (substituição textual não pegou todos os usos), --banner:js="var global=..." (sombreado pela IIFE), --inject:./global-shim.js (módulo importado mas escopo isolado). Conselho do advisor: shim no window antes do script é mais simples e robusto.

    Performance

    • Tamanho bruto: 4.6MB → 1.2MB (-73%)
    • First-visit em 4G médio (5Mbps efetivo): ~7.5s → ~2s (economia ~5.5s)
    • First-visit em 100Mbps: ~370ms → ~100ms (economia ~270ms)
    • Subsequent visits: zero (browser cache + CF cache 4h em ambos os casos)

  12. v4.4.0 2026-05-10 HTMX em filter changes (4 rotas)

    Filter changes em /comparativo, /radar, /espectro e /cenario-geral deixam de fazer full page reload e passam a fazer fragment swap via HTMX. Form ganha auto-trigger no change (debounce 300ms) — não precisa mais clicar "Atualizar". Classe ativa das pills do /cenario-geral é re-renderizada junto com o partial.

    Added

    • HTMX 2.0.4 self-host em app/static/js/htmx-2.0.4.min.js (51KB, mesmo motivo do Plotly: evita DNS/TLS pra CDN, reaproveita HTTP/2 do mesmo domínio).
    • Listener global em base.html que re-executa <script> inline injetado por swap. Browsers não executam scripts que entram via innerHTML — o listener detecta htmx:afterSwap e re-cria cada <script> como nó novo (browser executa nós recém-injetados). Chave para fazer Plotly funcionar pós-swap (pio.to_html() injeta <div> + <script>Plotly.newPlot()</script> inline).
    • Partials por rota em app/templates/partials/:
      • comparativo_results.html — tabela resumo + 7 charts + top tópicos
      • radar_results.html — spider chart + tabela de valores brutos
      • espectro_results.html — KPIs por espectro + 4 charts
      • cenario_geral_results.html — pills + KPIs + 5 charts + lista de artigos
    • Detecção de HX-Request header nas 4 rotas — se presente, retorna partials/<rota>_results.html; se ausente, retorna pages/<rota>.html que {% include %} o partial. Sem duplicação de lógica de contexto. Compatível com no-JS (forms ainda submetem normalmente).

    Changed

    • Forms ganham hx-trigger="change delay:300ms" — qualquer toggle de checkbox dispara filter change após 300ms de inatividade (debounce). Botão "Atualizar" mantido como fallback.
    • Pills do /cenario-geral ganham hx-get + hx-target + hx-push-url — funcionam tanto como links normais (no-JS) quanto como swap HTMX.
    • hx-push-url="true" em todas as rotas — URL no browser atualiza junto com o swap, mantém comportamento de navegação esperado (back button, share URL, etc.).

    Notes

    • Ganho de tempo no filter change varia conforme cache do db_cached:
      • Cache hit (rota leve, ex.: /cenario-geral?period=30d segunda vez): ~700-1000ms full reload → ~150-300ms swap. Redução ~80%.
      • Cache miss (rota cara, ex.: /comparativo com seleção nova): ~8-15s full reload → ~8-15s swap (mesma carga server-side; ganho marginal de render+transfer). Mas a sensação de "responsividade" do click é melhor — UI muda instantaneamente quando a resposta chega, sem flash de white reload.
    • Todas as outras rotas (/, /zeitgeist, /cenario-mensal, /cenario-semanal, /pre-candidatos, /pre-candidatos/{slug}, /analises, /metodologia, /changelog, /privacidade) ficam intocadas — não têm filtros interativos e portanto não precisam de HTMX.