Working in Parallel (Mostly)
Worktrees, the bd worktree warning, and the one Docker constraint we have not solved yet · ~14 min read ~– min read · Suggested by Bob engineer
Worktrees let two streams of work coexist without stepping on each other. On Strike specifically, that is mostly true — with one caveat about Docker we have not solved yet. This cairn covers the mechanics, the one warning that bites every new contributor, and where the parallelism actually pays.
The .worktrees/ pattern
Every Strike worktree lives under .worktrees/<name> in the repo root. The directory is gitignored, so a worktree does not show up as untracked files in your main checkout. Names are short, descriptive, kebab-case — .worktrees/auth-rewrite. The convention matters because the agent will reach for it without you spelling the path.
Worktrees are a structural tool, not just a convenience. They are how you let an agent work on something else without stepping on what you are doing.
The agent must use bd worktree create, not git worktree add
The warning that costs new contributors a session if they (or their agent) miss it. Always reach for bd worktree create .worktrees/<name>. The bd command does the worktree creation and writes a .beads/redirect file pointing at the main repo’s database, which is what lets bd ready and the rest see the project’s issue graph from inside the worktree. Plain git worktree add skips that and leaves beads blind.
If the agent reaches for git worktree add, redirect it. The signal: a fresh worktree where bd ready shows nothing while the main repo has work in flight. In older beads versions this caused real data loss.
Why worktrees pay off
Three reasons. Branch isolation: your main checkout stays on main for reading and review while feature work happens in a worktree. Concurrent work: two streams in motion — you on the auth rewrite, an agent on a typo fix — each with their own beads and merge cadence, without the stash-and-switch dance. Reshape over patch: a worktree on a branch is where you give the agent permission to be brave — rename modules, restructure aggressively — because the gates and the review pass catch anything that should not have moved.
Strike caveat: Docker compose and ports
The constraint that keeps Strike from being fully concurrent. We do not yet support multiple docker compose stacks on unique ports across worktrees, so only one full stack runs at a time. More bounded than it sounds: branch isolation without the stack still works (lint, unit tests, timbers), and non-runtime tasks — docs, frontend-only refactors against mocks, schema design, spike work — live happily in side worktrees. Concurrent stacks today require manual port reassignment; port-pinned compose per worktree is tracked, undated.
The just control plane for worktrees
What we have today is bd worktree create, git worktree list, git worktree remove. Missing is a just worktree family: switch, reset, status. Until those land, the pattern is “work in the worktrees you have; bring the stack up in the one that needs it; teardown before switching.”
Pre-merge review cadence
After each M-or-larger bead lands on the worktree’s branch, run dm-work:review or a scope-bound subagent diff review. Pre-merge, run the heavier pass: multi-reviewer, optionally with Codex. A branch with twelve beads landed has had twelve cheap micro-reviews and one macro-review by merge — more review than a flat-branch flow gets, at lower per-pass cost. XS and S beads skip the per-bead review and lean on the gates.
Plugins, today and tomorrow
Two horizons. Today: dm-work:worktrees teaches the conventions in this cairn; adjacent dm-work plugins live at dark-matter-marketplace, one maintainer. Tomorrow: a Constructured-specific plugin set is in progress, pared down, influenced by dm-work but not a copy. We will name it when it is real. Until then, install what pays off in your loop, skip what does not — plugins compete for the agent’s attention.
- How We Build Here — The "shape of work shifted up the stack" framing is what makes "give the agent a worktree to be brave in" a sensible pattern.
- Beads, the Backbone — Required reading for understanding why
bd worktree createis non-negotiable. - Your Box and Your Trust Model — The Docker-and-runtime cairn. The port-collision constraint restated here from the worktree angle.
- git-worktree — Official git documentation on the worktree mechanism.
- beads — Source code and documentation for
bd worktree createand the redirect-file mechanism.
Two pieces of work, one repository. The classic git answer is to switch branches: stash, checkout, work, switch back, restore. That works, but it is fragile — anything you forgot to stash leaks across, your editor’s open files now point to phantom diffs, and the agent’s mental model of “what’s in the working tree right now” gets confused by every switch. For agent-assisted work, especially when more than one agent is in motion, branch switching is unusably noisy.
Git worktrees solve this. A worktree is a second working tree on disk, attached to the same repository, on its own branch. Two of them coexist on the filesystem; both share the same .git/ machinery; neither disturbs the other. The pattern is decades old and worth using.
There is one twist on Strike specifically. Read the warning in section two before you create your first worktree.
The .worktrees/ pattern
Every Strike worktree lives under .worktrees/<name> in the repo root. The directory is gitignored already, so a worktree’s existence does not show up as untracked files in your main checkout. One subdirectory per concurrent stream of work. Naming convention: short, descriptive, kebab-case. .worktrees/swe-trail-plan, .worktrees/auth-rewrite, .worktrees/bug-fix-os-7zfx.
The convention matters because the agent will reach for it. When you say “make a worktree for the auth rewrite,” the agent should land on .worktrees/auth-rewrite/ without you spelling the path. The .worktrees/ prefix is the convention; what you call it after that is yours.
Worktrees are a structural tool, not just a convenience. They are how you let an agent (or a second agent, or a colleague’s agent) work on something else without stepping on what you are doing.
The agent must use bd worktree create, not git worktree add
The warning that costs new contributors a session if they (or their agent) miss it. The agent should always reach for bd worktree create .worktrees/<name>, never plain git worktree add. Strike’s AGENTS.md and the dm-work:worktrees skill (where installed) both teach this, and the agent gets it right most of the time. The reason this section exists is that “most” is not “always,” and you need to recognize the failure when it happens.
The mechanics are quick to explain. bd worktree create does the worktree creation and the surrounding setup that makes the worktree work as a first-class beads context — gitignore registration, branch handling, and the beads-side bookkeeping that lets bd ready, bd close, and the rest see the project’s issue graph from inside the worktree. Modern beads (1.0.3+) discovers the shared graph through git’s common-directory mechanism; you do not have to wire anything by hand. Plain git worktree add skips the beads setup and tends to leave the worktree’s beads commands blind to the project’s actual work.If a worktree was created the wrong way and the agent’s beads commands come up empty, the safe recovery is to ask the agent to re-run bd worktree create on the same path. It is idempotent on the setup it owns. No data is lost; the worktree just was not seeing the graph.
If you see the agent reach for git worktree add, redirect it. The signal is concrete: a fresh worktree where bd ready shows nothing even though the main repo has work in flight. This is a common source of confusion and, in older beads versions, real data loss, and it is exactly the kind of mistake an agent will repeat across sessions if it goes uncorrected the first time.
Why worktrees pay off
Three reasons the worktree pattern is worth the small overhead, in order of how often they matter:
Branch isolation. Your main checkout stays on main (or whatever branch you do most reading from). Your active feature work happens in a worktree. Switching branches in your main checkout no longer moves work in progress; you cd into the worktree to work, cd back to the main checkout to read or review.
Concurrent work. Two streams in motion. You are working on the auth rewrite in .worktrees/auth-rewrite/; an agent is working on a typo fix in .worktrees/typo-os-9kp4/. Both can have their own beads claimed, their own commits in flight, their own merge cadence. Without worktrees, that pattern requires you to fully finish or stash one stream before touching the other.
Reshape over patch. Agents do their best work when they can rewrite rather than patch. Inside a worktree on a branch, the agent can delete unused files, rename modules, restructure aggressively — and the merge gate (the gates from Quality Gates, the review pass from Codex as Second Opinion) is the contract that catches anything that should not have moved. Inside the main checkout shared with everything else, the agent is more cautious because the cost of breaking something is immediate. The worktree is where you give the agent permission to be brave.
The third reason is the most underrated. If you find yourself wishing the agent would refactor more aggressively, give it a worktree and tell it to. The branch isolation plus the gates plus the review loop is enough containment for the agent to take real swings at the structure.
Strike caveat: Docker compose and ports
Here is the constraint that keeps strike from being fully concurrent today. We do not yet support multiple docker compose stacks running on unique ports across worktrees. The local services — Postgres, Keycloak, the rest of just up — bind fixed ports in the compose definition. Today, only one full stack runs at a time. If you have just dev going in worktree A, you cannot also bring it up in worktree B without manually reassigning ports, which we have not made painless yet.
That is a real limitation, and it is honest to call it out. It is also more bounded than it sounds. Worktrees are still useful in Strike for several patterns:
- Branch isolation without the running stack. Read code, run unit tests, run lint, run the gates, write timbers entries — none of that needs the full stack. You can have one worktree with the stack running and several others where you are doing all of the above without colliding.
- Tasks that do not touch the runtime. Doc work, frontend-only refactors that test against mocks, schema-design work, ADR drafting — all live happily in side worktrees while another worktree owns the running services.
- Spike work. Throw-away experiments where you want a clean slate. Don’t need the stack; just need a place to fail without polluting the main checkout.
For the cases that do need concurrent stacks — heavy api work in one worktree while another is also doing heavy api work — the workaround today is manual: change the compose port mappings, change the .env overrides, document what you did. That is friction, and the team has it on the list.The eventual fix is port-pinned compose stacks per worktree, with a just recipe that derives the port offsets from the worktree name. Tracked as a follow-up; no date. When it lands, expect this section to shrink.
The just control plane for worktrees
What we have today is bd worktree create (the safe creation command), git worktree list (the inventory), and git worktree remove (the cleanup). What is missing is a just worktree family of recipes that would centralize switching the active stack, resetting derived state, and the port-pinned compose work above.
That is an open work item. The shape of it is roughly:
just worktree switch <name>— bring services up against the named worktree’s stack, tear down whatever was active.just worktree reset <name>— clean derived state for the named worktree (database, generated code, build artifacts) without touching anything else.just worktree status— what is active, what stacks are running, what would conflict.
None of those exist yet. When they do, the friction in this section drops meaningfully. Until then, the pattern is “work in the worktrees you have; bring up the stack in the one that needs it; teardown the stack before switching.”
Pre-merge review cadence
After each M-or-larger bead lands on a worktree’s branch — closed, code merged into the branch — run a review pass. The team’s dm-work:review slash command (or a scope-bound subagent diff review) gives you a structured audit before too many beads pile up. Pre-merge — before you merge the worktree’s branch back to main — run the heavier review: a multi-reviewer pass, optionally with a Codex second opinion via the codex plugin. From Plan to Pull Request walks the full pre-merge ritual.
Two reasons to be disciplined about the cadence: drift catches early, and accumulated debt does not accumulate. A worktree branch with twelve beads landed on it has had twelve micro-reviews and one macro-review by the time it merges. That is more review than the same work would have gotten in a flat-branch flow, with much of it cheap because each pass is small.
For XS or S beads, skip the per-bead review and lean on the gates. The cost of running a review on a fifteen-line change rarely earns its keep. The pre-merge pass is the safety net for those cases.
Plugins, today and tomorrow
The plugin layer for worktree-friendly agent work has two horizons.
Today. The dm-work:worktrees skill, when installed, teaches the agent the conventions in this cairn — the .worktrees/<name> location, the bd worktree create rule, the cleanup discipline. Other dm-work plugins (dm-work:orchestrator, dm-work:subagent, dm-work:review) are useful adjacent. They live at rbergman/dark-matter-marketplace and are maintained today by one person, which is part of why we are spinning up the team-specific equivalent.
Tomorrow. A Constructured-specific plugin set is in progress — a private repo in the Constructured GitHub org, pared down to the commands and skills relevant to our work, influenced by dm-work but not a copy. When it lands, expect a follow-up cairn that updates this section and the equivalent recommendations across Claude Code as Daily Driver and Quiet Power Tools. We will not commit to a date; we will name it when it is real.
The general rule, until then: install the dm-work plugins that pay off in your own loop. Skip the ones that do not. Resist the urge to install everything; plugins compete for the agent’s attention.
Summary
- Worktrees live under
.worktrees/<name>in the repo root. Already gitignored. One per concurrent stream of work. - Use
bd worktree create, notgit worktree add. The bd command writes the.beads/redirectfile that shares the issue graph; the git command does not, and yourbd readyqueue will be silent. - Three reasons worktrees pay off: branch isolation, concurrent work, and giving the agent room to reshape rather than patch.
- The Docker port limitation is real. One full
just devstack at a time. Worktrees still pay for non-stack work, branch isolation, and spike work; concurrent stacks are a known TBD. - The
just worktreecontrol plane is incomplete. Switch / reset / status recipes are an open work item; until they land, the patterns are manual. - Review cadence: per M+ bead, then pre-merge. Lots of small reviews beats one giant one. XS/S beads can skip the per-bead pass and lean on the gates.
- Plugin layer is in flux. dm-work today, Constructured-specific set tomorrow. Follow-up cairn when the team set lands.
- The "give the agent a worktree and tell it to be brave" pattern only works if the gates and review loop are tight. In your own loop, are the gates tight enough that you'd genuinely let the agent restructure aggressively in a worktree, or do you find yourself watching it more closely than that?
- The Docker port limitation is real today. If you had a free afternoon to fix it, how would you shape the fix — port offsets derived from worktree name, a per-worktree compose override file, something else? What breaks first when you generalize?
- The pre-merge review cadence assumes worktrees that span multiple beads. Some teams keep worktrees so short that one bead's worth of work merges directly. What's your preferred ratio of beads-per-worktree, and what does it tell you about how you scope work?
- How We Build Here — The trail's opening cairn. The "shape of work shifted up the stack" framing is what makes "give the agent a worktree to be brave in" a sensible pattern.
- The Workshop — The trail's tool map. Worktrees and the dm-work plugin layer both sit in the recommended row of that table.
- Beads, the Backbone — Required reading for understanding the
.beads/redirectmechanism and whybd worktree createis non-negotiable. - Your Box and Your Trust Model — The Docker-and-runtime cairn. The port-collision constraint discussed there is restated here from the worktree angle.
- git-worktree — Official git documentation on the worktree mechanism. Required reading if you want the deep mechanics under
bd worktree create. - beads — The beads project. Source code and documentation for
bd worktree createand the redirect-file mechanism. - dark-matter-marketplace — Source for the dm-work plugin set referenced throughout this cairn. Single-maintainer today; the case for the team-specific set in progress.
Generated by Cairns · Agent-powered with Claude