How I Work

Agentic Engineering

I design and run multi-agent development loops — fleets of Claude Code and other CLI coding agents that plan, implement, review, and merge their own pull requests, with GitHub as the single source of truth and humans in the loop for judgment calls. Codewright, a knowledge base of terminal recipes for developers and coding agents, is built almost entirely this way: Planner, Worker, Reviewer, Janitor, and Orchestrator agents coordinating through GitHub Issues, atomic claims, and branch protection — no side-channel state, no bespoke orchestration database.

The Loop

The core idea is simple: treat the software delivery pipeline itself as the agent's task queue, and make GitHub the only place that state lives. No dashboard, no message queue, no database of "what the agents are doing" — just issues, branches, pull requests, and labels, which means any agent-agnostic harness (Claude Code, Codex, or a human) can join the same loop and stay coordinated with the others.

The loop

Every agent — worker, reviewer, or planner — runs the same protocol:

  1. Read state from GitHub (open issues, their labels, open PRs).
  2. Claim exactly one unit of work atomically, so two agents can never grab the same task.
  3. Develop the change on its own branch.
  4. Open a pull request back into the integration branch, ready for review — never as a draft.
  5. A different agent instance (or a human) reviews it against the linked issue and the project's written specs.
  6. On a passing review and green CI, the PR auto-merges and the issue closes.

Why GitHub as the only source of truth

Putting every piece of coordination state in GitHub — not a side-channel database — means the loop is inspectable by anyone with repo access, survives an agent session dying mid-task, and lets me mix long-running local CLI sessions, cloud sessions, and scheduled cron-style kickoffs in whatever combination the day calls for. If an agent's context runs out or its process gets killed, the next one just reads the same issue and labels and picks up where coordination left off — nothing was ever only in that agent's head.

Branch and environment model

I keep day-to-day integration separate from what's actually deployed: a non-deploying integration branch that all agent work targets, a staging branch that only receives batch promotions once a set of changes is verified together, and production, which only receives promotions from staging. Agents squash-merge their own feature work into integration, but promotions between the long-lived branches use merge commits so the branches never permanently diverge.

Roles

I split the loop into five roles, each with a narrow job:

  • Planner — reads the roadmap and open issues, files well-scoped, claimable tasks with clear acceptance criteria, and keeps a healthy backlog of ready work at all times so workers are never idle or guessing at scope.
  • Worker — claims exactly one ready task, implements it end to end, and opens one pull request per issue. If a task turns out bigger than expected, it ships the smallest useful slice and files a follow-up rather than growing the PR.
  • Reviewer — a different agent instance from the one that wrote the code (an agent is never allowed to review its own work) checks the diff against the issue, the project's specs, and its conventions, then delivers an explicit approve or request-changes verdict.
  • Janitor — a small, deterministic, keyless process (no model calls at all) that runs on a schedule to release stale claims nobody has touched in hours and nudge pull requests that have gone quiet, so the loop can't silently stall.
  • Orchestrator — supervises the fleet directly from the Codewright Agents macOS app instead of a one-off kickoff prompt: watching every running session, keeping the right mix of roles active, and stepping in where a single agent shouldn't decide alone.

Each role can be a long-running local session, a self-pacing loop that keeps re-claiming work until none is left, or a scheduled kickoff that fires on a cadence and does exactly one pass. Mixing all three concurrently is fine — the claiming protocol is what keeps them from colliding, not which harness launched them.

Coordination

Concurrency between agents has to be safe by construction, not by convention, so I lean on primitives GitHub already guarantees rather than inventing a locking layer:

Atomic claiming

A worker claims work by trying to create a uniquely named branch ref for that issue. Git ref creation is atomic on the server, so when several agents race for the same task, exactly one creation succeeds and every other contender immediately falls through to the next candidate. Only after that succeeds does the claiming step assign the issue and flip its status label — so the label itself is never the thing two agents could race on.

Labels as a state machine

Every issue moves through an explicit label sequence — ready, claimed, blocked — and every PR carries its own reviewer verdict as a label. An agent only ever acts on the state it can see written in a label; nothing is inferred from timing or memory of a previous turn. That also means any agent — including one that just started with zero prior context — can look at an issue or PR and know exactly what stage it's in and what's allowed to happen to it next.

One claim at a time, subagents included

A single agent identity holds at most one claimed issue at a time, which keeps the queue from being hollowed out by one agent hoarding work while others sit idle. An agent harness is still free to spin up subagents for research or parallel exploration within that one claimed task — but only the top-level agent is allowed to touch GitHub state (claim, push, open the PR, apply review labels). That keeps "who is allowed to mutate coordination state" narrow and auditable even when the work underneath is highly parallel.

Review & Safety

Letting agents merge their own team's work needs guardrails that don't rely on trusting any single agent's judgment:

No agent approves its own work

Every session — whether it's writing code or reviewing it — authenticates as the same account, and platforms correctly forbid approving your own pull request. So a reviewer agent submits its verdict as a plain review comment first; a separate, narrow, keyless automation is the only thing that can turn an approving verdict into a real, branch-protection-satisfying approval. Every new push to the PR immediately invalidates that approval, so a revised PR always needs a fresh reviewer verdict — an agent can't quietly slip a late change past the review that already happened.

Auto-merge on green, not on trust

A PR merges automatically once — and only once — it has an approving reviewer verdict and every required CI check (lint, type-check, tests, build, and any domain-specific validation) is green. Neither condition alone is enough; a green build with no review, or an approval with red CI, both block the merge.

Human-only paths

Some surfaces never get an agent-issued approval, full stop: CI/CD workflow definitions themselves, database migrations touching auth or row-level security, and anything that handles real credentials. Those are enforced structurally (code-ownership rules), not by asking an agent nicely to be careful.

Escalation instead of thrashing

If a pull request goes through two rounds of requested changes without converging, the loop stops trying to force it and hands the decision to a human instead — a stuck disagreement between agents is a signal to escalate, not a puzzle to keep grinding on autonomously.

Case Study: Codewright

Codewright — a curated, risk-classified knowledge base of terminal recipes for developers and coding agents, served through a website, a public API, an MCP server, and an agent skill — is my clearest example of this loop running for real rather than as a demo. It's pre-alpha and, by design, built by an autonomous multi-agent development loop with me in the loop as the owner rather than as the one writing every line.

In practice that looks like:

  • A planner agent keeps 5–15 well-scoped issues ready at all times, each with clear acceptance criteria and the right area and priority labels.
  • Worker agents — sometimes several running concurrently, sometimes a single self-pacing session that keeps re-claiming the next ready issue — each ship one pull request per issue against the project's specs (a recipe schema, an architecture doc, a style guide).
  • Reviewer agents, always a separate instance from whichever one wrote the code, check every PR against its issue and those same specs before it can merge.
  • An hourly, keyless GitHub Actions job — no model calls, no cost — plays janitor: releasing claims nobody has touched in three hours and nudging pull requests that have sat idle for a day.

I built a native macOS app, Codewright Agents, to actually run this fleet: a control center for launching multiple concurrent Worker, Reviewer, and Planner sessions across Claude Code, Codex, and OpenCode, each in its own isolated workspace, with every issue, PR, review, and commit a session touches tracked back to that run. It has no container layer — sessions run as native host processes — and it started life as its own repository before I folded it into the Codewright monorepo once it stabilized.

I'm the one who decides when to launch agents and in what mix — a local CLI session, a self-pacing loop that works the queue until it's empty, a cloud session, or a scheduled kickoff that fires every 15–20 minutes — but which specific piece of work gets picked up, implemented, and merged next comes out of the protocol, not out of me micromanaging a task list. My own job shifts from typing every line to writing specs precise enough for agents to build correctly, and to being the human-in-the-loop for exactly the decisions the protocol reserves for a human: infrastructure secrets, product calls, and anything two review rounds couldn't resolve on their own.