Guides
FAQ
The questions a sceptical engineer asks before letting a tool sit between them and every repo they own.
On this page
- Why not just use git worktree and a shell alias?
- What does this do to my machine, and how do I undo it?
- Does it work with Cursor, Copilot, or no AI at all?
- Is my code or my prompts leaving my machine?
- How much context does the per-prompt injection cost?
- I already have twelve repos checked out with in-flight branches. What happens to them?
- What do I do when a rule blocks something I actually need?
- What happens when the hooks break and I don’t notice?
In brief. Answers to what a sceptical engineer asks before adopting Repolane: when a plain alias is enough, what it changes on your machine, what leaves it, what it costs in context, how to get past a rule, and how you find out the hooks stopped working. The one thing to remember: the rules are a guardrail with stated limits, and
lane doctoris how you check they are in force.
Why not just use git worktree and a shell alias?
For one repo, do that. git worktree add plus a function in your .zshrc is genuinely enough,
and Repolane would be overhead.
What an alias does not give you is the part that is not about one repo:
- One name across several repos. A lane is one id, one branch of that name in each repo it
touches, and one folder holding all of them.
lane start ABC-123 web apidoes in one command what is otherwise agit worktree addper repo plus the bookkeeping, andlane withadds a repo to work already started. - A written-down goal that outlives the terminal.
.repolane/registry/lanes/<id>.mdholds the goal, the note you left when you parked it, findings, and a log that gets a line when a session ends. An alias has nowhere to put that. - Refusing to lose work.
lane donechecks every repo in the lane before removing any of them, and refuses while anything is dirty or unpushed — including the case where a worktree has gone missing but its branch was never pushed. - The rules. Most of the Rules page is enforced by a hook rather than asked for in a prompt, and the page says which rules are only instructions. An alias cannot stop a session reading the repo next door at all; a hook can, within the stated limits.
If none of those four is a problem you have, the alias wins. That is a real answer, not a modest one.
What does this do to my machine, and how do I undo it?
The package, installed by pipx or uv in its own environment with its commands linked into
~/.local/bin; a list of your workspaces under ~/.local/share/lane; some symlinks (each asked about
first, and in a few commands deletions) under ~/.claude/projects; and your workspace folder, which
holds everything else. Nothing in /usr/local, no shell profile edits, no global Claude Code settings
changed.
The full list, file by file, and the removal steps, are on
What it changes on your machine. There is no lane uninstall command; the undo is
manual and takes a couple of minutes.
Does it work with Cursor, Copilot, or no AI at all?
Yes, with one honest caveat.
Every command works standalone. lane start, lane status, lane brief, lane park,
lane done, lane board and the rest are bash and Python over git; they do not call a model and
they do not know whether one is running. Claude Code does not even need to be installed: the hooks only
matter inside it.
A lane is an ordinary set of git worktrees in an ordinary folder, so any editor can open it —
lane workspace (it takes no argument) writes VS Code .code-workspace files: one for the workspace
with every active lane, and one per lane.
The caveat: the rules do not travel. The guard is wired through Claude Code’s hook events, so under another assistant you get the structure — one lane, one branch per repo, the lane file, the board — and none of the refusals. Nothing pretends otherwise, and nothing breaks.
Is my code or my prompts leaving my machine?
Repolane itself does not send your code or prompts anywhere. It has no telemetry, no analytics, no update check and no crash reporting. It is not network-silent, though:
- The hooks read and write files inside the workspace (and, when a session ends, tidy
duplicate transcripts under
~/.claude/projects). What they add to a session is text in the session you are already having; it goes wherever that session already goes, which is to Claude. - Git talks to your remotes, as git does (
lane add <url>, the fetch inlane start,lane rules pull, andlane status/ barelane, which rungit fetch -q originin every base clone each time). lane upgradeandlane upgrade --checkask PyPI for the newest version, and only when you run them; pipx or uv then download it.ghtalks to GitHub if you have it installed and signed in:lane gh,lane sync, and, while the board is open, a pull request lookup for each branch every 60 seconds.lane boardbinds to127.0.0.1and nothing else, and rejects any/apirequest without the per-run token. Its page makes no outbound requests; the fonts ship with it.
See what leaves your machine for the full table.
How much context does the per-prompt injection cost?
Bounded, because memory notes are never injected, only their indexes.
Once per lane per session, on the first prompt, the guard adds a memory brief: .repolane/knowledge/preferences.md
(capped at 3,000 characters), each .md file in .repolane/knowledge/shared/ (3,000 each), the cross-repo
.repolane/memory/MEMORY.md index (3,000), each lane repo’s .repolane/memory/<repo>/MEMORY.md index (3,000 each), the
list of attached references, the four-scope table and a short note on how to save a memory. The
individual notes those indexes link to are not injected; Claude opens one only if it turns out to be
relevant. A three-repo lane with no shared knowledge has a worst case of about 16,000 characters once
(roughly 4,000 tokens, an estimate at about four characters a token), and each shared knowledge file adds
up to 3,000. An index shorter than its cap costs only its length.
Separately, the first time a session touches a repo, that repo’s own CLAUDE.md is injected, cut at
8,000 characters, with its .claude/rules/ files listed by name and description rather than by content.
On every prompt, the first included, what is added is one line per repo — branch, ahead, behind, uncommitted — plus a pointer to the lane’s spec, and for a multi-repo lane a short reminder of the four ways to reach each repo. For the three-repo example on Working with Claude that is about 1,000 characters (roughly 250 tokens, an estimate); it grows with the number of repos in the lane, not with the size of anything else.
I already have twelve repos checked out with in-flight branches. What happens to them?
Nothing happens to them. lane add <path-to-a-checkout> takes the checkout’s origin as the
remote and copies its git database into .repolane/repos/<name>; it never writes to the original. Your
branches, your stashes and your uncommitted work stay exactly where they are.
Two things happen that you should know about (precisely, on
Install): the copy’s default branch is reset to
origin/<default>, so unpushed commits on it are not on that branch in the copy (push them
first), and any ignored .env* files are copied into the base clone.
It also records where the repo came from, in the repo’s old_paths and in
.repolane/registry/old-checkouts.json. That has two effects worth knowing: lane status will tell you
when work is happening out in the old checkout rather than in a lane, and the guard will refuse
to let a session read the old path — because a frozen copy of a file from before you adopted
Repolane is exactly the sort of thing that gets quietly edited in the wrong place.
The practical migration is unexciting: add the repo, finish or push the in-flight branches in the old checkout at your own pace, then delete it when you stop using it.
What do I do when a rule blocks something I actually need?
Every rule has a way through it, and the refusal message names the one that applies.
| Blocked | The way through |
|---|---|
| reading something outside the workspace | lane ref <id> add <path> "why" — it appears under lanes/<id>/refs/<name>. Read-only by default; add --rw to allow edits |
| pushing to a default branch | prefix the command with the marker you can see: ALLOW_DEFAULT_BRANCH_PUSH=1 git push … |
reading a .env or a key file | lane keys <path> from your own terminal lists the variable names and never the values. Inside a session that spelling is refused; the bare lane-env-keys passes, or point the session at .env.example (readable as normal). lane env <repo> checks the file is wired |
a recursive grep -r or Grep-tool search refused because a repo has a .env | leave the secret out: --exclude='.env*', rg -g '!.env*', or a Grep glob of "!.env*"; the denial names the option |
| a read-only reference you need to edit | lane ref <id> add <path> --rw, or lane ref <id> rm <name> to detach it |
If what you need is not on that list, there is no switch that turns a built-in rule off. The built-in
refusals live in the guard inside the installed package (scripts/hooks/guard.py), and an upgrade
replaces it. The events the guard runs on are the lane hook … entries in .claude/settings.json at
the workspace root. Deleting one there turns that event off for sessions started at the root, but
per-lane sessions get their own .claude/settings.local.json, rewritten by lane start and
lane resume, and lane upgrade and lane doctor --fix put Repolane’s hooks back. Make any such edit
from your own terminal or a session at the workspace root (a lane session is refused). Rules specific
to your project go in .repolane/registry/rules.yaml instead, and those can only add refusals (see
Extending).
What happens when the hooks break and I don’t notice?
A guard that silently stops guarding is worse than no guard at all, and the bad case is real: if
guard.py stops compiling, every rule is off at once and a session looks entirely normal. The check for
it is lane doctor, and it makes more than one check. First, that guard.py compiles
and still refuses: it feeds the guard eleven named probes (ten that must be denied, one ordinary
command that must not be), so a file that compiles but does not enforce is caught too, and it checks
that each of your own project rules still refuses its example. Second, that the guard is actually wired
in everywhere: it walks every lane and worktree looking for the hook entries in each
.claude/settings.local.json, and for a PreToolUse matcher that includes Glob and Grep, because a
session whose local settings lost its hooks is unguarded even while guard.py is perfectly healthy. If a
lane’s wiring is missing, the fix it prints is lane resume <id>, which rewrites it.
lane status runs the one-line form of the same check at the top of its output, so it is in front of you in
the command you already type. lane doctor also checks, before anything else, that lane is on the PATH
the hooks use: every hook is a lane hook … command, and one that cannot start leaves Claude Code running
tools unguarded. lane doctor -v also runs the fuller guard test suite (over 1,800 cases), which
exercises the individual refusals. It builds its own throwaway setup and never skips: if the fixture
cannot be built, the run fails. A green doctor means the rules are in force, not that nothing can
get past them; the security model lists how the guard fails.