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.

  1. 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.

  2. 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.

  3. v3.4.2 2026-04-20 Agenda de coleta ajustada para 07:00/14:00/21:00

    Changed

    • eleicoes-coleta.timer passa 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.

  4. 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() em src/dashboard.py, CSS de .stChatInput e 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 apenasDELETE/DROP bloqueados, 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 FROM em 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).

  5. 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 login periódico no LXC 121; agora usa o mesmo backend do pipeline de coleta. claude_cli.py passa a LEGACY.
    • Texto na Metodologia atualizado: "IA (Claude Haiku 4.5)" → "IA (Gemma4 31B-cloud via Ollama Pro)".

  6. 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, Light e Use system setting. Sem toggle custom.
    • Dict COLORS computado 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 consumir COLORS['text'], COLORS['grid'], COLORS['hover_*'] em vez de #ffffff hardcoded.
    • Radar (spider chart + scatter de quadrante) migrado para consumir COLORS — antes bypassava o helper plotly_layout() e ficava ilegível no light.
    • Cards "Candidato destaque" e "Sentimento da semana" da página Início trocaram background:#141d27 hardcoded por var(--bg2).
    • Eyebrow da sidebar (Sobre artes e técnicas) consome var(--accent-gold) em vez de hex fixo.
    • Links no cream: var(--color-yellow) remapeado para o gold editorial #b8874a (amarelo vivo #eeee22 era 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 (precisava claude login periódico, credencial expirava silenciosamente). Agora usa ollama_client.call_model() como o restante do pipeline. Mesma assinatura, zero mudança de comportamento para o usuário final. claude_cli.py passa 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.

  7. v3.1.0 2026-04-18 Design system, iconografia Lucide, coleta 3×/dia e qualidade de dados

    Added

    • Design system compartilhadosrc/static/design-system.css consome tokens de sapienslabs-design-system (paleta, tipografia, dividers, gradients, utilitários). Hex hardcoded no dashboard.py caiu ~43% (193 → 110 ocorrências).
    • Iconografia Lucide — Novo módulo src/icons.py com 24 ícones SVG inline. Helpers lucide() e lucide_data_uri() para uso inline e via mask-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_detail em articles (extrema-esquerda / esquerda / centro / direita / extrema-direita). CLASSIFICACAO.md documenta critérios. scripts/refresh_editorial_lean_detail.py faz 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×/diaeleicoes-coleta.timer agora 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 Telegramgrep -c ... || echo '0' gerava stdout multilinha que quebrava [ "$LOCKS" -gt 0 ]. Novo helper count_lines() sanitiza, aplicado a FPS/ERROS/LOCKS.
    • DB locked em Tavilycoletores/tavily.py migrado 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 padronizadocandidate_utils.py agora usa get_db_connection() (timeout=30 + busy_timeout=30000 + WAL) em vez de sqlite3.connect() cru.

    Data quality

    • Backfill de editorial_lean em 58 fontes (unknown caiu de 25,7% para 17,1% — redução de 32%). source_lean_map.py expandido de ~50 para 159 entradas.
    • Limpeza histórica de 305 falsos positivos via verificador.py --horas 450.

  8. v3.0.1 2026-04-15 Tópicos Emergentes: ordenação e desempate

    Fixed

    • 🗓️ Exemplos de artigos mais recentesTópicos Emergentes agora ordena os 3 artigos de exemplo por pub_date decrescente antes do .head(3). Antes o widget mostrava os primeiros na ordem natural do DataFrame (por id crescente, aproximadamente os mais antigos da semana), fazendo parecer que não havia material do dia corrente mesmo quando já havia.
    • ⚖️ Desempate determinístico no scoreemerging_topics.sort agora 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 do value_counts() do pandas.

  9. 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 adaptadosplotly_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 os st.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 por kpi_card() com cores accent por semântica.

  10. v2.6.4 2026-04-13 Filtros de qualidade no coletor Brave

    Fixed

    • 🚫 Filtro de data no Bravesave_articles() agora descarta artigos com published_at anterior a MIN_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_PATTERN do 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 positivo para 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).

  11. v2.6.2 2026-04-12 Normalização de timezone nos coletores

    Fixed

    • 🕐 published_at gravado em UTC — Todos os 5 coletores (Brave, Tavily, RSS, Google News, Sitemaps) agora gravam published_at com 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ívelparse_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íram mktime() por calendar.timegm() para interpretar tuples feedparser como UTC, não como hora local.
    • 🗓️ Sitemaps com dateutilextract_news_entries() e extract_daily_entries() normalizam via dateutil.parse().astimezone(utc).

  12. 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.pills na 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