Lanes
Two engineers on a payments team in São Paulo had the same Claude Code session running on the same repo for forty minutes before either of them noticed. They were on different branches, different terminals, different floors of the same building. By the time they reconciled, three of the agent’s commits had been clobbered, two new ones contradicted each other, and the on-call engineer had been paged by a flaky test that wasn’t flaky — it was being rewritten by a model neither of them had briefed.
That kind of collision is what a lane exists to prevent. If you remember nothing else from this page: a lane is a fence. It tells the workspace, the agent, and your teammates that this work, on this scope, with this set of permissions, is owned by this run until it closes. Two lanes never overlap on the same files. Two agents never run inside the same lane.
The thesis: most of the chaos in agentic engineering isn’t model quality, it’s the absence of a primitive that says “this is mine, leave it alone.” Lanes are that primitive.
What a lane actually is
In storage terms, a lane is a row in lanes with a UUID, an owner (a human or an agent identity), a list of paths it has claimed, a signed start entry on the provenance ledger, and a status — open, paused, merged, or abandoned. You don’t typically think about the row. You think about the card in the Lanes panel: the title, the scope, the live diff, and the sign-off slot at the bottom.
In runtime terms, a lane is an enforcement boundary. When an agent inside lane A tries to read or write a file outside A’s claimed scope, the workspace returns a LANE_SCOPE_DENIED error before the file system is touched. The agent sees the error in its tool log and either narrows the work or asks the lane owner to expand the claim.
In audit terms, a lane is a self-contained chapter on the provenance ledger. Every read, write, model call, command execution, and sign-off inside the lane chains back to the lane’s start entry. When you export evidence for SOC 2 or for an internal post-mortem, you export lanes, not commits.
Why this isn’t just a branch
The obvious objection: “I already have git branches. Why do I need lanes?”
Branches answer the question what files are different from main? They don’t answer who is working on this?, what tools are they using?, did anyone else touch the same paths in the last hour?, or is the agent allowed to call the production database from inside this work? Branches are a snapshot primitive. Lanes are a session primitive.
You’ll often have a 1:1 mapping in practice — one lane creates one branch and merges into one PR. But the lane outlives the branch. The lane carries the model logs, the tool calls, the sign-off, and the provenance after the branch is deleted. Six months from now the branch is gone; the lane is still queryable.
Cryptographic provenance
Each lane carries a chain of signed entries — Ed25519 signatures over BLAKE3 content hashes, in append-only order. The chain head signs the previous head, so tampering with any historical entry invalidates every entry that follows. This is the same construction federation root signers use for the workspace’s evidence bundle.
Three keys matter:
| Key | Lives | Used for |
|---|---|---|
| Lane signing key | Workspace HSM | Signing tool-call entries inside the lane |
| Owner attestation key | User device | Signing the sign-off entry that closes the lane |
| Federation root key | Workspace owner only | Stapling the closed chain head into the evidence bundle |
You don’t manage any of these by hand for a normal lane. They show up if you’re integrating an external HSM (BYOK), exporting evidence for an audit, or verifying a federation receipt from outside the workspace.
Scope claims and conflicts
A scope claim is a list of glob patterns: apps/api/auth/**, infra/terraform/networking/*.tf, package.json. When a second lane tries to claim a pattern that overlaps an open lane, the workspace surfaces the collision in both lanes’ UIs and refuses to start the new one. The owner of the new lane sees the existing owner’s name and can either wait, ask them to release the conflicting paths, or scope down.
Three exceptions worth knowing:
- A lane can voluntarily share a path with another lane via
Share path. Used rarely — mostly for shared config files that two parallel features both touch. Sharing turns the path into a serialized resource: agents take a lock for the duration of a single tool call. - The workspace owner can force-claim a path away from a stalled lane. This logs an explicit sign-off entry naming the override.
README.md,CHANGELOG.md, and any path matched by.sprintloop/shared-pathsare always shared. Every lane can append to them.
Creating a lane
Three ways, in increasing automation:
-
Manually, from the Lanes panel. Click New lane, type a one-line title, pick the repo, claim the paths you expect to touch, assign yourself or an agent as owner. The lane opens in Drafting state with no entries on its ledger yet.
-
From an Issue. When a SprintLoop Issue is moved into In progress, the workspace offers to spin up a lane against the linked repo with the issue’s acceptance criteria pre-loaded as the lane’s brief. This is the path most teams settle into within the first week.
-
From an agent harness. A Claude Code or Codex session running inside SprintLoop opens its own lane on first file read. The agent is the owner; the human who started the session is the sign-off authority. The lane is auto-titled from the first prompt and renamed after the first commit.
Closing a lane
A lane closes when its diff merges, when the owner explicitly abandons it, or after seven days of inactivity (configurable per workspace). On close, the chain head of the lane’s ledger is signed by the owner and stapled into the workspace’s federation root. That’s the moment the work becomes archivally provable: anyone with the public verification key can later check that this set of changes, in this order, by this owner, with these tool calls, ended at this commit hash.
If you’re going to read one more page after this, make it Harness racing — that’s how a single lane gets dispatched to two or more agents in parallel.