When the World Changes, Your Dashboard Should Too
How I added real-time conflict intelligence to a personal life dashboard, and why a data system that ignores the world outside your habits is not really intelligence.
Life Hub tracks my sleep, habits, mood, goals, and 38 other data streams. But until last week, it had a blind spot: the world.
The Iran war started on February 28th. Within days, oil hit 4-year highs, the Strait of Hormuz was contested, and US service members were killed in retaliatory strikes. My dashboard showed me I'd missed a habit streak. That imbalance bothered me.
The Problem: News as Noise
Life Hub already had a news collector: RSS feeds from NYT, BBC, MarketWatch, Ars Technica, and Hacker News piped through feedparser, scored for personal relevance, and summarized by Claude Haiku into a daily briefing. But the system only knew two categories: “world” and “tech.” A Pentagon deployment and a royal wedding got the same treatment.
The Iran war headlines were drowning in a sea of undifferentiated world news. Meanwhile, Life Hub’s conflicts system (a separate module tracking 38 US military engagements with LLM-refreshed data from DVIDS and DoD press releases) sat completely isolated. Two systems that should have been talking weren’t.
Five Categories, Not Two
The fix started with classification. The news collector matches feed URLs against patterns to assign event_type. I added conflict as the first match (first match wins), keyed on defense-specific publishers:
_FEED_CLASSIFICATION = [
(("defensenews", "breakingdefense", "military", "stripes.com"), "conflict"),
(("technology", "techcrunch", "arstechnica", "hnrss", "bleepingcomputer"), "tech"),
(("politics", "politico", "npr.org/1014"), "politics"),
(("marketwatch", "business", "economy", "finance"), "economy"),
]Two new RSS feeds, Defense News and Breaking Defense, provide the raw signal. Both are already covering the Iran conflict directly with Pentagon sources. The AI briefing prompt now processes five categories in priority order: conflict, politics, world, economy, tech. Conflict gets explicit guidance: “military operations, active wars, defense policy, troop deployments, casualties, arms deals.”
Relevance Scoring: Token Matching Done Right
Life Hub’s relevance engine scores headlines using keyword overlap with your active goals, journal tags, habits, and current life season pillar. I added 28 conflict-specific keywords (war, military, iran, pentagon, casualties, ceasefire, centcom, drone, airstrike), each worth +3.0 points per match.
The first version used substring matching (if kw in title.lower()), which worked for multi-word phrases like “special forces” but caused false positives on short words: “war” matched “software” and “warranty”; “army” matched “pharmacy”; “ai” (from the tech keywords) matched “terrain.”
The fix: split keywords into two sets. Multi-word phrases keep substring matching. Single words use token-based set intersection against the tokenized headline, which only matches whole words. A small change that eliminated an entire class of scoring distortions.
# Token matching for single words (no false positives)
score += len(title_tokens & _CONFLICT_TOKEN_KEYWORDS) * 3.0
# Substring matching for multi-word phrases
for kw in _CONFLICT_PHRASE_KEYWORDS:
if kw in title_lower:
score += 3.0Today’s top headline: “Pentagon reportedly sending more warships and Marines to Middle East” scores 12.0 (four keyword hits). A generic world headline about Cuba scores 8.0. The ranking reflects actual importance to someone whose professional world intersects with defense infrastructure and whose country is at war.
Bridging the Gap: Conflicts Meet the Morning Briefing
The conflicts module already tracked 38 active engagements with structured data: country, scale, risk level, personnel counts, coalition partners, and an LLM-maintained changelog citing DoD sources. But none of that reached the morning briefing.
One try/except block fixed that. The morning briefing MCP tool now queries the conflicts system and surfaces a summary: 38 active engagements, 5 critical (Operation Epic Fury, Ukraine defense support, Strait of Hormuz, Taiwan Strait transits, Israel military aid). That’s context I want before checking email.
The Today Page: Rose for Conflict
On the UI side, the today page now renders five news sections instead of two. Conflict appears first in rose (distinguishing it from red for politics). The implementation is a data-driven Jinja2 loop, not five copy-pasted HTML blocks. Adding a sixth category later means adding one tuple:
news_sections = [
("conflict", "Conflict", "text-rose-400/80", "bg-rose-400"),
("politics", "Politics", "text-red-400/80", "bg-red-400"),
("world", "World", "text-teal-400/80", "bg-teal-400"),
("economy", "Economy", "text-amber-400/80", "bg-amber-400"),
("tech", "Tech", "text-cyan-400/80", "bg-cyan-400"),
]What This Actually Means
A personal dashboard that ignores the geopolitical context you live in is a fitness tracker with blinders on. Oil prices affect your portfolio. Military deployments affect people you know. Active conflicts shape policy that shapes your industry.
The total change was about 150 lines across 7 files, plus tests. The conflicts system was already built. It just needed a bridge to the news pipeline and the morning routine. Sometimes the most impactful work is connecting things that already exist.