PROJETO · HISTÓRICO DE VERSÕES
Changelog
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 12 · 136 versões no total.
-
v3.5.1 2026-04-20 Timeline de cobertura sobe para logo após os KPIs Changed
- Timeline de cobertura agora aparece full-width imediatamente após os KPIs globais, antes do Export CSV e do grid de candidatos. Antes ficava como terceira coluna do hero "Esta Semana", competindo com dois cards pequenos.
- Hero "Esta Semana" agora exibe apenas "Candidato destaque" e "Sentimento da semana" lado a lado (2 colunas). Ganham mais respiro visual.
Reason
- A série temporal de cobertura é o dado contextual mais potente depois dos KPIs cumulativos — permite enxergar a evolução do volume por candidato de relance. Tê-la enfiada numa coluna lateral do hero subaproveitava o gráfico e fragmentava a leitura.
-
v3.5.0 2026-04-20 Landing focada em relevância: Cenário Geral vira a porta de entrada Changed
- Cenário Geral é a landing default. Acessar
eleicoes2026.sapienslabs.com.br/cai direto no painel de métricas; não há mais pedágio de onboarding entre o visitante e o dado. - Página Início removida. URLs antigas (
?page=Início) redirecionam automaticamente para Cenário Geral. - Nova página Sobre (última do menu, antes de Changelog): agrega o conteúdo editorial que antes vivia na Início — "o que é este painel" + disclaimer de imparcialidade, feed Análises SapiensLabs e guia de navegação das demais páginas.
- Banner fino de disclaimer no topo do Cenário Geral: "Monitoramento automatizado — não é pesquisa eleitoral" com links para Sobre e Metodologia. Dispensável via
✕durante a sessão.
Removed
- Seções da Início que duplicavam o Cenário Geral: Destaques da Semana (4 métricas), Evolução por pré-candidato (tabela), Tópicos em destaque (tags). Esses fenômenos já eram cobertos com mais profundidade no Cenário Geral (hero "Esta Semana", heatmap, tópicos emergentes com exemplos clicáveis).
Reason
- O valor editorial de um observatório de mídia está no fluxo (o que mudou), não no estoque (artigos acumulados). Submeter o usuário a uma tela de onboarding antes do dado era um atrito injustificado — especialmente para leitores retornantes. A partir desta versão, o primeiro paint já mostra candidato destaque, sentimento da semana, timeline de cobertura e tópicos emergentes.
- Cenário Geral é a landing default. Acessar
-
v3.4.2 2026-04-20 Agenda de coleta ajustada para 07:00/14:00/21:00 Changed
eleicoes-coleta.timerpassa a disparar às 07:00, 14:00 e 21:00 BRT (antes 07:00, 13:00, 20:00). Intervalo de 7h entre coletas, mantendo a frequência 3×/dia.
Reason
- Afastar a coleta do meio-dia (horário de almoço) e da noite (20h concorria com outros jobs de coleta/monitoramento). 21h dá mais folga pra consolidar o dia.
Fixed
- Timer havia sido parado manualmente em 19/04 às 15:07 (decisão temporária durante rematch de sentimento) e não foi re-iniciado, causando gap de ~28h sem coleta. Timer re-ativado junto com a mudança de agenda.
-
v3.4.1 2026-04-20 Página Perguntar temporariamente despublicada Removed
- Perguntar fora do menu lateral e da guia de navegação da Início — retirada da UI pública enquanto a camada de precisão é endurecida. Função
page_chat()emsrc/dashboard.py, CSS de.stChatInpute schema SQL documentado permanecem no código como dormant.
Reason
Testes manuais identificaram bugs incompatíveis com o posicionamento editorial do dashboard:
- Contaminação de contexto entre turnos — número de uma pergunta vazava para a próxima (ex: "Bom dia" herdava "925 registros" da anterior).
- Filtro de operações destrutivas em inglês apenas —
DELETE/DROPbloqueados, mas "apague todos os artigos sobre Lula" passava batido. - Palavras sem sentido viram SQL — typos sem substantivo reconhecível eram traduzidos em consultas arbitrárias (retornavam contagem de algo).
- Texto instrucional do prompt vazando — respostas apresentadas ao usuário incluíam literalmente "A consulta retornou N resultado(s)" apesar da instrução explícita contra.
- Off-topic aceito como pergunta de dados — questões sobre o sistema geravam respostas fabricadas inventando categorias inexistentes no schema.
Next
Bloco fundacional antes de reativar:
- Filtro destrutivo em PT (apagar/deletar/remover/excluir/limpar).
- Validação sintática do SQL gerado (exigir
FROMem tabela real do schema). - Branch explícito para saudação/smalltalk/meta — não cair em "SQL ou NAO_RELEVANTE".
- Isolamento de histórico para evitar vazamento de contexto entre turnos.
- Upgrade de modelo para
gemini-2.5-flash(ou Pro) — Flash Lite é frágil em seguimento de instruções longas. - Conjunto de testes de regressão com 15 perguntas de referência (mix de contagens, rankings, N:N, smalltalk e tentativas destrutivas).
- Perguntar fora do menu lateral e da guia de navegação da Início — retirada da UI pública enquanto a camada de precisão é endurecida. Função
-
v3.3.0 2026-04-18 Página Perguntar reativada e migrada para Gemma4 Added
- Página Perguntar de volta no menu lateral — entre Espectro Editorial e Metodologia, ícone
help-circle(Lucide). Chat com IA para consultas em linguagem natural sobre os dados do painel: volume por candidato, sentimento por fonte, tópicos por espectro editorial etc. - Guia de navegação da página Início ganha entrada para Perguntar.
Changed
- Perguntar migrada do Claude CLI para Gemma4 31B-cloud (Ollama Pro). Era o último serviço que dependia de
claude loginperiódico no LXC 121; agora usa o mesmo backend do pipeline de coleta.claude_cli.pypassa a LEGACY. - Texto na Metodologia atualizado: "IA (Claude Haiku 4.5)" → "IA (Gemma4 31B-cloud via Ollama Pro)".
- Página Perguntar de volta no menu lateral — entre Espectro Editorial e Metodologia, ícone
-
v3.2.0 2026-04-18 Tema Light Editorial Creme + picker nativo Added
- Tema Light Editorial Creme como segunda paleta oficial. Fundo papel
#f5efe2/#ebe2cf, tinta navy#1a2332, accent gold#b8874a, borda creme#d4c7a8. Contraste calibrado para texto corrido (~9:1) e texto sutil (~6:1) sobre o papel. - Picker nativo do Streamlit integrado (menu ⋮ → Settings → Choose app theme) — três opções:
Dark,LighteUse system setting. Sem toggle custom. - Dict
COLORScomputado em runtime (_COLORS_DARK/_COLORS_LIGHT) para Plotly, que não interpreta CSS variables. Cores positive/negative ajustadas para manter contraste em ambos os temas (verde#27ae60/#1d8047, vermelho#e74c3c/#b4382c). - Variantes
[theme.light]e[theme.dark]no.streamlit/config.toml— Streamlit 1.35+ mostra as duas opções no picker.
Changed
plotly_layout()helper passou a consumirCOLORS['text'],COLORS['grid'],COLORS['hover_*']em vez de#ffffffhardcoded.- Radar (spider chart + scatter de quadrante) migrado para consumir
COLORS— antes bypassava o helperplotly_layout()e ficava ilegível no light. - Cards "Candidato destaque" e "Sentimento da semana" da página Início trocaram
background:#141d27hardcoded porvar(--bg2). - Eyebrow da sidebar (
Sobre artes e técnicas) consomevar(--accent-gold)em vez de hex fixo. - Links no cream:
var(--color-yellow)remapeado para o gold editorial#b8874a(amarelo vivo#eeee22era ilegível sobre papel). - Metodologia: referências "Claude Haiku 4.5" substituídas por "Gemma4 31B-cloud (Ollama Pro)" onde aplicável. Frequência de coleta atualizada (3×/dia).
- Página Perguntar migrada para Gemma4 — era a única parte do dashboard que ainda dependia do
claude_cli.py(precisavaclaude loginperiódico, credencial expirava silenciosamente). Agora usaollama_client.call_model()como o restante do pipeline. Mesma assinatura, zero mudança de comportamento para o usuário final.claude_cli.pypassa a LEGACY.
Not changed (por decisão editorial)
- PyVis (grafo force-directed de candidatos na página Comparativo) permanece em paleta dark mesmo no Light — fica como cards escuros sobre papel creme, look editorial aceitável.
- Cores de candidatos (Lula vermelho, Flávio azul, etc.) — semânticas partidárias, idênticas nos dois temas.
- Tema Light Editorial Creme como segunda paleta oficial. Fundo papel
-
v3.1.0 2026-04-18 Design system, iconografia Lucide, coleta 3×/dia e qualidade de dados Added
- Design system compartilhado —
src/static/design-system.cssconsome tokens desapienslabs-design-system(paleta, tipografia, dividers, gradients, utilitários). Hex hardcoded nodashboard.pycaiu ~43% (193 → 110 ocorrências). - Iconografia Lucide — Novo módulo
src/icons.pycom 24 ícones SVG inline. Helperslucide()elucide_data_uri()para uso inline e viamask-image. Aplicado em menu lateral, status badges, KPI cards, hero labels, guia de navegação, badges de tópicos emergentes e botão CSV. - Taxonomia editorial de 5 níveis — Coluna
editorial_lean_detailemarticles(extrema-esquerda / esquerda / centro / direita / extrema-direita).CLASSIFICACAO.mddocumenta critérios.scripts/refresh_editorial_lean_detail.pyfaz backfill incremental após cada coleta. - Verificador anti-falso-positivo (
src/produtores/verificador.py) e classificador de gênero jornalístico (src/produtores/genero.py) rodam via Gemma4 após cada coleta. - Página Início — janela da semana explícita — KPI "Artigos esta semana" mostra o intervalo concreto (ex:
13/04 (seg) – 18/04) com caption explicando que "esta semana" = segunda a hoje (resumo semanal do blog usa recorte sexta-a-sexta).
Changed
- Coleta 3×/dia —
eleicoes-coleta.timeragora roda 07:00, 13:00 e 20:00 BRT (antes 09:00 e 21:00). Consumo Tavily projetado em ~6.300 req/mês. - Produtores em Gemma4 31B-cloud — Após teste de migração para Sonnet 4.6 no mesmo dia, revertido por custo (~R$ 470/mês). Benchmark com 6 modelos cloud do Ollama Pro confirmou Gemma4 como melhor trade-off qualidade/formato/custo.
- Página Metodologia — referências a Claude Haiku atualizadas para Gemma4 31B-cloud; frequência de coleta atualizada.
Fixed
run_coleta.sh— contadores em alerta Telegram —grep -c ... || echo '0'gerava stdout multilinha que quebrava[ "$LOCKS" -gt 0 ]. Novo helpercount_lines()sanitiza, aplicado a FPS/ERROS/LOCKS.- DB locked em Tavily —
coletores/tavily.pymigrado para padrão short-lived (uma conexão por candidato). Mantinha uma conexão aberta por ~30 min, suscetível a lock sob concorrência. - Escopo de conexões padronizado —
candidate_utils.pyagora usaget_db_connection()(timeout=30 + busy_timeout=30000 + WAL) em vez desqlite3.connect()cru.
Data quality
- Backfill de
editorial_leanem 58 fontes (unknown caiu de 25,7% para 17,1% — redução de 32%).source_lean_map.pyexpandido de ~50 para 159 entradas. - Limpeza histórica de 305 falsos positivos via
verificador.py --horas 450.
- Design system compartilhado —
-
v3.0.1 2026-04-15 Tópicos Emergentes: ordenação e desempate Fixed
- 🗓️ Exemplos de artigos mais recentes —
Tópicos Emergentesagora ordena os 3 artigos de exemplo porpub_datedecrescente antes do.head(3). Antes o widget mostrava os primeiros na ordem natural do DataFrame (poridcrescente, aproximadamente os mais antigos da semana), fazendo parecer que não havia material do dia corrente mesmo quando já havia. - ⚖️ Desempate determinístico no score —
emerging_topics.sortagora usa(score, cnt_now)como chave. Em empate de score (comum quando vários tópicos aparecem pela primeira vez com cnt_now idêntico), o tópico com maior volume ganha, em vez da ordem implícita dovalue_counts()do pandas.
- 🗓️ Exemplos de artigos mais recentes —
-
v3.0.0 2026-04-13 Dark theme + design system Changed
- 🌙 Tema escuro completo — Migração de tema claro para escuro. Fundo navy
#1a2332, sidebar#141d27, bordas#1e2a38. Motivação: conforto visual / sensibilidade à luminosidade. - 🎨 Paleta de cores hierárquica — 12 cores com papéis definidos: branco puro para títulos, azul para KPIs, âmbar para explicações, amarelo para links.
- 📐 Escala tipográfica padronizada — De 27 tamanhos arbitrários para 8 níveis consistentes (hero → small).
- 🔤 Inter substitui Roboto Condensed — Labels e captions agora usam Inter, alinhando tipografia com o blog SapiensLabs.
- 📊 Gráficos Plotly adaptados —
plotly_layout()aplica cores claras em fontes, backgrounds transparentes, grid e zeroline sutis.
Added
- ✨ Helper
kpi_card()— Card KPI padronizado com border-left accent colorido. Substitui todos osst.metric()nativos. - 🏗️ Layout "Esta Semana" redesenhado — De 1/3+2/3 vertical para
[1,1,3]horizontal + sentimento por candidato em grid 3 colunas.
Removed
- 🚫 HRs pesados eliminados — 61
st.markdown('---')substituídos por divisórias sutis. - 🚫
st.metric()nativo removido — Substituídos porkpi_card()com cores accent por semântica.
- 🌙 Tema escuro completo — Migração de tema claro para escuro. Fundo navy
-
v2.6.4 2026-04-13 Filtros de qualidade no coletor Brave Fixed
- 🚫 Filtro de data no Brave —
save_articles()agora descarta artigos compublished_atanterior aMIN_PUBLISHED_DATE(2026-03-25), alinhando-se ao comportamento já existente no Tavily. Corrigido falso positivo: artigo de outubro/2025 da RTP sobre Michelle Bolsonaro havia sido importado. - 🎯 Validação de padrão do candidato — Antes de salvar, verifica se o texto do artigo casa com pelo menos um
CANDIDATE_PATTERNdo candidato primário. Evita que a API retorne resultados irrelevantes (ex.: Hertz a empresa, notícias da Colômbia) que eram aceitos por padrão. Log[SKIP] Falso positivopara rastreabilidade.
Removed
- 🗑️ 3 artigos falsos positivos removidos do banco — IDs 8833, 8850 e 8857: artigo da RTP (fora de data), notícia da Colômbia (sem relação com candidatos BR) e artigo sobre Hertz/Amazon (empresa homônima ao candidato Hertz Dias/PSTU).
- 🚫 Filtro de data no Brave —
-
v2.6.2 2026-04-12 Normalização de timezone nos coletores Fixed
- 🕐
published_atgravado em UTC — Todos os 5 coletores (Brave, Tavily, RSS, Google News, Sitemaps) agora gravampublished_atcom offset UTC explícito (+00:00). Anteriormente cada coletor usava um formato diferente (naive BRT, naive UTC ou misto), causando bug recorrente de timezone. - 🔄 Dashboard retrocompatível —
parse_published_dates()converte UTC→BRT ao exibir datas novas; datas legadas (naive BRT) passam sem alteração.
Changed
- ♻️
calendar.timegm()— RSS e Google News substituírammktime()porcalendar.timegm()para interpretar tuples feedparser como UTC, não como hora local. - 🗓️ Sitemaps com
dateutil—extract_news_entries()eextract_daily_entries()normalizam viadateutil.parse().astimezone(utc).
- 🕐
-
v2.6.1 2026-04-12 Filtro de período consolidado Changed
- 🔗 Controle unificado de período — Substituídos dois controles redundantes ("Janela de dados" selectbox + filtros "De/Até" por página) por um único
st.pillsna sidebar:7 dias | 30 dias | 90 dias | Tudo | Personalizado - 📅 Modo Personalizado — Ao selecionar "Personalizado", campos De/Até aparecem lado a lado para intervalo exato
- 🌐 Período global — Todas as páginas (incluindo Início e Cenário Geral) respeitam o período selecionado
- 🧹
apply_filters()simplificado — Contém apenas sentimento, espectro editorial e fontes; sem mais sobreposição com filtro de datas - 📐 Reposicionamento — Controle de período movido para abaixo da navegação
- 🔗 Controle unificado de período — Substituídos dois controles redundantes ("Janela de dados" selectbox + filtros "De/Até" por página) por um único