Skip to content

changelog: Journalist features, dark mode, story packages cache, UI fixes — Mar 16-17 2026 #13

Description

@rodanaya

Summary of changes — March 16–17, 2026

This issue documents the batch of features and fixes shipped over the last two sessions.


✅ Journalist Investigation Features

GET /api/v1/stories/packages — 8 pre-packaged investigation narratives with live data

  • Each package includes: Spanish lede, concrete vendor examples, expected government defense, FOIA next steps, difficulty badge (rapida / requiere_solicitud / investigacion_larga)
  • Packages: ghost_companies, top_suspicious, administration_comparison, efos_vendors, sector_overpricing, new_vendor_risk, monopoly_capture, direct_award_surge

GET /api/v1/vendors/{id}/similar-cases — cosine similarity vs GT case centroids

  • Computes vendor z-score centroid from contract_z_features, returns top 3 similar corruption cases with shared/divergent features

GET /api/v1/vendors/{id}/narrative — year-by-year arc detection

  • Returns arc_shape (explosive_entry, capture_pattern, single_burst, etc.), peak_year, active_years, per-year data

StoryFinderSection in /journalists — replaces static "IDEAS" section

  • Expandable StoryPackageCard components with difficulty badges, live vendor examples table, defense blockquote, numbered next steps
  • Falls back to original static cards on error/loading

"Periodista" tab in VendorProfile (pre-existing, confirmed working)

  • Narrative arc BarChart, similar GT case cards, auto-generated Spanish lede with copy button, evidence CSV download

🐛 Bug Fixes

stories/packages 30s timeout (fix(stories))

  • Root cause: 3 of 8 queries hit full 3.1M row table scans — sector_overpricing took 71.6s alone, direct_award_surge 23.8s, admin_comparison 20.6s
  • Fix: module-level 1-hour TTL cache + background thread. First request returns 503 Retry-After: 30 while computation runs (~2 min). warm_stories_cache() called from _warmup_caches at startup so cache is hot before users arrive. Subsequent requests: <1ms

test_year_filter PHI test — 1 failing test fixed

  • PHI year-filtered endpoint legitimately returns 503 on cold cache (background computation model by design). Test now accepts 200 | 503

Executive Summary stat bombs overflow (fix(ui))

  • 6-column grid was clipping 361K, 130K, 0.849, 11.9% at narrower viewports
  • Fix: gap-4→3, p-5→p-3 sm:p-4, font size scaling >5chars:1.6rem / >3chars:2rem / ≤3chars:2.5rem, overflow-hidden→whitespace-nowrap

Journalists page dark mode (feat(journalists))

  • Removed hardcoded style={{ backgroundColor: '#faf9f6' }} inline style
  • Added dark: Tailwind classes throughout: cards, tables, text, borders, grade badges, difficulty badges, attribution blockquote, copy button states

🚀 Deploy

All changes deployed to production at http://37.60.232.109 (rubli.xyz) via Docker Compose.

Commits:

  • 2a62dcf feat(journalists): dark mode support throughout
  • 1f00482 fix(stories): add TTL cache + startup warmup to avoid 30s timeout
  • 99ab5ef fix(model): v6.1 red-team hardening + frontend CSS theming (incl. stat bomb fix)

Test status: 590 passing (backend) · 0 TypeScript errors · build ✓

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdocumentationImprovements or additions to documentationenhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions