Git remains the main mechanism of change control, and Codex does not try to replace it. Sessions store context, checkpoints help roll back within work, but the real history, branching and review live in git. A managed worktree is a way to let several chats work in parallel without conflicting in one checkout: each gets a separate working directory and branch state. It is the best foundation for parallel work precisely because the trees physically do not overlap.
The point of a worktree is isolation without duplicating the whole repository. A separate working directory and branch state mean that one chat can change its files while another works on its own, and their edits do not step on each other. This is cheaper and more reliable than several full copies of the repository, and safer than two agents in one tree. But for the isolation you pay with attention to what exactly gets into the new worktree and what does not.
The key subtlety is which files appear in the worktree on their own. Files tracked by git are present automatically: it is a clean checkout under version control. But gitignored dependencies, generated files and local config do not appear by themselves. So the dev environment in a new worktree usually needs to be initialized anew - install dependencies, run setup - rather than expecting it to magically carry over from the main working folder.
For the needed gitignored files there is a managed carrying mechanism. The desktop app can apply a local environment setup or copy specially listed ignored files via .worktreeinclude. Importantly, only what is actually listed is carried - it is not automatic mirroring of everything ignored but an explicit, checkable list. This control is the point of the mechanism: you decide what gets into the isolated tree rather than relying on chance.
It helps to see what a meaningful .worktreeinclude looks like once. Below is an example: only the necessary disposable local files, such as a local env example or a file with the toolchain version in a cache. You return to this form when setting up parallel work: the list is kept short and conscious. The rule is simple - you carry disposable files needed to run, not everything from the main folder, whose state may be unclear.
Two bans around .worktreeinclude matter more than anything else. Do not include secrets: carrying .env with all keys into every worktree multiplies the secret across copies of the repository. And do not include huge dependency directories: copying node_modules or the like via include is slow and fragile. It is better to describe a deterministic setup script that will restore the environment from a clean state than to copy the unclear state of the main working folder, which you do not know exactly what is in.
# .worktreeinclude - only the necessary disposable local files
.env.example.local
.cache/toolchain-version
# Do NOT include secrets and huge dependency directories;
# better describe a deterministic setup script than copy an unclear stateThe engineering conclusion about parallelism is simple: isolation is justified where the work is truly independent. A managed worktree gives this isolation cheaply, but by itself it does not make two dependent tasks independent. If two chats still change one contract, separate trees only postpone the conflict until the merge. You parallelize independent areas, and the merge is done by a human or a separate integration task after tests - exactly as with any other parallel work.
The typical failures around git and worktrees are predictable. Expecting gitignored dependencies and local config to carry into the worktree by themselves. Copying secrets or giant dependency directories via .worktreeinclude instead of a setup script. Relying on a checkpoint as a replacement for a commit. And splitting into trees tasks that still depend on each other. Keep git the real history, carry into a worktree only the needed and explicit, and parallelize only what truly parallelizes.