Dave HunterBlog

Mac Login

GitHub is the State

AI got good enough to write a lot of my code, and for a while it made building software considerably less enjoyable.

The problem wasn't that the code was always bad. The problem was the way I was working with it: prompt an agent, watch it go, catch the mistakes, redirect it, repeat. I had gone from writing software to supervising a very fast engineer with no memory and questionable judgment.

Spending my days catching AI errors was never my dream. Was this now my job?

Gross.

The breakthrough wasn't a better prompt or a better model. It was changing the abstraction. Instead of interacting directly with Claude all day, I built an engineering workflow around a small team of agents. GitHub is their shared state. They pick up work, design, implement, review each other, respond to CI, create new backlog issues, and escalate decisions they can't make.

I still own the product, architecture, priorities, and creative decisions. I just don't need to sit in front of an agent while it types anymore.

Unexpectedly, this has made building software genuinely fun again.

What I'm Building

The product deserves its own post, but the complexity matters here. I'm building an app that controls hardware, deals with low-level network protocols like UDP and multicast, and has a lot of data modelling and UI. It started as a React Native app with a C++ engine connected through TurboModules. I've since moved the client to SwiftUI for iPhone, iPad, and macOS while keeping the C++ core bridged directly into Swift. There is also a Next.js backend.

This isn't a toy TODO app I built to demonstrate agents. The workflow evolved while I was trying to ship the actual product. When something broke or became annoying, I changed the system and kept going.

The Wrong Abstraction

I was lucky to be at Shopify when OpenAI released the first thing I'd used that was actually helpful for software engineering, and I got to experiment with it a lot in those early days. I was incredibly excited at first. Over time, though, it settled into being a slightly annoying but useful autocomplete and research tool. It was wrong too often, so I frequently used it for reference but wouldn't let it write my code.

Around the winter of 2025/26 that changed. The models became good enough that their ability to write software couldn't be ignored. Copilot improved. Agent mode became genuinely useful. Claude started producing more and more of my work.

And I hated the experience.

I've worked in tech for a long time and I love code. I care about the craft. Watching a model produce code and then spending my time hunting for the subtle ways it was wrong felt unsatisfying and, honestly, a little unsettling. In hindsight I think I was mourning the way I had always built software.

Eventually I started asking a better question. Was AI making software development miserable, or was I choosing a miserable way to work with AI?

The interesting thing about coding agents isn't that they can type code. It is that they can participate in a process. So instead of trying to become a world-class Claude prompter, I started building the process.

A Mac Mini Full of Minions

My agents run on a Mac mini. I'm building for Apple platforms, and the agents need to run Xcode builds, launch apps, execute UI tests, and do other things that require a real logged-in Mac. Cloud hosting would have been simpler for a web app or backend, but it wasn't a natural fit here—especially when I started the project on beta versions of macOS and Xcode.

Each agent has its own macOS account with limited permissions and its own GitHub account with limited access to the repo and project. I gave them the names and avatars of Minions, because apparently my serious answer to identity and access management is Bob and his friends.

The joke is useful. The identity is real. When an agent claims an issue, opens a pull request, leaves a review, or gets something wrong, I can see who did it. Permissions are bounded, and the work has an audit trail.

A startup script opens a persistent tmux session for each agent, starts Claude, and runs:

/loop /next-task

I can SSH into the mini and observe those sessions or jump in when necessary. I generally keep an iPad Air on my desk running Termius with an SSH session open to each agent. Early on I watched them constantly. These days I mostly glance over to make sure nobody is on fire.

Loops? WTF

When I started looking into more autonomous agent workflows, I kept coming back to loops. All the cool kids were using loops. Loops were apparently what we were supposed to build. WTF was a loop?

So I built one.

In my system, /loop repeatedly invokes a command called /next-task. Claude loads its instructions from .claude/commands/next-task.md, performs one tick, records what happened, and then decides when to run again. If work is available it can run again in a minute. If the board is quiet it backs off, up to 30 minutes.

To be clear, I didn't write all of next-task by hand. AI did. Initially it was a long Markdown file describing what to do. It has since evolved into a set of instructions backed by command-line tools: shell scripts at first, now mostly Swift applications wrapping gh and the GitHub APIs. The tooling includes a fair amount of caching so a team of enthusiastic Minions doesn't burn through my REST and GraphQL quotas by repeatedly asking GitHub the same questions.

The loop itself isn't the important part. The important part is what it reads and writes.

GitHub is the State

The agents don't share a magical long-term memory. They don't need one. The GitHub Projects board, issues, pull requests, reviews, assignees, dependencies, and CI results collectively describe the state of the engineering organization.

Issues describe work. Their order on the board is priority. Columns say which role is responsible. Dependencies say what is blocked. Assignees act as claims. Pull requests hold the deliverables and the review conversations. CI failures and review comments send work back to the appropriate role.

An agent wakes up, reads reality from GitHub, performs one unit of work, writes the result back to GitHub, and disappears again. Another agent can take over later without inheriting its context window, because the durable facts are already in the system.

My board includes states such as Backlog, Ready, Design, In Progress, Agent Review, Human Review, and Needs Human. The agents generally work from right to left after Done: respond to my feedback in Human Review, address another agent's comments in Agent Review, move implementation or design work forward, or claim the next Ready item.

GitHub Kanban Board

Each agent can perform one or more roles:

The reviews are not ceremonial. Agents can go back and forth several times, and designs go through the same process before code is written. That catches a surprising number of problems while they are still cheap.

CI is part of the workflow rather than something I monitor on the side. If a check fails, the state changes and the responsible role gets another turn. If an agent is blocked on a product or architectural decision, it moves the issue to Needs Human and asks me to make the call—preferably as a small set of choices. Once I've answered, I move the card back and the system continues.

The agents can also create backlog issues. If a Minion finds an unrelated bug, missing test, or weakness in the workflow, it records it instead of derailing the current task or hoping someone remembers. The agent has autonomy to say, "This should exist as work." I retain the decision about whether and when it becomes Ready.

This is the part that feels less like building a coding tool and more like designing an engineering organization. Identity, permissions, priorities, claims, handoffs, review, CI, escalation, and recovery matter more than the prompt that produces a line of code.

A single /next-task tick looks roughly like this:

My Interface to the Project

I now interact with the project in three main ways:

That last one has changed my relationship with the project more than I expected. I can go for a walk, talk through a design, have ChatGPT challenge my thinking, and then turn the result into durable work in GitHub. I can ask it to pull up a code review and help me reason through what the agents are doing without opening an IDE.

I've literally moved the project forward while floating in my pool wearing waterproof headphones, talking through a design with ChatGPT and having the resulting work added to GitHub. This is objectively a ridiculous way to build software. It is also pretty great.

The Job I Actually Want

This system is not flawless. Agents still make questionable decisions. CI breaks. GitHub has rate limits. Workflow rules develop weird edge cases. Sometimes I have to prod the Mac mini. I'm still debugging constantly.

What changed is the level at which I'm debugging.

Before, I was sitting beside Claude, catching individual mistakes as it wrote code. Now I'm more often debugging the engineering organization: improving how work is described, deciding which role should act, tightening a review rule, fixing an escalation path, or making sure the right facts survive in GitHub.

I haven't handed over the parts I care about. I still decide what the product should be. I make the architectural calls. I argue through designs, set priorities, judge tradeoffs, and shape the system that produces the software. I can drop into the code whenever I want, but the workflow doesn't require me to spend all day watching an agent type.

I thought AI was taking away the part of software development I loved. It turned out I was working at the wrong abstraction.

GitHub is the state. The Minions do a remarkable amount of the implementation. I get to concentrate on product, architecture, and the creative parts of engineering—and, for the first time since coding agents became unavoidable, I'm genuinely enjoying building software this way.