Concepts
Architecture
How base clones, lanes, worktrees, hooks and memory fit together — the whole mental model in one place.
On this page
Concepts defines the six words. This page is how they connect: one clone per repo, a worktree per piece of work, a session that opens above all of them, and a guard that reads the tool calls Claude Code sends it before they run.
One clone, many worktrees
Everything below lives in one workspace: your CLAUDE.md, .claude/ and
lanes/ at the top, and everything else Repolane keeps under .repolane/. Repolane itself is the
installed package; the workspace holds only your data.
Every repo you register is cloned exactly once into .repolane/repos/<name> (a normal working clone, which
the scripts and the guard call a mirror, and this site calls a base clone) and never switched or edited. It stays on its default
branch, clean, so its Git object store can be shared. A lane does not get
its own clone; it gets a worktree of each repo it touches, which is Git’s own feature for a
second working directory backed by the same object store. That is why opening a second piece of
work costs a checkout of files, not another clone, and why two lanes can both touch api without
either waiting on the other.
The lane lifecycle
A lane is a folder at lanes/<id>/ holding a worktree of each repo the ticket touches, plus a
plain-text lane file. Four commands move it through its life:
| command | what happens on disk |
|---|---|
lane start <id> <repo…> | creates the branch in each repo, a worktree of each under lanes/<id>/, the lane file, and an editor workspace |
lane resume <id> | recreates any worktree that is missing and prints where things stand |
lane park <id> "<note>" | commits anything dirty as a WIP commit, writes the resume note, leaves the worktrees in place |
lane done <id> | refuses while anything is dirty or unpushed; once clean, removes the worktrees and archives the lane file under .repolane/registry/lanes/done/ |
Nothing here is a special Repolane object — lanes/<id>/web is a real git worktree, visible to
git worktree list from inside .repolane/repos/web. Repolane’s contribution is treating every worktree of
one ticket as a single unit with one branch name, rather than leaving each repo’s worktree to be
tracked separately.
Where Claude actually sits
A session is started from lanes/<id>/ — the lane root, above every repo it holds, not inside any
one of them. That placement is deliberate: every repo is equally reachable from there, so cross-repo
work cannot quietly collapse back into a run of single-repo sessions. Claude reaches into a repo the
same way you would — git -C <repo>, lane run <repo> <cmd>, or editing <repo>/path/to/file
directly — and never changes directory into one.
The hook sits between the model and the tool call
Claude Code calls a hook before certain tool calls run (PreToolUse) and at other points in a session.
Repolane wires eight events in each lane’s settings.local.json and in the workspace’s
.claude/settings.json, each as a lane hook <event> command (lane hook pre-tool-use,
lane hook session-start, …) rather than a path into the installed package: SessionStart, PreToolUse, PostToolUse, UserPromptSubmit, PreCompact,
SessionEnd, WorktreeCreate and WorktreeRemove. The PreToolUse hook is matched to
Bash|Edit|Write|MultiEdit|NotebookEdit|Read|EnterWorktree|Glob|Grep, so those are the only tool calls
it judges. It is a script that reads the arguments of the specific call (the path, the command being run)
against the built-in rules, your own rules in .repolane/registry/ and the lane layout, and returns allow or
deny before the call executes. That is a different mechanism from an instruction in a prompt: the model is
not being asked to stay in its lane, the call itself is inspected.
PreToolUse fails closed: a payload the guard cannot make sense of, or an error while judging, comes back as
a deny, because a hook that crashes would otherwise let Claude Code run the tool. The other events fail open.
Rules lists what is refused and why; Security model sets out exactly what this
mechanism depends on to keep working, and where it stops being a guarantee.
Context that survives a resume
Three things persist outside the conversation, so a session opening a lane a week later does not start from zero:
- The lane file — goal, resume note and a
## Findingssection, read bySessionStartand bylane brief. - The session log line — appended to the lane file’s
## LogbySessionEnd: one line per session covering every repo’s branch, how far ahead and behind and how many files were dirty, plus the files the edit tools touched (not files changed by shell commands). - Memory — filed into one of four scopes by shelf life (
pref,cross,repo,lane), never written silently; see Concepts.
lane park is what writes the resume note and commits anything dirty (everything git does not
ignore, untracked files included, except files whose name says secret, which it leaves out) before you step away; lane brief is what reads all of it back. What is stored
where, and what a resume or lane done keeps or removes, is on Where state lives.
The board
lane board is a local HTTP server bound to 127.0.0.1 that reads the same files the commands do:
not a background daemon, and not a separate source of truth. It runs in the foreground from the
moment you start it until you press Ctrl-C, and while it runs it refreshes git state every 3 s and
polls GitHub through gh every 60 s. See The board.
The rules a session is given
Your CLAUDE.md imports .repolane/REPOLANE.md with one line, @.repolane/REPOLANE.md, so every
session started anywhere in the workspace reads Repolane’s rules. A session that enters a lane, or
starts inside one, is also handed .repolane/LANE.md, the rules for working inside a lane. Both
files belong to the installed version: they are written again on the first lane command or hook
after the version changes, and never carry your name (the hooks name you when a session starts).
See Rules.
More than one workspace
You can have as many workspaces as you like; each has its own .repolane/repos/, lanes/, registry
and settings. lane works on the workspace that contains your current folder (or the one
LANE_WORKSPACE names), and the hooks on the one Claude Code was started in. Every workspace is
recorded in ~/.local/share/lane/planes.json, which is how lane upgrade refreshes all of them.
A workspace cannot sit inside another one.