Inspiration

I'm a Georgia Tech student and I ride MARTA. The app says a bus is coming at 9:14. Sometimes it is. Sometimes it's 30 minutes late. And sometimes it's a ghost bus( on the schedule, but it never left the depot). You find out by standing at the stop until you give up and pay $18 for an Uber.

The data to catch this exists, MARTA publishes a live GTFS-Realtime feed of every vehicle. But nobody keeps score. Google Maps predicts your next bus; no one tells you whether that route keeps its promises. During the hackathon I pointed a poller at MARTA's own feed and found that ~49% of scheduled trips that day never appeared on GPS. That number is why this project exists.

What it does

Transit Ledger ATL is an independent accountability layer for Atlanta transit:

  • Bus or Uber? Type where you're going ("Georgia Tech, arrive by 9:30") — it finds every route serving that destination and returns a verdict backed by a live track record: on-time %, ghost no-shows, minutes since last GPS ping, and better alternatives.
  • Ghost-bus detection — joins live GPS against today's static timetable to catch trips that should be in service but never showed.
  • Live pressure map — every active bus, colored by its route's reliability. Red clusters show which neighborhoods are being failed, not just which routes.
  • Ask the data — a Gemini chat with function-calling tools wired into the live database.
  • Campus briefing — a durable Render Workflow that pulls the day's stats and has Gemini write a rider-facing morning briefing.

How I built it

  1. Ingest — A Render Cron Job polls MARTA's GTFS-Realtime feed (protobuf) every minute. MARTA only sends absolute predicted times, not delays, so the poller diffs each prediction against the static GTFS timetable to compute every delay itself.
  2. Store — Everything lands in Tiger Data (TimescaleDB): two hypertables ingesting ~2,900 vehicle positions and ~226,000 delay samples every 10 minutes. A continuous aggregate (route_reliability_15m) keeps every route's on-time % precomputed, so every verdict and chart is a lag-free index scan — no GROUP BY at request time. Columnar compression kicks in after 2 hours to keep it on a tiny instance. On-time is defined as |delay| ≤ 5 minutes vs. the static schedule.
  3. Reason — A Render Workflows service (@renderinc/sdk) runs a durable task graph — campusBriefing fans into gatherStats (query Tiger) → writeBriefing (Gemini) → storeBriefing (back into Tiger) — with per-task retry and backoff.
  4. Serve — A Render Web Service (Express + vanilla JS) does the rider-facing work: the bus-vs-Uber verdict engine, the Gemini function-calling chat with five tools over live SQL (reliability, ghosts, headway gaps, worst routes, commute verdict), a live Leaflet map colored by route reliability, and the workflow-written campus briefing. The web + cron services deploy from a render.yaml Blueprint. Challenges I ran into
  • MARTA's feed doesn't include delays. It sends absolute predicted times only — so I had to parse the full static GTFS (a ~55MB stop_times.txt), build a schedule index, and compute every delay myself by diffing predictions against the timetable.
  • "Georgia Tech" doesn't exist. GTFS stop names are street intersections ("NORTH AVE NW @ FOWLER ST"), so searching the campus name matched Georgia Piedmont Tech on the other side of town. Fixed with a landmark→coordinates alias layer and haversine nearest-stop resolution.
  • Gemini free-tier limits — I burned through the daily quota of one model mid-hackathon and had to swap models and make the model configurable. The workflow's retry/backoff isn't decoration; it's load-bearing.
  • Blueprints don't support Workflows yet, and Gemini 3.x requires echoing thought_signature parts back in multi-turn tool loops — both cost me time in docs.

What I learned

Continuous aggregates genuinely change the architecture — I never wrote a GROUP BY at request time. Durable, retried task graphs are the right shape for anything touching a flaky external API. And the most damning insight came from ~40 lines of SQL: the hard part of civic tech isn't the model, it's getting the data into a shape where the truth is one query away.

What's next

Stop-level (not route-level) scoring, a weekly "MARTA report card" published automatically by the workflow, and alerts for routes whose ghost rate spikes — because riders deserve receipts.Sometimes it is. Sometimes it's 30 minutes late. And sometimes it's a ghost bus — on the schedule, but it never left the depot. You find out by standing at the stop until you give up and pay $18 for an Uber.

The data to catch this exists — MARTA publishes a live GTFS-Realtime feed of every vehicle. But nobody keeps score. Google Maps predicts your next bus; no one tells you whether that route keeps its promises. During the hackathon I pointed a poller at MARTA's own feed and found that ~49% of scheduled trips that day never appeared on GPS. That number is why this project exists.

What it does

Transit Ledger ATL is an independent accountability layer for Atlanta transit:

Built With

Share this project:

Updates