CODEX TEBOUHow to Be a Beast
Codex TebouThe Workshopf. 40r

Table IV · The Workshop

Parallel Worktrees

Cole's fan-out launcher: one manifest line per task, and pw gives each its own git worktree, tmux window and Claude Code or Codex session, with a merged log every session reports to and a teardown that cleans up after.

f. 40r183 stichoiemended Oct 28, 2026cited by 17

Capitula · contents
  1. When to use it
  2. The manifest
  3. Launching
  4. The merged log
  5. Watching the lanes
  6. Tearing down
  7. Gotchas
  8. After the fan-out

Parallel worktrees is Cole’s launcher for fanning one plan out to many agents at once. You write one line per task in a manifest; pw launch gives each task its own git worktree, its own tmux window and its own Claude Code or Codex session with the task’s prompt already loaded, and opens a merged log that every session appends its summary to when it finishes. When the work is merged, pw teardown removes exactly what that launch made.

It is a skill and a set of small scripts in the public worktrees plugin: pw (launch, watch, broadcast, tear down), pw-append (the locked writer for the log), and underneath them wt-helper, the same primitive behind the solo wt command.

StudiumWorktree fan-out

Choose how many lanes, press Launch, or pick a lane to see what it gets.

4 lanes
The manifestThe repositoryWorktreesWindow 0 · the merged log0:log1:search2:export3:docs4:audit5:i18n6:perforigin/HEADsearchclaudefeat/search-filters.claude/worktrees/search.env.npmrc.envrcnpm i✻exportclaudefeat/csv-export.claude/worktrees/export.env.npmrc.envrcnpm i✻docsclaudedocs/api-reference.claude/worktrees/docs.env.npmrc.envrcnpm i✻auditcodex:sol:xhighreview/security.claude/worktrees/audit.env.npmrc.envrcnpm i◎i18nclaudefeat/i18n-strings.claude/worktrees/i18n.env.npmrc.envrcnpm i✻perfcodex:luna:highperf/cold-start.claude/worktrees/perf.env.npmrc.envrcnpm i◎

One launch

Each row of the manifest names a lane: a tag, a branch, the prompt and, if you like, the engine. Press Launch to watch pw turn rows into worktrees, worktrees into tmux windows, and finished work into the merged log.

The diagram follows one launch. A manifest’s rows become worktrees branched from the remote’s default branch, each bootstrapped with the files a fresh checkout lacks; each worktree gets a tmux window running its agent; window 0 follows the merged log, and every agent’s summary arrives there between sentinel lines.

When to use it

Cole’s rule for parallel work is to pick the axis first:

  • Subagents in one session when the work is read-only or the coordinator will pick between drafts: one tree, nothing to merge.
  • Parallel worktrees when several writers would touch the same files, share node_modules or a build directory, run the same Docker Compose project, or add database migrations. Each lane gets hard isolation: its own branch, its own files, its own build state.
  • Both when each lane is big enough to split again: a worktree session can run its own subagents.

A plan with three to seven independent slices is the sweet spot. Slices that depend on each other’s interfaces need a contract first; the contract-fanout skill in the same plugin runs that variant, with versioned contracts posted before anyone builds and two reviewers per slice.

The manifest

A manifest is a tab-separated file, one row per lane: a tag, a branch, the prompt, and optionally the engine. The example in the repository:

search	feat/search-filters	@/path/to/prompts/01-search-filters.md
export	feat/csv-export	@/path/to/prompts/02-csv-export.md
docs	docs/api-reference	@/path/to/prompts/03-api-reference.md
audit	review/security	@/path/to/prompts/04-security-audit.md	codex:sol:xhigh
  • Tag: a filesystem-safe name (letters, digits, dashes). It names the worktree and the tmux window.
  • Branch: created if it does not exist.
  • Prompt: @ and an absolute path to a prompt file, or a literal one-line prompt. A missing prompt file stops the launch before any worktree is made.
  • Engine (optional): empty or claude for Claude Code; codex, codex:MODEL or codex:MODEL:EFFORT for Codex, where sol and luna are short aliases for two GPT models and the effort is Codex’s reasoning effort.

Manifests go in a known directory, so a later session can find a run’s log and tear it down.

Launching

pw validate m.tsv                     # lint: missing prompts, missing log footer
pw launch m.tsv                       # every lane, Claude Code by default
pw launch m.tsv --engine=codex        # every lane on Codex
pw launch m.tsv --test                # sandboxed names: test- worktrees, test/ branches
PW_CLAUDE_CMD='yu --model opus --effort max' pw launch m.tsv

PW_CLAUDE_CMD sets the command typed into each Claude window (by default claude --dangerously-skip-permissions). Pointing it at a launcher like yu and the Claude Launchers, or at the Bedrock launcher, moves the whole fleet onto a different route or effort. Codex windows always use the ChatGPT login (the launcher unsets any API key in the window) and run with Codex’s approvals and sandbox bypassed by default, because unattended lanes need git and gh with network access.

What happens for each lane

  1. A worktree. pw asks wt-helper for one branched from origin/HEAD, the remote’s default branch, at a tag-based path nested inside the repository under .claude/worktrees/, which is git-ignored. A repository can move its worktrees elsewhere, such as an external drive, with the same worktree.baseDirectory setting the solo wt command reads.
  2. A bootstrap. A fresh checkout lacks every ignored file that made the main one work, so pw copies the ones that exist: .env, .env.keys, .env.local, .npmrc, .tool-versions, .nvmrc, .node-version and docker-compose.override.yml. It writes an .envrc with COMPOSE_PROJECT_NAME=<repo>-<tag>, runs npm install when there is a package.json, and finally runs PW_BOOTSTRAP_HOOK if you set one. See what to carry over and Env Files and dotenvx.
  3. A tmux window. The window exports the lane’s details (PARALLEL_TAG, PARALLEL_BRANCH, PARALLEL_LOG, PARALLEL_APPEND, the session id and the transcript path, and COMPOSE_PROJECT_NAME), then starts the agent with a fixed session id and the prompt as its first message. The session is interactive and stays open for follow-ups.
  4. A log header. Each lane’s transcript path is written into the merged log’s header, so a reviewer can open any session’s full transcript later without hunting for it.

Window 0 is named log and follows the merged log; the agents live in windows 1 to N.

The merged log

Every prompt must end with the same footer, which writes the session’s summary between sentinel lines:

{
  printf '\n<<SESSION tag=%s branch=%s worktree=%s finished_at=%s>>\n' \
    "$PARALLEL_TAG" "$PARALLEL_BRANCH" "$PWD" "$(date -Iseconds)"
  cat <<'SUMMARY_EOF'
<YOUR UNDER-150-WORD SUMMARY HERE — include: what you changed, tests status, any deviations>
SUMMARY_EOF
  printf '<<END SESSION %s>>\n' "$PARALLEL_TAG"
} | "$PARALLEL_APPEND"

pw-append takes a lock before writing, so two sessions that finish in the same millisecond never interleave. The lock is a directory created with mkdir, which is atomic on every local POSIX filesystem; flock would have been simpler but does not ship on macOS. The sentinels make the log machine-readable: a merge agent can pull one lane’s summary out with a single grep.

pw log opens the log in less +F, which follows new lines like tail -F until you press Ctrl+c to scroll and search.

Watching the lanes

pw status <session>                   # one line per pane: target, state, command
pw broadcast <session> followup.md    # the same prompt to every Claude pane
pw log

pw status classifies each pane as claude (input box, safe to type into), shell, permission (a tool permission prompt), workflow-prompt (a confirmation to run a dynamic workflow) or unknown, and exits 2 when any pane is waiting on a human. Run it a few minutes after an unattended launch: a session can sit at a confirmation for an hour with no error, and nothing moves until someone answers. For lanes on several machines, Drover’s board shows the same thing fleet-wide, and its files.wait op lets a coordinator block on the lanes’ status files instead of polling.

pw broadcast sends one prompt to every Claude pane in a session, for a refinement after launch or a fleet-wide instruction. It skips panes at a shell and never types into a pane that is waiting on a human, because the option preselected in a permission prompt would be approved by the broadcast’s closing Enter. Long prompts are chunked; anything over ten thousand characters is written to a file and the panes get a one-line “Read this file” pointer instead. Afterwards it checks that each pane actually started working.

Tearing down

pw teardown m.tsv                     # this manifest's sessions and worktrees
pw teardown m.tsv --delete-branches   # and its branches

Teardown reads the repository and the worktree paths from the run’s log header, so it works from any directory and removes only what that manifest’s launches made: its own tmux sessions (never another manifest’s that shares a name), and only directories git lists as linked worktrees of that repository.

Gotchas

  • Launch from the main checkout. From inside a linked worktree, the new worktrees would nest inside that worktree and copy its env files instead of the main checkout’s.
  • tmux matches -t name by prefix. With only app-parallel-test running, tmux kill-session -t app-parallel kills the test session. pw targets its sessions as -t "=name", which matches exactly; scripts of your own should too (in zsh, quote it).
  • The first prompt runs immediately. Slash commands such as /effort or /model are client commands, not text the agent can act on, so they cannot be part of the launch prompt. For a lane that needs one, launch with a standby prompt (“Reply READY and wait”), send the command, then point the session at its brief.
  • Under heavy load, the Enter after a paste can be dropped. The prompt sits in the input box below the separator with no spinner. One extra Enter fixes it and is harmless on an empty box; never paste the prompt again.
  • The bootstrap installs with npm. In a Bun repository that builds a second, divergent dependency tree. Launch with PW_NPM_INSTALL=0 PW_BOOTSTRAP_HOOK='bun install --frozen-lockfile'.
  • Budget disk first. A Bun-installed worktree of a mid-size monorepo is about 1.9 GB, so five lanes need about 10 GB. Check df -h and skip the install for lanes that only write docs.
  • Your shell can override .env. Docker Compose fills ${VAR} from the invoking shell’s environment before the .env file, so a region or credential exported in your shell silently wins. Compare env with .env when a container behaves as if it had the wrong settings.

After the fan-out

A launch is half the job. Each lane ends with a pushed branch and a summary in the log; then the coordinator reviews each branch at a pinned commit with two independent reviewers, verifies every finding against the real code before asking the lane to fix it, and merges what passed (see Tests for what “verified” has to mean). A lane that runs low on context mid-task hands itself off like any other session (Handoff Chains). The review side is described in Cole’s review docs, and the The Agent Fleet page covers the roles.

To try it:

/plugin marketplace add coletebou/dx
/plugin install worktrees@dx

The plugin puts pw on the Bash tool’s PATH while it is enabled; wt-helper comes with the dotfiles part of the repository (./install.sh --with worktrees).

f. 40v The back of the leaf

Cited by

Cites

Fontes · sources

Colophon of this leaf

↑ ↓ to chooseEnter to openEsc to close