How I Use Worktrees with Codex
The workflow I am using to run multiple Codex feature sessions without stuffing every change into the same local checkout.
I think we have a pretty slick workflow going with worktrees and Codex.
The basic problem is that I want to be able to effectively work on more than one feature at a time. That sounds simple, but once you are actually using agents all day, the local workflow starts to matter a lot more than it used to.
Before this, I had my workflow settings set up so I could work locally, create feature branches, and merge those branches back into develop. That was fine for one branch at a time. The problem was that I could not really switch between multiple feature branches and have multiple Codex sessions working cleanly at the same time. If two sessions were attached to the same local checkout, they were still sharing the same files, the same branch, and the same dev server assumptions.
So “work locally” was not really working well for me anymore.
I sort of always knew Git worktrees existed. I had used worktrees in cloud environments before, where the tool would create some ignored local worktree folder and give each branch its own checkout. I was waiting for the Codex version of that workflow to become a little more established, but eventually I just started exploring it directly and writing my own rules around it.
That has turned into my current default.
The mental model
A worktree is basically another checkout of the same repo. It is not a full separate project in the way people usually mean “clone.” It is more like Git saying, “Here is another working directory for this same repository, and this one can be on a different branch.”
Codex’s New worktree flow makes that practical inside the app. In the local setup I am using right now, Codex is creating worktrees under a path like:
~/.codex/worktrees/<id>/<repo-name>
For example, this TrySignalHire session is running from:
/Users/jessepeplinski/.codex/worktrees/7e52/trysignalhire
That matters because each session can have its own working directory. One session can be on one feature branch, another session can be on another feature branch, and the files do not get mixed together just because I am talking to two agents at once.
It is not magic. It is just a cleaner way to give each feature its own physical workspace.
Why branches alone were not enough
Feature branches solve the history problem. They do not automatically solve the local workspace problem.
If I am in one checkout and I ask Codex to work on feature A, then I ask another Codex session to work on feature B in that same checkout, I have a coordination problem. The agent can switch branches, but the working directory is still shared. Any uncommitted changes, generated files, local server state, or half-finished edits can bleed across the sessions.
That is the part that got annoying.
I do not want to think, “Wait, which branch is this terminal on?” every time I move between sessions. I do not want one feature’s .env.local, build cache, or dev server to quietly become the assumption for a different feature. And I definitely do not want two agents stepping on the same files because I was trying to be clever with one local checkout.
Worktrees make the boundary more obvious. This directory is feature A. That directory is feature B. Each one can have its own branch. Each one can be checked, built, and cleaned up separately.
The local server part
The other practical piece is localhost.
In a normal app, localhost:3000 becomes the place your brain expects to go. That is useful. I do not want to casually give every feature a random port and then spend the whole session remembering which browser tab belongs to which branch.
With worktrees, I can be more intentional. If I am actively testing one feature, that worktree owns localhost:3000. If I really need to compare two features side by side, I can run one on 3000 and another on 3001 or another explicit port. But the point is that I am choosing that, not accidentally letting two sessions fight over the same local app.
For TrySignalHire, I have been leaning toward one stable UI lane: http://localhost:3000 is the human handoff URL, and temporary smoke-test ports are separate. That keeps the normal review flow simple while still letting other worktrees run static checks, builds, or tests in the background.
The important thing is that worktrees isolate files and branches. They do not magically isolate ports. You still need a simple rule for who owns the dev server.
The setup around it
The workflow got much better once I stopped treating worktrees as a manual Git trick and started treating them as part of the agent environment.
In TrySignalHire, the rule is basically: real feature or fix work should start in a Codex New worktree based from origin/develop. Tiny docs edits or read-only investigation can still use the local checkout, but anything that could become a real change gets its own workspace.
Then the local environment has to be bootstrapped safely. I do not want to copy secrets into random folders by hand, and I do not want local QA accidentally pointing at hosted production resources. So the repo has a setup script:
npm run worktree:env
At a high level, that copies a local-safe .env.local into the worktree, locks down the file permissions, and runs a check that the important URLs and keys look like local/dev resources. Supabase has to point at localhost. Clerk and Stripe should look like development or test resources. The script prints a target summary, not secret values.
That one piece matters a lot. Worktrees make it easy to create more local checkouts. That also means it is easy to accidentally create more places where secrets can accumulate. The setup script keeps the workflow repeatable without turning every new session into a secret-handling exercise.
There is also a cleanup path:
npm run worktree:done -- <worktree-path> --delete-branch <branch>
That script is intentionally conservative. It checks that the target worktree is clean. It verifies the work has made it through the expected branches before normal cleanup. It stops a dev server if that worktree owns one. It deletes the ignored .env.local. Then it removes the worktree and prunes the stale Git metadata.
That is the part I like. The workflow is fast, but it is not just “make a bunch of folders and hope I remember to clean them up later.”
The Codex behavior changed
The biggest difference is that Codex sessions now feel more like separate workstreams instead of separate chats fighting over one checkout.
I can have one session working on a candidate-facing feature, another session investigating a company-side issue, and another session doing a small docs pass. They do not all need to be running the app. They do not all need the same port. They do not all need to be ready to merge at the same time.
But they can all exist without me constantly worrying that the local filesystem is lying to me.
That is the real win. It reduces the mental overhead around “where is this change happening?” and lets me focus more on the actual product work.
The rules that make it sane
I do not think the answer is to create unlimited worktrees and let agents run everywhere. That sounds productive for about five minutes, and then it becomes its own mess.
The rules I care about are pretty simple.
Use a new worktree for real feature work. Base it from the right branch. Give it a scoped branch name. Bootstrap the local env with a script instead of hand-copying files. Keep one clear dev server lane unless there is a real reason to run multiple ports. Merge finished slices back into the validation branch early. Clean up the worktree only after the change has actually made it through the workflow.
That is basically it.
Worktrees are not a replacement for product judgment or verification. I still need tests. I still need builds. I still need smoke checks. I still need to integrate branches carefully instead of pretending that parallel work removes merge conflicts.
But they make parallel agent work feel a lot less awkward.
My current default
My current Codex default is pretty clear now:
- New worktree for real feature or fix work.
- Start locally for read-only investigation, tiny docs edits, or intentional continuation.
- One branch per workstream.
- One stable localhost lane for human review.
- Scripts for local env setup and cleanup.
- Merge into
developfirst, validate there, and only promote to production after that.
That workflow has been especially useful on TrySignalHire because it lets me keep momentum without turning the repo into a pile of half-finished local state.
It also gives me a cleaner way to talk to Codex. I can tell the agent what kind of session it is in, what branch it owns, what local server it should or should not touch, and what cleanup path exists when the work is done.
That is the thing I would copy if I were setting this up again. Not the exact scripts. Not the exact ports. The shape of the workflow.
Give every serious agent workstream its own workspace. Make the environment safe by default. Keep localhost ownership explicit. Clean up when the work is actually through the pipeline.
That is a small change, but it makes the whole Codex workflow feel much more usable.