tmux Worktree Session
Stand up a self-contained workspace: an isolated git worktree, a tmux session to drive it, the dev stack running, and a fresh agent already working a briefing. The human attaches and the work is already in motion.
Composes two skills: tmux-orchestration for safely driving panes, and handoff for writing the briefing.
Standard layout
Session: <Project>-<work> (hyphenated, no spaces)
├── Window 1 "agent" → coding agent (Claude/Codex), cwd = worktree, given a
│ kickoff prompt pointing at the briefing
├── Window 2 "cli" → bare shell for tests and ad-hoc commands
└── Window 3 "dev" → dev stack
Three windows, one thing each — no splits. The common shape; adapt it (no dev stack, two agents, a logs window). Confirm if non-obvious. Don't add a git/lazygit window unless the user asks for one.
Inputs
Fill from context, don't interrogate:
- Work name → branch + session name. Prefer a PR/issue number if one exists.
- Agent + mode → Claude or Codex, fast/plan. Ask once if unsure.
- Base branch → usually the default; worktree tools often base on
origin/<default>, which matters for step 1.
Process
1. Write the briefing, commit it to the base branch first
Use handoff to write what the agent reads: mission, scoped task, files/patterns, conventions, guardrails. A briefing, not a history.
If the worktree tool bases new trees on origin/<default>, commit and push the
briefing to the base branch before creating the worktree — otherwise it won't
be there for the agent.
2. Discover and run the worktree tool
Look (see cheatsheet), don't assume. Common: bin/worktree add <name>,
bin/worktree add --pr 42. Fallback: git worktree add ../<repo>-<branch> -b <branch> origin/<default>.
Creation often triggers a full bootstrap (deps, DB, secrets) that takes
minutes — don't assume it hung. It may prompt for a secret (1Password,
master key, env token): that's the user's to approve — surface it and wait,
never type credentials yourself. Run it in a visible pane and wait for a real
completion signal (append ; echo DONE_$? and poll) before starting the dev
stack.
3. Discover dev-stack and test commands
Look (see cheatsheet). Prefer a headless/detached mode for your control
(e.g. process-compose -D + status/logs); a foreground TUI is fine for the
human to watch. Note the test runner — the cli window and agent need it.
4. Build the session
Mechanics via tmux-orchestration. Specifics that bite:
- Create it detached so the user's current session is untouched.
- Hyphenated name, no spaces.
- Window 1
agent:cdto worktree, launch agent; when its UI is ready, buffer-paste the kickoff, then Enter as a separate step. Keep it short — point at the briefing. Optionally hand it the sibling pane ids (cli, dev) so it can poll them itself via the tmux CLI. - Window 2
cli:cdto worktree, bare shell for tests and ad-hoc commands. - Window 3
dev:cdto worktree, run the dev stack.
Every window cds into the worktree and needs the right toolchain (see
version-manager gotcha).
5. Verify and report
- Dev stack actually healthy (poll
status/health), not just "started"; read process logs if anything failed. - Agent actually started (capture its pane, don't assume the paste landed).
- Report:
tmux attach -t <session>.
Gotchas
Version managers don't activate in spawned shells (the big one). A shell you
spawn programmatically (tmux new-session, non-login) often lacks the user's
version manager (mise/asdf/rbenv/nvm), so ruby/node/python silently falls
back to the system version and bootstrap fails (e.g. "Could not find bundler").
- Detect: compare
ruby -vetc. in your pane vs the user's. Mismatch = the smell. - Fix interactive panes: put shims on PATH, e.g. mise
export PATH="$HOME/.local/share/mise/shims:$PATH". - Fix the dev stack's child processes too. For mise, plain
mise activateis a no-op in non-interactivebash -lc— usemise activate <shell> --shimsormise exec --. If the dev-stack config uses plainmise activate, that's a real bug for agent/CI use — flag it.
Pane mechanics + safety → tmux-orchestration. Pane-id targeting, never
writing to your own pane, read-before-write, buffer-pasting multi-line prompts,
Enter as a separate step — all of that lives there. Don't restate it; follow it.
Two things worth repeating here: no spaces in session names (session:window
targets mis-parse), and quit a dev TUI with its real key (process-compose F10),
not a guessed Ctrl-C.
Discovery cheatsheet
| Need | Look for |
|---|---|
| Worktree tool | bin/worktree, script/worktree, DEVSTACK.md, just/make, else git worktree add |
| Dev stack | bin/dev, npm/pnpm dev, foreman/overmind, process-compose, docker compose, mise run dev, project verbs |
| Test runner | bin/rspec, pnpm test, pytest, go test, Makefile |
| Toolchain | .mise.toml/.tool-versions/.ruby-version/.nvmrc — confirm the spawned shell uses it |