Skip to content

Two sessions at once

One worktree per session. Two agents working on one repository at the same time get two working directories, each on its own branch, each holding its own task.

That is the whole rule. The rest of this page is why it is not a style preference, and how to lay it out.

CC binds a session to a task through the git branch — the hook reads the branch, resolves it to a task, and resolves the task to a grant. Nothing else identifies a session: no state file, no daemon, no cycle use <task> to forget to run. The thing developers already switch when they change work is the branch, so that is what CC reads.

A working directory has exactly one branch. So a working directory has exactly one task, whether the two sessions inside it meant to share one or not.

This is not a hypothetical. On 2026-08-05, a task passed the gate from the console, which created and checked out its branch in a worktree where another session was still working. The second session kept going, and four of its commits landed on the first task’s branch.

Because the hook resolves branch → task → grant, every one of those writes was authorised against a grant belonging to a task that did not make them — a grant that had the enforcement zone open for write, a zone the working session must never touch. Meanwhile the audit trail credited the wrong task with the work.

Nothing escaped, and the reason is the uncomfortable part: those four commits happened to touch only unprotected paths. What contained it was the accident of what that session was editing, not the design.

The lesson recorded with it is the one worth carrying: CC sells change management for parallel AI agents, and its session↔task binding had quietly assumed one agent per repository. Producing the failure took two sessions in one worktree — a customer’s ordinary Tuesday, not an edge case.

git worktree gives you a second checkout of the same repository, sharing one .git, with its own directory and its own branch.

Terminal window
git worktree add ../myrepo-second
cd ../myrepo-second

Keep it as a sibling of the repository, not a child. A worktree created inside the repository directory is a checkout of a repository nested in its own history, and it turns up in every file list that walks the tree — including the one the closing gate computes.

A linked worktree is a clean checkout: it carries no node_modules and no build output, so install dependencies there before you build — and before the first cycle command, which fails with MODULE_NOT_FOUND until you do. Measured on 2026-08-22 across three fresh worktrees: pnpm install --frozen-lockfile, then pnpm --filter @commitcycle/contracts build and pnpm --filter @commitcycle/cli build, about four minutes each, paid by every parallel session on its own. The contracts build is not optional even though that package is a closed zone: its emitted types are what the CLI compiles against, and a fresh worktree has none until it builds them.

Then start the second piece of work from inside it:

Terminal window
cycle start "the second thing" --goal "…" --criteria "…"

cycle start runs intake → scope → branch → gate → grant → record, and it creates and checks out the branch in whichever working directory you run it from. That is what keeps the two sessions apart: the branch is born where the work will happen.

When the task closes, take the directory away:

Terminal window
git worktree remove ../myrepo-second
  • One grant per working directory. Grants live under .zones/state/, which is never committed. It is not shared between worktrees, so each directory holds exactly one grant — the one for the branch it is on. Two sessions cannot reach each other’s access.
  • One AGENTS.md per branch. The active-task block is a tracked file, so each worktree carries its own copy on its own branch. Two sessions read two different blocks, and neither rewrites the other’s.
  • Every cycle command works inside a linked worktree. In a linked worktree .git is a one-line file pointing at the real one; the CLI follows that pointer. It did not always — a close once ran inside a worktree and was told it was not on a task branch, which was the tool arguing against its own advice.
  • cycle sync pulls the grant for the branch in that directory. Run it there, not in the primary checkout.

A branch can only be checked out in one worktree. Git refuses the second attempt:

fatal: 'main' is already used by worktree at '/path/to/other'

That is git protecting you from the shared-HEAD problem this whole page is about. Branch from the remote ref instead of the local branch:

Terminal window
git checkout -b task/CC-101-something origin/main

A worktree inside the repository gets swept up by anything that walks the tree. The closing gate ignores editor-created session worktrees for exactly this reason — a session worktree is harness machinery, not work, and a stale one blocked a real close before that exception existed. Your own worktrees get no such exemption, so keep them outside.

The grant expires on its own, per directory. A long-running second session hits its own expiry independently of the first. That is the design — nobody has to remember to close access — but it means “it stopped working” in one worktree says nothing about the other. cycle status in that directory names what is open to it.

Two sessions in the same directory. Nothing here makes that safe, and nothing in CC currently detects it: the binding is the branch, one directory has one branch, so two sessions sharing a directory share a task whether they intend to or not.

The remedies proposed after the 2026-08-05 incident — the gate creating a branch without checking it out, and the hook denying when a declared task disagrees with the branch’s task — are on the board, not shipped. Until they are, the layout above is the mechanism.

  • Two accounts on one machine — the other half: which identity a repository uses when a laptop holds more than one
  • Grants — what a grant opens, and why it expires
  • The closing gate — how a close computes what a branch actually changed
  • Install — the plugin, the CLI, and which one enforces