You have three features in flight, each in its own agent session, all in the
same repo. Git handles this fine — branches and worktrees have isolated
parallel working state for years. What breaks is coordination: which
session owns which plan, who writes which tracking file, and in what order N
finished branches integrate into main.
momentum’s answer is the lane.
Lanes
A lane is one concurrent workstream, made of three pieces:
| Piece | What it is |
|---|---|
| A branch | usually in its own worktree — the isolated git state |
| A plan node | the phase (or ad-hoc record) the branch is bound to |
| An agent session | the session working that branch, oriented by that plan |
One lane is just the normal momentum workflow — a phase on a branch. Lanes matter when there’s more than one: each session resolves its own phase from its own branch, writes its own phase artifacts, and lands at the shared integration point in a defined order.
The plan itself is a recursive graph:
ecosystem ⊃ repos ⊃ phases ⊃ tasks
Parallelism is legal at any node whose children are independent, and one
rule applies at every level: independent siblings fan out; dependencies
form waves; everything lands sequentially at its integration point. Two
independent phases in one repo run as two lanes landing on main. Two
independent repos in an ecosystem fan out the same way, landing into the
initiative. The integration point changes; the rule doesn’t.
Which phase is yours
With two sessions in one repo, “the active phase” stops being a single thing. Each session resolves its own:
- Branch binding first. Branch
phase-N-shortnamebinds to directoryspecs/phases/phase-N-shortname/. If you’re onphase-7-auth, your phase isphase-7-auth— regardless of what any other session is doing. status.mdas fallback and overview. The Active Phase table inspecs/status.mdholds one row per active lane (Phase | Branch | Status | Progress). Branches that don’t bind to a phase directory — and detached HEAD — resolve through this table. It’s also how any session sees what else is in flight.
Non-phase branches (fix/*, feat/*, chore/*) bind to the ad-hoc lane
(specs/adhoc/) — quick tasks and spikes get the same unambiguous ownership
without a phase scaffold.
The table is the board of record: add your row when a lane opens, update only your row while it runs, mark it when it lands.
Isolation: substrate by detection, never dependency
momentum deliberately does not manage worktrees, pools, or sessions —
that layer is solved, and solved well, by dedicated tools. momentum detects
and recommends; it never requires. Install stays momentum init, nothing
else.
Plain git worktree is the zero-install default. Two commands give a
second session its own working directory on its own branch:
git worktree add ../myapp-search phase-12-search
cd ../myapp-search # open your second agent session here
Both directories share one repository — same history, same remotes, fully independent working state.
treehouse — a worktree
pool manager — when cold starts hurt. Fresh worktrees start with empty
node_modules and cold build caches; treehouse keeps a pool of reusable
worktrees with warm caches, so a new lane is ready in seconds instead of
minutes.
GitButler takes a different route entirely: virtual branches apply N branches to one working directory as lanes you can move changes between, with each agent session pinned to a virtual branch. No worktrees at all — a genuinely different answer to the same problem, and fully compatible with momentum’s conventions.
Pick whichever substrate fits. The lane model above is the same on all three.
Landing: main is the runway
Finished lanes don’t merge whenever they feel ready — main is the runway,
and one lane lands at a time:
- One lane merges (with the usual Rule 6 approval gate).
- The full suite runs green on the updated
mainbefore the next landing. - Remaining lanes rebase onto the updated
mainbefore they land. - Stacked (dependent) lanes land parent-first; a child rebases onto its
parent until the parent lands, then onto
main.
Never land two lanes back-to-back without the suite passing in between. A
green suite on each lane’s branch proves each lane works alone; only a
green suite on the updated main proves the combination works. Sequential
landings are the consensus practice across parallel-agent development for
exactly this reason — the merge point is where independent-looking changes
collide.
Lane-scoped tracking
Concurrent lanes only stay sane if their writes don’t collide. momentum’s spec layer splits cleanly:
| Files | Ownership | Rule |
|---|---|---|
specs/phases/<your-phase>/ — tasks.md, history.md, evidence/ | Your lane only | Write freely — parallel-safe by construction, no other lane touches them |
specs/status.md, specs/backlog/backlog.md, specs/changelog/ | Shared | Append or touch your own row/line only — never reformat, renumber, or rewrite other lanes’ entries |
Per-phase artifacts can’t conflict because each lane has its own directory.
Shared files stay trivially mergeable as long as every lane adds its own
lines and leaves everyone else’s alone. Cross-lane edits — fixing another
phase’s tasks.md, updating another lane’s status row — are off-limits from
inside a lane: tell the user or file a backlog item instead.
Off-lane work
Not everything needs a lane. Two kinds of work are off-lane by design — zero tracking contention:
- Brainstorms (
/brainstorm-idea,/brainstorm-phaseexploration) write no files at all until you approve a plan. - Spikes — time-boxed, throwaway exploration — write only their own
specs/adhoc/<id>/record.
Neither touches the Active Phase table. You can brainstorm the next phase while three lanes run, with nothing to coordinate.
The mechanism: momentum lanes
The conventions above got their mechanism in the Run step of the
Parallel Lanes arc — a lane registry, ambient board, cross-session
signals, and a merge queue, all plain files at the shared git dir
(git rev-parse --git-common-dir), shared by every worktree, no daemon:
momentum lanes # board: every lane + queue pressure
momentum lanes open feat/search \
--touches src/search/** # opens a worktree at ../<repo>.lanes/,
# warns on touch-path overlaps (advisory)
momentum lanes signal <id> message "rebase when you can"
momentum lanes inbox <id> --ack-all # sessions read signals at checkpoints
momentum lanes done <id> # enter the landing queue (FIFO)
momentum lanes land <id> --execute # turn + rebase-freshness + graded gate,
# then merge --no-ff; nudges other lanes
Landing gates are graded by work type: a spike lands gate-exempt, a
quick-task needs its specs/adhoc/<id>/record.md, a phase needs a
retrospective with verification evidence. Freshness (the integration ref
already rebased into the lane) is never forceable. The board’s
queue-pressure footer shows done-but-unlanded lanes piling up before
trust erodes. The lane-state file format is internal for now — it gets
published as a contract once dogfood-stable.
Planning waves: momentum waves
The Fly step ships one recursive wave planner — the same engine at every level of the plan graph, with swarm as its top-scale consumer for cross-repo delivery. Declare dependencies where the plans already live:
- task groups:
## Group 2 — CLI (deps: G0, G1)intasks.md(fully-checked groups count as satisfied); - phases:
"deps": ["phase-1-auth"]inspecs/phases/index.json(complete phases count as satisfied).
momentum waves # phase-scale waves + wave-1 lane suggestions
momentum waves --tasks # group waves for the phase bound to your branch
Three ideas become three planned lanes: waves computes which can start
now, lanes open gives each its worktree, and finished lanes land
sequentially through the queue — when a wave completes, the next waves
run unblocks its dependents.
Walkthrough: two features in parallel, start to finish
Everything below is real command output (trimmed). Say you have two features and one follow-up that needs them both.
1. Declare the dependencies and plan the waves. Add "deps" to
specs/phases/index.json (or (deps: …) on task-group headings), then:
$ momentum waves
phase waves (non-complete phases; index.json "deps"):
wave 1: phase-1-auth phase-2-api
wave 2: phase-3-ui
wave 1 can start now — open a lane per node:
momentum lanes open phase-1-auth
momentum lanes open phase-2-api
2. Open a lane per wave-1 node. Each gets its own worktree — a separate directory on its own branch, ready for its own agent session:
$ momentum lanes open phase-1-auth --touches src/auth/**
✓ worktree created at ../myapp.lanes/phase-1-auth
✓ lane 'phase-1-auth' open — branch phase-1-auth, plan node phase:phase-1-auth, grade phase
Open your agent sessions inside those directories. Each session knows which phase is its own (branch binding), and preflight warnings surface fresh-worktree traps (missing exec bits, wrong node version) at open time.
3. Check the board from anywhere. Any session, any worktree, same answer — no daemon, it’s computed from files:
$ momentum lanes
● phase-1-auth phase:phase-1-auth phase open 2h ✉1
● phase-2-api phase:phase-2-api phase done 2h
queue: 1 done awaiting landing — oldest waiting 10m
The ✉1 is an unread signal; the footer is queue pressure — done lanes
waiting on you to land them. Message a session from outside it:
$ momentum lanes signal phase-1-auth message "api landed first — rebase when you can"
$ momentum lanes inbox phase-1-auth --ack-all # (from inside that lane)
4. Mark done and land, one at a time. land validates before it
touches anything — turn, freshness, and the evidence gate for the lane’s
work type:
$ momentum lanes done phase-2-api
✓ lane 'phase-2-api' marked done — position 1 of 1 in the landing queue
$ momentum lanes land phase-2-api --execute
landing checklist for 'phase-2-api' → main:
✓ status: done
✓ turn: head of the landing queue
✓ freshness: 'main' is contained in 'phase-2-api'
✓ gate[phase]: retrospective Verification Evidence present
✓ lane 'phase-2-api' landed on 'main'
ℹ advisory rebase signal sent to 1 open lane(s): phase-1-auth
If a lane hasn’t rebased onto the updated main, land refuses —
freshness is never forceable. Run your suite on the updated main, then
land the next lane the same way, and close --rm-worktree when finished.
5. The next wave unblocks itself. Once wave-1 phases are complete,
momentum waves shows phase-3-ui in wave 1 with its own
lanes open suggestion. That’s the whole loop: plan → fan out → land in
order → repeat.
How it works under the hood
- State is plain files at the shared git dir —
git rev-parse --git-common-dirresolves to the same.gitfrom every worktree, so the registry, per-lane manifests, and inboxes under<git-common-dir>/momentum/lanes/are shared by all sessions, untracked by construction (git never tracks.gitcontents), and gone when the repo is. Nothing to commit, nothing to gitignore. - No daemon, no watcher. Every command computes its answer from files at the moment you run it. Concurrent writes go through a lock; reads are lock-free.
- The conventions and the mechanism are the same thing. Rule 15
(branch↔phase binding, own-row tracking, sequential landings) is what
agents follow;
lanes/wavesmake it observable and enforced. The toolkit dogfooded this — the board and signals modules were built inside lanes opened by the CLI itself and landed through its own merge queue. - Uninstall = delete the files, same as everything else in momentum.
The lane-state format is internal today (
stateVersion: 1) and will be published as a small contract once it’s dogfood-stable, so external dashboards can render momentum lanes without owning them.
Related: Concepts for phases and history · Rules for the git lifecycle and tracking discipline · Swarm for multi-repo parallel delivery.