Skip to main content

Command Palette

Search for a command to run...

Humans Are Always the Bottleneck

How I Build Software with Labor0 and Codex

Updated
11 min readView as Markdown

Today, a single prompt turned 46 SWC issues into 45 pull requests.

I do not know exactly how long it took. While the work was running, I was looking at my phone and playing games. By the time I checked, the work had already moved on without me.

My name is kdy1. I created SWC, an open-source compiler project, and previously worked at Deno and Vercel. These days, I spend a lot of time building Labor0, along with Watchtower and theaiplatform.app at Zephyr Cloud IO.

My development workflow has changed dramatically over the past few years. I used to do nearly everything myself. Now, almost all of my development work involves Labor0 and Codex automatic review.

I have one rule that shapes the entire process:

Humans are always the bottleneck.

Human-in-the-loop is still essential. Decisions must be made by a human, and I still review and merge pull requests myself. But I want the AI to drive the work and call me into the loop as few times as possible.

When Labor0 encounters a decision, it can send me a question through something like a web push notification. Once I answer, it continues. When a PR is ready, I review and merge it, but Labor0 does not need to wait for me before starting the next independent task.

A day of AI-native development

On the day I wrote this, I worked on Labor0, Watchtower, and SWC, in that order.

For Labor0, I wrote three PRDs:

  • An automatic learning feature similar to Devin’s Knowledge system

  • Voice controls for desktop and Apple Watch apps

  • A large refactoring of Labor0’s messenger integration, so that status updates appear naturally inside the corresponding Slack or Discord thread

I started implementing the messenger refactoring. The other two remain PRDs for now.

I always use my Write PRD skill when writing a PRD. I use GPT 5.6 Sol for this work.

The skill does not make decisions for me. I make every decision myself. What surprised me, though, is how few decisions I need to make relative to the size of the projects I run. The absolute number is not small because these projects are large, but it is still much lower than I would have expected.

The skill turns PRD writing into a conversation. It identifies decisions that need to be made, and I resolve them.

I saw this clearly while working on Watchtower, a Sentry-compatible error monitoring and observability system that I recently started building in public. Watchtower uses a spec-driven development process.

While writing PRD #13, I initially wanted to use Connect RPC because I prefer strongly typed systems. JSON over HTTP was recommended instead, so I spent a few rounds of conversation checking whether Connect had a serious limitation.

It turned out that the main concern was Rust support for Protobuf and its surrounding tooling. That was not a fundamental constraint for me. I decided that we could implement what we needed if necessary, so the PRD settled on Protobuf over HTTP. I considered introducing Connect immediately, but decided to wait until we actually need streaming.

That decision was captured in the PRD. Labor0 then turned the PRD into PR #31, which I reviewed and merged that day.

After Watchtower, I moved on to SWC.

Finding 46 bugs with GPT 6 Astra

I wanted to try GPT 6 Astra, so I opened Codex Desktop and gave it this prompt:

Use subagents aggressively. Inspect every file in the ES minifier. For each file, examine every branch and check whether any condition is incorrect. If you find an incorrect condition, construct an example input and use $add-issue to report it.

This produced 46 GitHub issues.

The $add-issue skill is an important part of the process. A large number of parallel sessions is only useful when every session receives a sufficiently detailed issue. The skill ensures that a bug is supported by evidence and turned into a self-contained, implementation-ready GitHub issue.

Once the issues existed, I gave Labor0 one prompt:

Fix every es/minifier issue created by kdy1 today.

Labor0 converted the 46 issues into 45 tasks. One task covered two issues because their scopes belonged together. A Labor0 task generally produces one PR, so those 45 tasks became 45 pull requests.

All 45 tasks ran in parallel. When tasks depend on each other, Labor0 runs them in dependency order, but these tasks could run concurrently.

GPT 6 Astra found the bugs, but it did not implement all 45 fixes. Labor0 assigns models according to task difficulty, and many of these fixes were simple enough to use GPT 5.6 Terra. I had configured the SWC project to use GPT 6 Astra for difficult, Premium-tier work.

At the time of writing, I had merged 21 of the 45 PRs. I had not closed any of them. The remaining PRs were still going through the Codex review loop or waiting for CI failures to be repaired. When a required CI check fails, Labor0 starts work to fix it. It also repairs merge conflicts when they appear.

AI reviews AI until it runs out of comments

Codex automatic review is part of my workflow across every repository, including SWC, Watchtower, and Labor0 itself.

Each time Labor0 pushes a fix commit, Codex reviews the PR again. If Codex finds another issue, Labor0 applies the feedback and pushes another commit. Codex reviews that commit, and the loop continues.

When Codex can no longer find another review item, it leaves a +1 reaction on the PR.

I configured these repositories so that reviews from the chatgpt-codex-connector bot are applied automatically. Without that setting, Labor0 asks someone with permission on the Labor0 project whether it should apply the review.

Because I run so many sessions and create so many PRs, the +1 reaction is also my review queue. I filter for PRs that received a +1 and review only those. Otherwise, finding which PRs are ready for final review would itself become a job.

The 21 SWC PRs I merged all went through this process. Codex left a +1, I inspected the code directly on GitHub, and then I merged each PR manually.

This reduces my cognitive load considerably. Codex catches many small bugs before I ever see the code. Compared with reviewing everything alone, I can focus on fewer and more important questions.

But a +1 does not mean the PR is correct. It means that Codex has run out of review comments.

Why a human still has to look

Codex begins its review without fully knowing my intent. Because of that, it can sometimes push a PR in a completely wrong direction.

In one migration PR, I wanted a cut-over migration with no rollback path. Codex was concerned that rolling the migration back could damage application data, so it changed the direction of the PR. Labor0 automatically applied that review, and Codex eventually left a +1 on the result.

When I performed the final review, the PR was no longer implementing what I wanted.

I left another review explaining that this was a cut-over migration, that we would not roll it back, and that the previous change should be reverted. Labor0 applied my feedback, and the PR was eventually merged successfully.

I also check whether the AI has added too many tests. It tends to go too far. I have seen it add tests that inspect a script by matching its text piece by piece. Tests like that add volume without necessarily improving confidence.

For SWC, I also use Codspeed during final review and always check whether a change introduces a performance problem.

My final review therefore focuses on things that automatic review can still miss:

  • Whether the implementation matches the original intent

  • Whether the PR has moved in an unnecessary or incorrect direction

  • Whether the tests are excessive or overly tied to implementation details

  • Whether an SWC change causes a performance regression

  • Whether I am personally willing to merge the result

The AI can remove a great deal of routine review work, but the final judgment still belongs to me.

Small tasks are the foundation

My first rule when assigning work to AI is to split it into sufficiently small units.

Large PRs are difficult for both humans and AI to review, and review quality falls as the PR grows. Before AI coding tools existed, GitHub recorded more than 5,000 PR reviews from me in a single year. After doing that much review, I came to believe very strongly in keeping PRs small.

I suspect AI review quality also declines partly because of context size, although that is my guess rather than a confirmed explanation.

When Labor0 receives a large task, it breaks the work into smaller tasks and produces multiple PRs. It also represents dependencies between those tasks and executes them in the required order.

Detailed issues and PRDs make this possible. $add-issue produces well-defined bug reports. Write PRD helps me resolve decisions before implementation begins. Labor0 can then distribute those bounded tasks across many sessions.

This is the process I think maintainable vibe coding requires: small tasks, explicit decisions, detailed handoffs, independent PRs, and continuous review. Labor0 is my attempt to turn that process into a platform.

From six terminal sessions to more than fifty

I used Claude Code CLI in the past. Even six concurrent sessions made me very tired because a terminal was not designed as a UI for managing parallel work. I had to keep switching between sessions.

After moving to Codex Desktop, I could manage around 15 sessions.

With Labor0, there are moments when I have more than 50 sessions active across my projects without feeling particularly overwhelmed. Labor0 development alone commonly keeps around ten sessions consuming tokens around the clock. During the SWC run, I think Labor0 itself had around 15 active sessions. Combined with the SWC work, those two projects alone pushed the total beyond 50.

And those are not my only projects. I also run projects that port typescript-go to typescript-rust, port Turborepo to Effect-based TypeScript, and work on various personal projects.

The number of sessions is possible because the issues are detailed and the work is divided. Concurrency without clear scope would only create more things for me to untangle.

Routing models by cost and difficulty

Labor0 can assign a harness and model directly for each task, or allow AI to select them according to difficulty. Cheap, Standard, and Premium are the tiers Labor0 uses for this routing.

I currently use the Standard tier for applying review feedback, with GPT 5.6 Terra or GLM 5.3. CI repairs and merge-conflict resolution use the Cheap tier, with GPT 5.6 Luna at high or xhigh reasoning.

With the regular Codex provider, my Premium tier uses GPT 5.6 Sol. When I connect OpenRouter through Labor0’s Codex Custom Provider, Premium uses GLM 5.3 xhigh. Labor0 and Watchtower both use the Codex harness with this Custom Provider setup, which is how I can select GLM models.

For implementation through Labor0, I have been moving from GPT 5.6 Terra toward the more cost-efficient GLM 5.3. When I work directly in Codex Desktop, I still use GPT 5.6 Terra for miscellaneous tasks.

Labor0 sessions run in isolated environments on AWS ECS Fargate. In my security model, that isolation means using open models presents relatively little additional threat.

Cost still matters. Labor0 feels like a software factory that can operate 24/7, and a factory running 24/7 consumes a lot of tokens. That is why I do not immediately implement every PRD. Once I know that a feature is necessary, I start implementation. Until then, the detailed PRD is enough.

This workflow will change again

I expect my development process to change again as Labor0 improves. That is one reason I wanted to write this down: it is a record of how I work at this particular moment.

I also want to share what I have learned as someone getting closer to being an AI-native developer. I have always enjoyed helping people and sharing what I know. Since I was young, I have taught junior developers, reviewed their code, and helped in whatever ways I could.

I hope more developers can look at workflows like this and learn how development can work in this era.

The human is still the bottleneck. My goal is to spend that limited human attention on decisions, intent, and final judgment—and let the software factory handle as much of the rest as possible.

More from this blog

Labor0를 만든 이유

사람이 병목이 되는 횟수를 줄이기 AI 에이전트 세션을 많이 실행하는 것 자체는 어렵지 않다. 내가 해결하고 싶었던 문제는 세션이 늘어날 때마다 사람이 모든 진행 상황을 확인하고 관리해야 한다는 점이었다. 사람을 병목에서 완전히 빼는 게 목표는 아니다. 사람은 여전히 병목이다. 대신 사람이 개입해야 하는 횟수를 줄이고, 실제로 결정이 필요한 순간에만 사람을

Aug 22, 20265 min read

작업 기록: 자동 QA 구성

최근에 Zephyr Cloud 의 프로젝트에 자동 QA 시스템을 구성했다. 그 작업 기록이다. 원래 이런 작업은 리눅스에서 하는 게 맞다고 생각했다. 그런데 나는 계속 로컬에서 디버깅해야 했고, 개발 흐름상 맥에서 바로 돌릴 수 있어야 했다. 예전에도 자동 QA를 시도한 적은 있었지만 끝까지 제대로 굴러간 적은 없었다. 그러다가 지난주 중반쯤, “이건 로

May 13, 20263 min read

kdy1: The way I think

305 posts