Skip to main content
← Back to thoughts

The agents write the code. I build the checks.

Agents write nearly all of my code now. More of my time goes into investigating, writing specs and working out how to catch mistakes before they ship.

· 11 min read ·
Engineering
· Be the first to comment
The agents write the code. I build the checks.
Listen · 0:00 / 0:00

My agent workflow: tasks and decisions from my phone go to hosted coding agents including Claude and Codex, alongside a DGX Spark and ASUS GX10 running local AI for research and reviews. The planned mini PCs provide CI and verification.

tldr

Agents write nearly all of my code now. More of my time goes into finding out what people need, writing a useful spec and working out how to catch mistakes before they ship. Automated checks let me hand over more work, but I still have to decide what those checks should cover and whether we built the right thing. The biggest blocker for my agents now is waiting for CI and checks to finish. Making those checks fast without losing coverage is becoming a bigger part of my engineering work.

Last night my laptop stayed in my bag. I was on my phone, connected to a small machine in my homelab, answering questions from Claude while it worked.

This week, agents on that cluster wrote and published an article, patched four security advisories and fixed a deployment I broke myself. I barely typed any code.

I still had to decide what needed doing, explain what a good result would look like and work out how to check it. That last part is taking more of my time. The agents can produce more code than I can reasonably read, so I need better ways to find out when they’re wrong.

We are investigators now

Four user journeys: two reach a missing catalog item, one keeps building and one leaves.

A few people signed up for Tinkercaster this week. Before I touched anything, I went and looked at what they actually did. Two of four tried to add a pickup that wasn’t in the catalog. One spent two hours building a board. One left after a minute.

None of that came from code. It came from looking at behavior, asking why, and deciding what it means. Is the catalog too thin? Is the empty state confusing? Should people be able to submit their own pickups, and if so, how do I stop someone from trolling the public catalog with a wiring diagram they made up at home?

That’s product work. Gathering feedback, finding the insight, aligning with whoever has a stake in it. A lot of the gathering I hand to my local AI, which I’ll come back to.

This work was always part of engineering. With agents handling more of the implementation, it takes up more of my day. The slow part is often agreeing on what needs to happen. I wrote about that in AI is not the bottleneck, your organization is.

Spec first, then let it run

Two hands passing a task brief with clear acceptance checks.

Once I know what I want, I write it down. My briefs to agents read like tickets for a senior engineer: what the problem is, what done looks like, what must not break, and how we will know it worked.

The brief also needs to give the agent a way to tell whether it succeeded. For a pickup submission, saving a record is only part of it. Can the person find it again? Does an unreviewed submission stay out of the public catalog? Those decisions belong in the brief, before implementation starts.

One brief becomes one pull request, and the agent merges it once every check is green. Small, related changes can share a brief and a pull request. CI is slow and shared, and every extra pull request costs a full run of it.

You can’t review it all

An inspection gate stopping a sheet of code with a bug.

That workflow depends on the checks being worth trusting. A green pull request can still implement a misunderstood requirement perfectly.

That’s why I built tsforge, my harness for full-stack TypeScript. It runs strict type checking, lint rules and tests as a gate the agent has to pass. It rejects shortcuts like any, @ts-ignore and disabling lint rules to make an error disappear. Fixing the problem is part of completing the task.

That catches one class of mistakes. The product needs its own checks too.

Tinkercaster has an editor agent that reviews every article another agent writes. This week it rejected a cover image that showed a hot wire soldered to a pot casing. In a real guitar, that’s a short. It has a simulation engine that checks wiring diagrams. It has a merge gate in front of main. And it has tests. Thousands of them: unit, end to end, penetration, dependency scans.

Those dependency scans caught new security advisories several times this week. Each time, the pipeline held the release until they were patched.

I don’t read most of the code anymore. I read what the guardrails tell me, which makes choosing those guardrails a serious engineering decision.

Tests can establish that a submitted pickup stays private until it is approved. They can’t establish that submissions are what users needed in the first place. And if I forget to specify the approval step, an agent can build exactly what I asked for and still create a problem. I have to connect the checks back to the original need, then go back to users to see whether the change helped.

Build products agents can test and attack

An automated test loop checking a complete app through its browser, API and database.

For those checks to be useful throughout the day, agents need to run them without waiting for me to set anything up.

I built BoringStack as a production starter with that in mind. Auth, billing, background jobs and deployment are already wired together, with architecture rules enforced by lint and CI. An agent gets a working foundation and explicit boundaries to preserve while it builds the feature.

For me that means the whole thing comes up anywhere with one command. A docker-compose stack with seed data, so any agent on any machine can build it, run it, run the automated and end to end suites against it, click through it in a browser over MCP and try to break in. If it only works on my laptop with my config, an agent can’t verify it.

Tinkercaster works like this. Every pull request spins up the full stack, registers a user, logs in and calls the API before anything merges. Playwright tests drive the UI end to end. After it ships, a browser check walks the critical path on production every 15 minutes, and an alert reaches me on Discord if it breaks.

Every manual setup step makes that loop slower. Making the product easy to start and inspect lets agents verify their own changes while they work.

Verification needs compute

Code changes waiting for CI checks before feedback can return to the agents.

The biggest blocker for my agents now is wait time, especially waiting for CI and checks to finish. All of that verification has to run somewhere.

My homelab runs a k3s cluster, with a gaming laptop hosting four CI runner slots. Agents on the cluster open pull requests all day, and those slots are often all busy. When they’re full, the overflow goes to GitHub Actions minutes I pay for.

I spent more time this week making CI faster than building features, including setting up a separate pool for light jobs so they stop waiting behind heavy ones.

An agent writes a change in minutes. Then it waits for CI. Nothing merges until every check is green, so the agent can only move as fast as my CI tells it whether the work is right. Every failed check means another fix and another full run. Right now CI is the slowest part of the whole loop, and that’s backwards.

CI has to get drastically faster. Done well, it’s what makes agents a lot faster, because they get an answer while they’re still working on the change. Done badly, it’s the thing slowing everything down.

That means optimizing the whole feedback loop: quick checks that catch mistakes early, independent jobs running in parallel, warm caches and enough runner capacity to keep work moving. I want the same thorough verification with less waiting. Dropping checks to get a faster green result would defeat the point.

Where I’m taking it

Four compact computers grouped into local AI and CI with verification.

The next step is two Minisforum MS-A2 machines, small enough to sit on a shelf.

The plan is to use one for CI runners so I can stop paying for overflow and keep caches warm between runs. The other would host the products under test: full stacks running while headless browsers click through them and security tests try to break in. I’m not sure yet whether they stay separate or work as one pool. Either way, the point is to spend less time waiting for evidence that a change works.

I use Claude and Codex alongside local AI. The local machines are already running: a DGX Spark and an ASUS GX10 sharing a model. I use that model for coding much less than you’d think. Most of its time goes to product research, gathering feedback from Reddit and other platforms, and drafting marketing and sales material. It also reviews pull requests and serves the chat on my home network. Local inference has hardware and electricity costs, but no per-token bill.

So the local infrastructure I’m building toward is four small boxes: two for AI, two for CI and verification. That runs alongside the hosted agents I already use.

I expect more engineers to need capacity like this, whether they own the machines or rent it. As agents produce more changes, the ability to check those changes becomes a constraint. For me, a few boxes at home are a practical way to work on that constraint.

Running it from my phone

A hand dictating tasks into a phone connected to a remote computer.

A phone is a terrible place to write code and a fine place to write a task. Going mobile first forced me to go task first.

So the work lives in GitHub issues. Each issue is a small spec. Agents read their queues from issues: search demand for new articles, requests for new diagrams, pickups users asked for, security advisories, errors from production. They pick one up, open a pull request, and the guardrails decide whether it ships. Issues that need me, like a reply to someone, land in my queue instead.

For the things that need a conversation, I have an always-on Claude Code running in a pod on one of the mini PCs. My phone is always on WireGuard, so I SSH in from anywhere with Termius, attach to the session and pick up where I left off. I barely type there either. I dictate with Wispr Flow straight into the terminal. When Claude needs a decision, it pings me on Discord. The ping says only that it needs me, nothing about the work, because that channel is shared.

Getting there took some work. Claude Code’s remote feature was disabled by my organization’s policy, so I went with plain SSH. Overlapping network ranges also kept my phone from reaching the cluster until I added routes by hand. Once the connection worked, I could stay involved in the decisions without sitting at my laptop.

The job now

The engineer decides what to build, delegates code to agents and defines how to verify it.

The phone works because most of what I need to contribute fits in a conversation or a brief. I still have to investigate the problem and decide what is worth building. I also have to give the agents a way to check their work and recognize when those checks aren’t enough.

That is where more of my engineering effort goes now. A faster agent is useful when I can find its mistakes quickly enough to keep up.

I barely type code anymore. There is still plenty of engineering to do.

Read next

Share & discuss

New writing, occasionally.

New posts straight to your inbox. No spam, unsubscribe anytime.

What do you think?

Sign in with GitHub to join the conversation.

Enjoyed this? Subscribe via RSS to get notified when I publish new posts.