A field guide to The Lexington Times
A searchable AI transcript archive of every LFUCG meeting. A machine-readable news feed with MCP servers built for AI agents. A real-time 3D map of fire calls, flood gauges, and aircraft over Fayette County. All glued together by open-source code, public data, and one very opinionated editor. (An earlier 24/7 AI livestream, LexBot, has been retired — the meeting archive, feeds engine, and situation map carry the work forward.)
What you’re looking at
The Lexington Times is a small newsroom bolted onto a set of civic-data machines. Here are the three that sit behind the front page — with a live 3D map and one retired experiment further down.
The paper
lexingtonky.news
WordPress. Original reporting, commentary, and meeting agendas, plus AI-summarized wire copy that the feeds engine publishes straight in through the WordPress REST API. SEO via Yoast, stats via Jetpack, cached behind Cloudflare. The human-readable front door.
The feeds engine
feeds.lexingtonky.news
Node + Express. Scrapes public government and news sources, rewrites them with Claude, and stores each story as JSON. Serves RSS, sitemaps, social-card images, full-text search, a Markdown / JSON / llms.txt version of every article, and an MCP server so AI agents can query it directly.
The meeting archive
meetings.lexingtonky.news
Civic Memory. A Python pipeline turns every LFUCG council and commission meeting into a searchable, timestamped transcript with a plain-language summary, a RAG question-and-answer endpoint, and its own MCP server. The same pipeline runs for Paris, Kentucky at civicmemory.news.
The stack
llms.txt · open-CORS JSON
Public data sources
Everything below is either public record, a licensed re-publisher, or data we generate ourselves. No scraping of paid sources. No social-media private APIs. No PII.
The meeting pipeline
LFUCG publishes council and commission meetings on Granicus with scanned agenda PDFs and auto-generated captions. That’s where our civic coverage starts — but the raw feed isn’t something you can skim. A dedicated Python pipeline turns each clip into something searchable.
extracted_facts.json. Pure structured extraction, no narrative.[timestamp: MM:SS] markers for clickable video seeking.The whole thing is open source — code, docs, and the Granicus reverse-engineering notes (granicus.md) that made timestamp deep-linking possible.
The situation map
A photoreal 3D globe of Fayette County that pulls the same public data as the paper and plots it in space and time. Every layer below is fed by a stateless overlay API and rendered in the browser with Cesium over Google’s 3D Tiles — no game engine, no login.
The livestream (2025–2026), retired
For about a year, The Lexington Times ran LexBot — a 24/7 civic-radio livestream on YouTube. An orchestrator drove OBS through rotating traffic cams, scanner ambience, NWS alerts, and real-time LFD incidents, while dual AI hosts (scripted by Claude, voiced by ElevenLabs) read the day’s news.
The formats were deliberately unhurried — a civic radio feed, not a scroll: hourly news briefs, a weekday morning briefing and evening wrap, weather cut-ins, listener “Ask Lex” questions fact-checked against a live web search, per-meeting recaps chosen by the RAG archive, an aggregated crime blotter, and lighter turns like a pet of the day and a neighborhood of the week.
The broadcast was retired in mid-2026. Its scripts remain in the meeting archive as historical transcripts, and the code stays in the repo — but the meeting archive, feeds engine, and situation map now carry the work the livestream used to do.
Guardrails
- AI disclosure — every AI-summarized article and meeting summary carries a visible note that it was machine-generated, which model wrote it, and when.
- Fair use — aggregated headlines carry a title + ≤200-character summary with the canonical source link. Everything else is paraphrased and cited; only licensed re-publishers run verbatim.
- Grounded answers — the meeting Q&A runs at low temperature behind a similarity gate with a per-claim citation check. If it isn’t in a transcript, it doesn’t go in the answer.
- Fresh facts — questions that need current information route through Anthropic’s
web_searchtool, geo-biased to Lexington, KY, rather than guessing. - Circuit breakers — every poller opens after 3 consecutive failures and stays open 5 minutes before retry. No silent infinite-retry.
- Corrections — a public corrections log and a one-click way to flag an error, linked from every footer.
- Aggregated crime — crime data is shown by council district and type. We don’t name individuals or addresses, even when the public data set would allow it.
Humans in the loop
There is a human behind all of this. Most of it was built, is operated, and is continuously debugged by Paul Oliva, with help from friends and open-source contributors. The machines are fast: AI drafts the wire summaries and the meeting recaps. The judgment is not automated — a human decides what gets promoted to the paper, writes the original reporting and the Glaze and Off-Beat columns, answers tips, and owns every correction.
The paper will only ever be as opinionated as the humans behind it.
Questions we get
What’s automated, and what isn’t?
The plumbing is automated: scraping public sources, summarizing wire stories, transcribing and summarizing meetings, and refreshing the map all run on their own. The editorial calls are not. A person decides which summaries get promoted onto the paper, writes the original reporting and the columns, and signs off on corrections. Think of the AI as a very fast wire desk, and the human as the editor who still has to say yes.
How do you stop the AI from making things up?
Three layers. First, grounding: meeting answers are built only from facts extracted out of the transcript — low temperature, a similarity gate, and a citation check on each claim — so if it isn’t in the clip, it isn’t in the answer. Second, fresh lookups: questions that need current information route through Anthropic’s web_search tool, geo-biased to Lexington, so “when did that park open” pulls a real source instead of a guess. Third, disclosure: every machine-written article and summary is labeled as such, with the model and time, so nothing hides behind a byline it didn’t earn.
Can I contribute?
Yes — the meeting pipeline is open source at paul-codes-1/fuzzy-potato, and we accept tips, corrections, and pitches at [email protected].
This is pretty cool. Can we hire Paul?
Paul M. Oliva — Senior Full Stack Engineer
Yes — he is open to new opportunities. Paul built the paper, LexBot, the meeting pipeline, and most of the civic-data plumbing on this site on top of a full-time engineering job — shipping features end-to-end across a Next.js frontend and a Node/TypeScript API for a high-volume ticketing platform.
What you’re hiring, in short:
- FrontendReact, Next.js, TypeScript, Redux, Tailwind
- BackendNode.js, Express, REST, GraphQL, tRPC, FastAPI
- AI / LLMOpenAI, Claude, RAG, ChromaDB, Tesseract OCR
- CloudAWS (Lambda, S3, RDS, SNS/SQS), Docker, GitHub Actions
- DataPostgreSQL, MySQL, DynamoDB, MongoDB, Redis
- PaymentsAdyen, Stripe, PayPal
- ObservabilityDatadog, Sentry, CloudWatch
The code
Public data begets public code. Much of the infrastructure powering this newsroom is open source and happily borrowed from:
- WordPress for the front-of-house, with a custom feeds→WP REST publishing bridge
- Node + Express + TypeScript for the feeds engine, and Fastify for the maps API
- Python + FastAPI + Whisper + ChromaDB for the meeting pipeline and Civic Memory RAG
- Cesium + Google 3D Tiles for the situation map
- MapLibre GL JS + Puppeteer + sharp for civic maps and social-card images
- Model Context Protocol servers so AI agents can query the archives directly
- pino, node-cron, cheerio, fast-xml-parser, Zod
- Anthropic Claude & OpenAI for the AI pieces
Back to the paper: lexingtonky.news. Or read the editorial stance: about the paper.