JAMIE
A persistent AI development partner built on agent harnessing and context engineering.
[ read case study ]A daily news dashboard that replaces six morning apps with a single fresh read.




Six apps every morning. Google News for headlines, ESPN for scores, a podcast app for what dropped overnight, TMDB for what's new in theatres, a streaming service for what to watch tonight, a local outlet for what was happening near home. Each one was a tab away from the next, each one a different shape of "here's what's going on," and none of them knew what I actually cared about.
I wanted one place that pulled the actual signal from each source and laid it out in the shape my morning already has — a quick scan of headlines, then sports, then what's worth listening to or watching that night.
Paperboy is two halves that don't talk to each other in real time.
A TypeScript command. RSS feeds for editorial news (Google News subsections + ESPN sports + opinion outlets), the ESPN scoreboard API for the day's games, and TMDB for movies and streaming — 80–145 API calls per run, all in flight at once, finishing in 2–3 seconds. The output is a single digest.json under digests/YYYY-MM-DD/. That file is the artifact.
A Next.js app that reads digest.json at request time. There is no server running between digest builds. When you load the page, the dashboard parses today's JSON and renders. Static-ish behavior at the surface; the data underneath is fresh every morning. It surfaces three tabs:
The dashboard is built entirely on Loom, a design-system pipeline I built before Paperboy — its generated atoms covering every interaction surface.
The dashboard reads a static digest — but any story, podcast, or title carries a Deep dive button that expands it into a full-page, AI-synthesized read on demand. One shared spine runs every time: resolve the item from the digest, gather its sources, make a single model call, write the result to a markdown file, and render it back. The gather half is what changes per content type.
Fetches the full article body plus its related coverage — Google News' opaque CBMi… redirects resolve to the real publisher URL first — then synthesizes a clean recap: summary, key points, full story, other coverage, context. Roughly a third of sources sit behind paywalls; those fail soft to the digest snippet, with the model told not to invent the missing body.
Uses the published transcript when a show ships one; otherwise writes an honest "listening guide" from the show notes — never a fabricated quote. The gather path knows the difference between a real transcript and a thin description, and the output says which one it had.
A spoiler-free "should I watch this?" built from structured TMDB detail plus OMDb critic scores (IMDb / Rotten Tomatoes / Metacritic) — scores and availability first, then premise, makers, and reception. A hard zero-plot-inference rule keeps it from guessing the story from keywords.
The model gets the data, not the web. Synthesis is one call behind a provider-agnostic seam — a gemini-2.5-flash adapter today, swappable without touching the call site. The grounding rule is the same across all three: the provided sources are untouchable and stay verbatim, while the model's own knowledge flows freely into the context it's genuinely good at — who the people are, the prior events a story assumes, where a film sits in its genre. What it can't do is invent: no fabricated quotes, no guessed plot, no inventing a paywalled body it never fetched. When a source drops, the read degrades honestly instead of hallucinating the gap shut.
Generation is always explicit — a button, never an auto-fire on navigate — and writes to disk, which makes deep dives a local-only feature. The rest of the dashboard deploys fine as a static read over a committed digest.
Script-first, not server-first. Most aggregators I'd seen had a server polling, a database holding results, an API serving the dashboard. Paperboy has none of that. One script produces one JSON file; the dashboard reads the file. The interface between pipeline and frontend is the filesystem. No DevOps, no persistent state, no drift between "what the database has" and "what the user sees." If the digest is right, the dashboard is right.
Editorial sources over keyword search. Google News topic feeds, ESPN's structured RSS, podcast publisher feeds, TMDB's curated lists. No keyword search anywhere. Search noise is the easiest way to ruin a daily feed — one viral headline pollutes a topic for a week.
Graceful degradation as a baseline. Every external fetch is wrapped in Promise.allSettled with per-source isolation. When TMDB has a bad morning, news and scores still ship; entertainment degrades to empty with a warning. When ESPN goes down, you still get news and podcasts. One outage doesn't kill the day.
Team color accessibility built in. Every team color routes through an ensureContrast() utility that compares the brand color against the current theme's surface luminance and substitutes the alternate when contrast is insufficient. Light mode and dark mode both supported. The dashboard uses team brand colors aggressively — scoring breakdowns, schedule tables, box-score row hovers — and the contrast logic keeps every one of them readable.
Audit scripts that close the loop on every warning. Dozens of external feeds and endpoints drift independently — a feed goes dead, an ESPN URL changes shape, a TMDB list moves. So check-endpoints status-checks every RSS feed, ESPN URL, and TMDB endpoint in about a second, and audit-media-bias scans past digests for outlets missing from the bias map. When the daily digest emits a warning, there's a script that tells me exactly what moved — no guessing at which of the sources broke.
JSON in, files out. The three slices below show the shape of the pipeline end to end — the digest artifact, the fetch wrapper that keeps it resilient, and the filter that turns raw RSS into clean signal. Pick one.
The artifact at the heart of the pipeline. One file per morning under digests/YYYY-MM-DD/ — the dashboard reads this directly, no server in between.
{
"meta": {
"date": "2026-05-26",
"day_of_week": "Tuesday",
"story_count": 142,
"run_mode": "initial",
"last_run": "2026-05-26T07:14:33.812Z"
},
"sections": {
"popular_today": [ /* trending headlines */ ],
"local": { "locations": [ /* per-location story lists */ ] },
"for_you": [ /* topic feeds the reader follows */ ],
"on_your_radar": [ /* topic feeds in rotation */ ],
"scores": {
"team_sports": { "recaps": [...], "schedule": [...], "standings": [...] }
},
"entertainment": { "movies": [...], "streaming": [...], "upcoming": [...] },
"podcasts": [ /* episodes that dropped today */ ],
"opinions": [ /* opinion-section pieces */ ]
},
"deep_dives": [ /* generated on demand — one .md per story */ ]
}A persistent AI development partner built on agent harnessing and context engineering.
[ read case study ]A design-system pipeline that scaffolds application UI from a single config source.
[ read case study ]A digital bulletin board for spontaneous event planning — events are posters, your people and calendars are the boards.
[ read case study ]