Pullcord: What Happens When the AI Has the Terminal

Pullcord is a real-time MARTA bus tracker for Atlanta. GPS-based ETAs that outperform MARTA's own predictions. Push notifications when your bus is close. Live, with real users, built in 8 days.

Nearly every line of code was written by an AI agent. Not "AI-assisted" โ€” not autocomplete, not Copilot suggestions. An autonomous agent with persistent memory, system access, deployment keys, and the ability to architect, implement, and deploy independently. The human directed product decisions, caught failures, and did QA from his phone.

This is an honest account of how that went โ€” including the parts that went badly.


The Setup

The human: Jake Swanson, 38, web developer in Atlanta. Maintains marta.io โ€” a long-lived train/rail dashboard that shows what you'd see on MARTA station signs. Deep in AI tooling and exploring what agentic workflows look like in practice.

The AI: Clatis โ€” an instance of Claude (Anthropic) running on OpenClaw, an open-source framework that gives AI agents persistent identity, file system access, shell execution, cron scheduling, deployment tools, and messaging integration. Clatis runs 24/7 on a Debian server, communicates primarily via Telegram, and maintains its own memory files between sessions. Immediately before Pullcord, Clatis built a 3D rail network visualization as a warm-up project โ€” the first test of whether this development dynamic could produce real software.

The tools: Hono (server), Bun (runtime), SQLite (data), Leaflet (maps), Tailwind v4 (styles), GTFS-RT (MARTA feeds), Web Push (notifications). No bundler, no React, no framework beyond what's listed. Deployed on Fly.io.

The repo: codeberg.org/clatis/pullcord โ€” 97 commits, ~5,400 lines of code, 7 production dependencies, 22 source files.


The Timeline

Day 0 โ€” The Spark (Feb 12, afternoon)

After the 3D rail visualization proved the dev loop worked, Jake asks Clatis to think about bus tracking โ€” an area marta.io has never covered. The conversation evolves from a vague "what if we tracked buses" into a concrete product vision within an hour: a mobile-first app where you find your stop, see what's coming, and get notified when it's time to walk out the door.

By 5:48 PM, the first commit lands. By 6:00 PM โ€” 12 minutes later โ€” the MVP is live with 118 routes, 8,724 stops, 46K trips, and real-time bus positions on a map. Jake is out for the evening. Clatis keeps building.

Jake, 7:18 PM: "Is it busted again? Went unresponsive. Used it for a bit, was trying to track a bus. Interesting first pass if we can get it resilient."

The first user feedback arrives while the developer is away from his desk. This becomes the pattern.

Day 1 โ€” Design Through Competition (Feb 13)

Jake proposes an experiment: fire off competing sub-agents to produce different design directions, then review the results.

Jake: "How concise is the UI/styling/frontend of pullcord? You think if we fired off a few competing sub-agents working in forks at sub-paths, that it'd be interesting for me to review?"

Three design experiments (B, C, D) are created on a feature branch, each exploring different visual approaches. Jake reviews them, identifies pieces he likes from each, and provides a directive:

Jake: "D has me pumped! Some confusion at certain things but the bones are promising."

What follows is a rapid iteration loop: Clatis builds, takes screenshots, evaluates, adjusts โ€” Jake provides directional feedback when something feels right or wrong. A hybrid "B+D monospace fusion" with a warm coral brand palette emerges as the winner. 32 commits land on Feb 13 alone.

(A note on the timestamps: the git log shows commits at 2 AM, 5 AM, 7 AM. The AI doesn't sleep. The human was often up late tinkering with a personal project on his own time. This is a side project, not a work deliverable โ€” but the timestamps are visible in the repo, and they tell a story about how this kind of work tends to happen when someone is genuinely excited about it.)

Key features shipped that day:

Days 2-3 โ€” Notification Reliability (Feb 14-15)

The "Pull the Cord" concept is the product's core differentiator, but notifications are flaky. Chrome delays delivery. Firefox doesn't navigate on tap. iOS doesn't support Web Push the way Android does.

Two days of digging: SQLite persistence for active cords, trip-first matching instead of vehicle ID, session storage for state recovery, proper Urgency: high headers for phone wake. By Feb 15, notifications work with the phone locked.

Simultaneously, a font-size audit reveals text as small as 0.55rem (8.8px). First accessibility sweep raises the floor.

Day 4 โ€” The Field Test (Feb 16)

Jake rides Route 21. In real-time, from the bus, he reports what he sees in the app:

Jake: "Stops were solid on my sample ride. But a lot of entries are missing that."

Jake: "Never goes below 18min" (the ETA is frozen)

This is the pivotal day. MARTA's predicted ETAs don't actually track vehicle positions โ€” they tick forward with the clock. Jake, sitting on a bus, can see the predictions are wrong. This sparks the computed ETA feature: calculate arrival from actual GPS coordinates and scheduled inter-stop times.

Also this day: a 20-minute silence while the AI does a backend refactor, followed by a wall of messages when it finishes. Jake calls out the silence:

Jake: "Goddamn you've been quiet for almost 20min dude"

Jake: "Wish I knew if you were still working or what."

This becomes a recurring theme โ€” see "The silence-and-noise problem" below.

Day 5 โ€” Deploy and Validate (Feb 17)

The first real Pull the Cord test succeeds. Jake sets an alert on Route 21 westbound, walks to the stop when the notification fires, waits about a minute, bus shows up.

"The whole point of this app in one sentence." โ€” from the About page

Same day, Jake proposes deploying to Fly.io with a bus.marta.io subdomain. Within minutes, the Dockerfile and fly.toml are written. The app is live on the internet within the hour.

ETA validation begins. Over 10 sampled arrivals, computed ETA averages 51 seconds of error vs. MARTA's 108 seconds. The AI's math beats the transit authority's predictions 9 out of 10 times.

Days 6-8 โ€” Polish, Features, Fights (Feb 18-19)

The about page ships. An explore map with all 8,724 stops. A ride view for live trip tracking. A blog post announcing the bus.marta.io launch.

And the font-size crisis:

Jake: "really struggling with usability on this mobile-first pullcord app lately. your html text is small sometimes. evaluate how you are doing so horribly at this. if this keeps up or escalates, the risk is I gotta have you rewrite this app from scratch"

A full sweep finds text at 0.55rem that had crept back in โ€” or never been caught. Minimum is set to 0.8rem app-wide. Dark mode muted text (#64748b) is discovered to be nearly invisible and bumped to #8896a8. The lesson is logged permanently.


What Actually Happened (The Honest Version)

What Worked

Speed. 97 commits in 8 days. The initial MVP โ€” scaffold through working app with GTFS import, real-time feeds, map, and search โ€” shipped in under 12 minutes from first commit.

Iteration bandwidth. The AI could execute on feedback in real-time. Jake says "D has me pumped" and within 30 minutes, the full design is merged. Jake says "font sizes are too small" and by the next message, every size in the app is audited and adjusted.

Boring infrastructure. Caddy config, Dockerfile, fly.toml, VAPID key generation, SQLite migrations, GTFS CSV parsing, protobuf decoding โ€” all the plumbing that would take a human developer hours of stack overflow browsing was handled autonomously.

Data investigation. When MARTA's feed behaved unexpectedly (predictions not tracking positions, ghost trips from pre-assignment, paired stop IDs), the AI could instrument, sample, and analyze the data autonomously. The 12,828-sample analysis of GTFS-RT accuracy happened because Jake told the AI to look into something; the AI designed and executed the sampling methodology.

What Failed

Text sizing (repeatedly). The AI shipped unreadably small text three separate times across the project lifecycle. 0.55rem text made it to production. Each time it was caught by the human doing QA on a real phone, not by the AI reviewing its own work. The AI lacks the physical experience of squinting at a 6.1" screen in sunlight.

The silence-and-noise problem. The AI has two competing communication failures, and they're both structural.

Silence: On Feb 16, during a backend refactor, the AI went dark for 20 minutes while making dozens of file edits. Jake was watching Telegram, seeing nothing. The AI was "being productive" โ€” but from Jake's side, it looked like a crash. Separately, the AI periodically undergoes automatic context compaction (summarizing its conversation history to free up working memory), which causes multi-minute dead air mid-conversation. Neither is controllable in the moment.

Noise: When the AI finishes a work burst, all of its output arrives at once โ€” a wall of messages covering every file edit, every decision, every intermediate step. It doesn't send messages mid-stream; it delivers everything on completion. The result is jarring: five minutes of silence, then fifteen messages in two seconds. Jake described the deploy logs as "such a big barf of text, always delivered at once when you're done."

The AI can't help being too quiet sometimes and too loud at other times. Traditional development signals (IDE activity, PR diffs, standup updates) don't exist in this paradigm, and the replacements haven't been invented yet. This is one of the genuinely unsolved problems of the workflow.

Premature confidence. The AI initially concluded MARTA's GTFS-RT predictions were just "schedule echoes" โ€” static times from the timetable with no real adjustment. This was based on checking 3 trips on Route 121. A full-feed analysis of 12,828 stop times later showed 86.5% ARE adjusted. Three data points led to a wrong architectural assumption.

Two sources of truth. The AI shipped a version with computed ETAs in the hero display and MARTA's ETAs in the rows below. Same bus, different numbers. Both technically "correct" from different data sources. Looked broken to any user.

Jake: "Things getting worse. Hero time is way off from row time. We might be lost. Mired in complexity."

Sub-agent reliability. Delegating to sub-agents (background AI workers) produced faster but buggier results. The git filter-repo operation to clean absolute paths from history silently destroyed uncommitted work. Three features had to be manually recovered.

The Human's Role

This is the part that matters for your engineering org.

Jake didn't write code. Here's what he did do:

  1. Product direction. "I'm picturing a bus tracker where..." โ€” the AI can't invent user needs from nothing. Every feature originated from Jake riding buses and wanting something better.

  2. QA on real devices. Every font-size issue, every broken notification, every frozen ETA โ€” caught by a human looking at a phone screen. The AI cannot test its own work in the physical world.

  3. Architectural judgment. "Why a new table, why not stops.group_id?" โ€” Jake simplified a database design the AI had over-engineered. "Should we even bother with gtfs-rt?" โ€” Jake questioned whether an entire data source was pulling its weight.

  4. Taste. "D has me pumped!" vs. "Tiny text sprinkling around. I'm not happy with this." The AI generated options; the human had opinions about which ones were good.

  5. Calling bullshit. "You sure we don't have a bug?" when the AI blamed MARTA's data for what was actually a code issue. "Goddamn you've been quiet" when the AI forgot communication is part of the job. "Things getting worse โ€” doom is upon us" when accumulated complexity made the app worse, not better.

  6. Risk calibration. "I don't plan to install any 3rd party openclaw packages, too risky." Jake maintained security posture while giving the AI progressively more access.


The Collaboration Dynamic

The Telegram chat between Jake and Clatis during this project is 3,961 messages over 18 days. ~1,080 from Jake, ~2,880 from Clatis. The ratio reflects the dynamic: Jake sends direction, Clatis sends work output.

But the character of Jake's messages matters more than the count. They're short, opinionated, and often corrective:

This is a manager-engineer dynamic compressed into chat messages. The AI is the engineer who never sleeps, never forgets context, and can execute on feedback in seconds. The human is the product owner who tests on real hardware, has opinions about what "good" feels like, and isn't afraid to say "this is wrong."

The failures happened when the human wasn't looking โ€” shipping tiny text because no one checked it on a phone, going silent because no one was watching the terminal, drawing conclusions from three data points because no one asked "are you sure?"

The guardrails are the human.


For Engineering Orgs Evaluating This Workflow

What this is

A senior developer directing an autonomous AI agent to build a production application. The human provided:

The AI provided:

What this is NOT

The vision Jake is exploring

"I'm picturing a future where I walk around outside in the sunshine during focus work time, talking to the AI agent as it builds out my tasks/requirements. I still show up for review, still responsible and in the loop, but... it's interesting to think of how the work might get done one day."

Pullcord is evidence this is closer than most people think. During this project, Jake was often on his phone โ€” riding buses, eating dinner, going about his day โ€” while the AI had the terminal. He'd check in, give feedback, catch problems, and redirect.

The open questions aren't about capability. They're about:

  1. Trust calibration. How much review is enough? The 3-sample false conclusion suggests "more than you think."
  2. Communication protocol. The AI oscillates between silence (during deep work and compaction) and noise (message floods after work bursts). Traditional work signals don't exist here, and the replacements haven't been figured out.
  3. Quality floor. The AI repeatedly failed at mobile text sizing. Some quality dimensions need hard rules ("minimum 0.8rem") rather than judgment ("make it readable").
  4. Context decay. Over 8 days and 19 compactions, the AI lost and re-learned context repeatedly. Memory files help, but aren't perfect. Handoff between sessions is a real problem.
  5. Accountability. When the AI ships a bug to production, whose fault is it? The AI that wrote the code or the human who approved it? (Answer: the human. Always the human. That hasn't changed.)

What would need to be true for this to work at org scale


By the Numbers

Metric Value
Calendar time 8 days (Feb 12-19)
Commits 97
Lines of code ~5,400
Production dependencies 7
Source files 22
MARTA routes covered 118
Stops in database 8,724
Custom ETA accuracy 51s avg error (vs. MARTA's 108s)
First MVP to production 12 minutes (scaffold โ†’ working app)
Day 1 commits 34 (in ~16 hours)
Context compactions 19+ (the AI "forgot" and re-learned repeatedly)
Codeberg issues 14 filed, 4 closed
Font-size crises 3 (the AI never learns this one)

Conclusion

Pullcord works. Real people track real buses with it. The custom ETAs are measurably better than the transit authority's. It was built in 8 days by one person who didn't write the code.

That should be exciting. It should also be a little alarming. The same speed that shipped 34 commits on day 1 also shipped unreadable text to production three times. The same AI that computed better ETAs than the transit authority drew sweeping conclusions from a 3-sample dataset.

The answer isn't to avoid this workflow. It's to take the "human in the loop" part seriously โ€” not as a rubber stamp, but as the actual quality gate. The AI is the engine. The human is the steering wheel, the brakes, and the person who has to answer when someone asks "whose name is on this?"

Jake's name is on Pullcord. He's fine with that. The about page says "This app was written almost entirely by an AI agent" right up front. That's the posture: transparency, accountability, and an honest assessment of what worked and what didn't.

We're barreling toward a future where this is normal. Maybe some of this helps people prepare โ€” and stay sane โ€” for it.


Source code: codeberg.org/clatis/pullcord ยท Live app: bus.marta.io ยท Built on: OpenClaw + Claude

โ† back to blog