How I Use Coding Agents
January 6, 2026
I use Claude Code and Codex for a lot of my programming work now.
This post started as notes on Claude Code, but the workflow has changed quite a bit since then. It is mostly about coding agents in general now.
These are the parts of my current setup that have proved useful enough to write down.
From a Ramble to a Worktree
My first prompt is often not much of a prompt. I use Monologue for dictation on mobile and macOS, and I will ramble about a problem: half-formed hypotheses, conflicting ideas, things I have already tried, and details that may or may not matter.
That kind of stream-of-consciousness bullshit is surprisingly good input for an LLM. It can pull out the sensible threads, separate symptoms from causes, resolve contradictions, and turn the mess into concrete questions or the first draft of a plan. I think of this as the genesis prompt. I am supplying raw material, not pretending I already know the right specification.
For local work, Superset turns that genesis prompt into a new git worktree. It infers the branch name and the title shown in Superset from the task itself. The result is a useful overview: I see what each agent is working on rather than a list of opaque branch names.
The work monorepo accounts for most of my coding-agent use, and worktrees have been especially useful for parallelizing work inside that one project. Superset acts as the GUI around each worktree: diff view, file browser, project context, and a terminal session. I also use it across personal projects.
I may have one session implementing, another researching an architectural question, another reviewing from an independent angle, and one waiting for CI or a long-running test. Each parallel task gets its own worktree and a bounded job. Worktrees stop agents editing the same files; the plan and ledger keep the shared state visible.
This does not feel much like managing junior developers. They are tools with separate contexts. It is easy enough to switch between them when each session has a bounded job and somewhere to leave what it learned.
For long-lived remote sessions, I start Claude Code under tmux on a Mac mini at home and connect through Remote Control. When I am on the road, the laptop is only a client; it does not need to remain powered up and doing the work. This is great when an idea strikes. I can put it into a session from my phone, let the agent investigate, and return later to something more useful than an empty chat.
PLAN.md, WAVES.md, and NEXT.md
I always have a PLAN.md. It is what I point at and say: this is the thing we are doing. It holds the current mission, system state, decisions, scope, and definition of done.
When the plan is ready, the goal is usually:
/goal implement PLAN.md. Use WAVES.md as your work ledger.
If loose ends or new ideas appear, put them in NEXT.md.WAVES.md matters when the task has a long horizon. It records what each wave discovered or changed, what passed validation, what remains uncertain, and what the agent improvised when reality was more detailed than the plan. After context compacts, a fresh session can see what earlier sessions actually achieved. The ledger becomes a useful partial transcript of the implementation without forcing every tactical decision back into the plan.
NEXT.md is optional overflow. It keeps a good idea from derailing the current mission. Sometimes it becomes the list for the next wave; sometimes an item graduates into a GitHub issue.
I keep all three files gitignored. They are working files for the agents. Decisions that need to last are copied into an ADR or the project documentation. Work that needs to last goes into the backlog.
Long-running tasks
A long feature needs more than a good prompt. It needs a goal, a stop condition, and memory that can survive context boundaries. /goal implement PLAN.md is well suited to that because it gives the agent a concrete outcome while the three working files tell it how to continue.
Long sessions get less useful as the context fills up. I treat roughly 20–40% remaining as the warning range: quality starts to wobble and compaction is coming. That is fine if PLAN.md still describes the target and WAVES.md records what the previous contexts did.
A substantial /grill-me session can run to 40 or 50 questions before implementation begins. The resulting goal session can then work for four or five hours, sometimes longer while I sleep. It can research, implement a wave, run checks, record the result, and continue with the next bounded step. I come back to a changed codebase and a ledger—not a transcript that stopped when I went to bed.
Getting a second opinion
Coding agents are designed to help. Give them a task and they will usually start trying to solve it. That is useful, but it can also mean they do not question a bad premise as much as they should.
A long context can make this worse because each answer is anchored by assumptions from earlier turns. When I want criticism, I often get better results from a fresh agent that has not watched the idea develop. I ask it to restate the assumptions, find the strongest objection, or examine the idea from a specific angle.
For substantial work, I often send research agents in before allowing implementation. Their brief is narrow:
investigate read-only
cite concrete files, lines, or primary sources
describe current behavior and invariants
expose risks and unknowns
return a recommendation an implementer can act on
The same pattern works beyond the repository. Agents can harvest primary research, online conversations, competing approaches, and prior art, then synthesize the useful disagreement back into PLAN.md.
I use /grill-me to establish what I actually want. Once the requirements are settled, I may use my roundtable skill to harden the plan. Roundtable launches independent, untainted specialist seats in parallel—architecture, security, data, UX, operations, or whatever the proposal needs. Each sees the same proposal but none of the other critiques. They are critics, not implementers, and their findings are synthesized rather than pasted together.
Roundtable is the thorough version. A one-off blind agent is often enough: review an API and README for developer experience, review writing in a particular voice, or assume expertise in one domain and attack the idea from that angle. A pull-request review bot should not be the first time an idea encounters serious criticism. I want criticism while the premise is still cheap to change.
What I mean by waves
A wave is not a phase in the plan, and it does not correspond to a context window. I do not plan the waves in advance.
WAVES.md is a running ledger of concrete progress towards the goal. The agent adds to it when it reaches a useful checkpoint: a piece of the plan has landed, an experiment failed, a subagent returned something useful, or the implementation hit a problem and had to improvise.
Both successes and failures belong there. That makes the file useful after compaction: the next context can see what has already been tried, what worked, what did not, and why the code now looks the way it does.
This is loosely related to autoresearch, where an agent repeatedly tests hypotheses and records every keep or discard decision before choosing the next experiment. The difference is that autoresearch is open-ended hill climbing against a metric. My waves have a fixed destination—PLAN.md—but the route is allowed to change as the agent learns more.
Subagents can work inside a wave. When their work comes back and changes the direction or advances the implementation, that junction goes into the ledger. I do not have a detailed Waves methodology; I simply tell the agent to use WAVES.md as its work ledger, and it generally works out what is worth recording.
Sandboxes for risky work
For risky changes, a worktree is not enough. We have spent a lot of time building an agent sandbox around the work monorepo, and I wrote up the general approach as a sandbox-builder recipe.
Each worktree gets one Docker container containing the whole local universe: database, API, web app, admin app, workers, mock services, and observability sinks. On macOS it is wired into OrbStack. The worktree gets a branch-derived namespace and each service gets its own hostname, such as web.<branch>..., admin.<branch>..., or api.<branch>.... Those hostnames route to the appropriate ports inside the container. Two agents can run the same monorepo at once without sharing ports, cookies, processes, or data.
The isolation starts with a fresh PostgreSQL database. A named scenario seeds it from the ground up with the users, bookings, checkout state, or other data needed for the task. The API and browser are exercising that database through the normal application paths. The isolation continues through the networking layer, so the browser uses the same service boundaries and hostnames the real application expects.
Integrations that would normally send data elsewhere are redirected into local drains. PostHog instrumentation writes to a local event ledger, so an agent can inspect the exact events and properties the production application would have sent. Sentry envelopes go to a local Sentry-compatible sink with stack traces, tags, breadcrumbs, and request details. Email, webhooks, queues, provider requests, and other side effects get similar inspectable surfaces. The point is not merely to stop them escaping: the agent can query them and use them as evidence.
The browser is part of the sandbox too. An agent can open the real web service, follow a seeded flow, take screenshots, and capture console errors, page errors, failed requests, and the final URL. A screenshot on its own is weak evidence, so it can pair that with a database row, analytics event, error envelope, email, or recorded request. This gets surprisingly close to careful manual QA.
All of the processes run under Procpane, a Rust process supervisor I wrote for this kind of setup. It gives every service a name and a health check, and exposes clean CLI commands for status, waiting, tailing logs, grepping across processes, reading from a log cursor, restarting services, and sending signals. Logs are kept in queryable ring buffers with ANSI formatting stripped, which means an agent can skim them without scraping a terminal UI or rereading thousands of lines. The sandbox CLI builds on top of that to navigate services, log levels, browser artifacts, and the different observability drains.
It is not completely local. AI inference and other platform-only services either need to be blocked, mocked, or routed to a guarded remote test service. The sandbox reports those gaps rather than pretending they are covered. That is an important part of making its evidence trustworthy.
In practice I do very little of this testing myself now. The sandbox is good enough for an agent to seed a scenario, use the application, inspect the side effects, and leave proof of what happened. It is also useful for debugging and migrations. For a difficult backfill, the agent can seed the relevant shape of data, run the migration, inspect the resulting rows and logs, adjust the SQL, and repeat without touching shared development data.
This took a lot of engineering, but it is probably the closest I have seen an agent get to doing its own manual QA. I expect more teams will end up building something similar.
Claude Code and Codex
I do not assign Claude Code and Codex permanent roles. Sometimes Claude implements and Codex reviews; sometimes the order is reversed. Sometimes both investigate the same question independently and I compare the reports.
The model matters less than the task contract:
What may the agent change?
What evidence must it return?
What does “done” mean?
Where should it record uncertainty?
What is the next independent check?
Repository instructions and handoff files mean either tool can pick up the task. If the useful state exists only in one transcript, that is a problem.
Skills and custom rules
Most of my reusable workflows live in jokull/jokull-skills. When a procedure repeats and clearly saves time, I turn it into a skill or slash command. The useful part is the workflow and its checks, not a clever prompt.
Specialized agents are usually simple contracts: a validator that only runs checks, a researcher that returns evidence, a reviewer that hunts removed behavior, or a browser agent that inspects the product as a user would. Narrow output formats tell an agent what not to do as much as what to do.
When an agent needs to understand a third-party library, I tell it to put a checkout in ~/Forks. That is much easier to search and navigate than prodding at node_modules or accumulating GitHub curls, and the source is still there for the next session.
Some lessons should go deeper than a skill. If an agent discovers an architectural or performance invariant, I want to encode it as a test, schema, tool contract, or custom lint rule. The Trip repository uses custom Oxlint rules and fixture tests for type-inference boundaries, import seams, serialized error shapes, and UI conventions.
A good example is fractional-index ordering. The query must preserve COLLATE "C" so indexes sort by byte order. An in-memory Postgres test and production can have different collations, so a locally green sort may still be wrong. A custom rule and fixture would catch it the next time an agent removes the clause.
Reviewing the work
At work we now rely heavily on bot reviews. I still skim code all the time, but I no longer expect a person to read every line before it ships. Plenty of routine pull requests do not need a colleague's pair of eyes. Human review is useful when judgement is the point: a disputed product decision, a new dependency, an architectural boundary, or a change that simply feels risky.
The automated loop is fairly ordinary:
wait for CI
fetch the review comments
classify each finding as fix, dismiss, or escalate
apply the warranted fixes
push and trigger another review
repeat until the evidence is clean
The classification step matters. A review bot can be wrong, and a technically correct suggestion can still damage the intended design. The agent should assess the feedback rather than blindly obey it, and leave a short explanation for both changes and intentional dismissals.
The more interesting review work happens when a problem recurs. Instead of fixing the same symptom in pull request after pull request, we change the harness: agent instructions, lint rules, tests, steering, or the skill that babysits a pull request through CI and review. Our discussions have moved up a level. We spend more time deciding what belongs in the agent files, how the review loop should behave, whether to adopt a library, or where to split a service, and less time trying to inspect every line.
Keep the backlog boring
My backlog is GitHub Issues. I do not use Linear, and I do not maintain a separate personal scratchpad. If an idea in NEXT.md survives long enough to deserve tracking, it becomes an issue.
I tell agents to use the GitHub CLI as much as possible. They can search for duplicates, inspect related code, create a grounded issue, and connect it to the actual branch. Keeping the backlog in one place matters more than adding another planning product.
What is still rough
CI is still slow. A well-designed babysitting loop can spend several minutes per iteration waiting for external systems.
Context limits still matter. The plan and ledger reduce the cost of compaction, but they do not eliminate the need to decide what deserves to survive.
Parallel agents also create review work. More output is not automatically more progress. Someone still has to decide which findings are real, which changes belong together, and which experiments should be discarded.
I am still not sure how much orchestration should live in code and how much should remain a prompt convention. I do not think string routing and giant system prompts will scale particularly well. Structured contracts, better tools, sandboxes, and clear parent/subagent boundaries look more promising.