Isolated Sessions · crystl Docs
Isolated Sessions
Isolated sessions let you run multiple coding agents (Claude Code, Codex, or any agent you run in a shard) on the same repository simultaneously, each on its own git branch. Under the hood, crystl uses git worktrees to give each agent a separate working copy, either on your Mac or on a remote server over SSH. New to worktrees? Start with our complete guide to git worktrees with Claude Code.
How it works
When you create an isolated shard, crystl:
- Creates a new git worktree from the gem’s integration target (the repository default branch by default)
- Detects your package manager and installs dependencies
- Symlinks
.envfiles from the main project - Tells the agent it’s in an isolated worktree (via system prompt) so it stays scoped to its task
- Opens a terminal session in that worktree directory
- Marks the shard with a branch icon so you can tell it apart from regular shards
Each isolated shard has its own copy of the repo on its own branch. Agents in different isolated shards can edit the same files without overwriting one another’s live checkout; if they change the same lines, git may still report a conflict when you integrate the branches.
Creating an Isolated Shard
Click the worktree row (the branch icon in the column that drops when you hover the “+” in the shard bar, also on the File menu) to spin up a new isolated shard in a fresh worktree, forked from the gem’s integration target (the repository default branch by default). crystl names the branch, opens the worktree, and drops you into a ready agent session.
Forking from a Branch
Control-click (or right-click) the worktree row to open the branch picker: fork the worktree from an existing branch instead of the default, or start a new branch from a checkout of your choice. Use this to base a shard on a release line, a feature branch, or a teammate’s work rather than main.
Via the CLI
From any shell, or from inside another shard’s agent session, use the crystl CLI to create an isolated shard without touching the GUI:
crystl shard create --gem myapp --isolated # auto-named worktree
crystl shard create --gem myapp --isolated --crystal-name jade # pick the name
crystl shard create --gem myapp --isolated --prompt "fix #42" # seed it with a task
crystl shard create --gem myapp --isolated --prompt "fix #42" --scrollback 500 # cap scrollback for a light worker
Useful for orchestrator agents that want to fan parallel work out into worktree-isolated siblings without waiting for a human to click.
Local vs. Remote Isolation
An isolated shard runs wherever its gem is currently working. Any shard in crystl falls into one of four states:
| State | Shell | Git worktree | How to create |
|---|---|---|---|
| Local | Your Mac | None | Click “+” in a local gem |
| Local isolated | Your Mac | On your Mac | Isolated-shard row in a local gem |
| SSH’d | Remote server | None | Open an SSH gem or shard |
| Remote isolated | Remote server | On the remote server | Isolated-shard row in a gem with an active SSH shard |
When you click the isolated-shard row in a gem that already has an SSH shard, crystl detects the existing remote session and creates the worktree on the remote server instead of locally. You get all the isolation benefits (parallel agents, branch tracking, merge-on-close) without pulling the repo to your Mac first.
Remote Isolated Shards
Remote isolation piggybacks on crystl’s existing SSH ControlMaster connection to run git commands on the remote host.
Creation Flow
When you click the isolated-shard row in a gem that has a live SSH shard, crystl:
- Detects the SSH shard in the current gem and reuses its
RemoteSession(ControlMaster socket). - Finds the remote git root by running
git rev-parse --show-toplevelover SSH from the current remote working directory. - Asks the host which crystals it already holds and counts up from the name it was given until one is free:
amber, thenamber-2, thenamber-3. The name is allocated from your Mac, but the branches that matter are on the server, so crystl checks there before it creates anything. That is why the isolated-shard button works on a remote gem without you choosing a name. A name you typed yourself, and a reattach of a shard you named, are left alone: each means that exact shard. - Creates the worktree remotely:
git worktree add -b crystl/{crystal-name} .crystl/worktrees/{crystal-name}
- Symlinks config files on the remote so the agent picks them up:
CLAUDE.md,AGENTS.md,.mcp.json,.claude/. - Writes an
autorun.shinto the worktree’s own.crystl/folder on the remote. The autorun wraps the agent command with its system-prompt flag (for Claude Code,claude --append-system-prompt) so the agent knows it’s isolated (see Agent Context). - Opens a new SSH shard to the same host and
cds into the remote worktree path. - Tracks
remoteWorktreePathandremoteProjectDiron the session so merge/close operations can find the right files later.
The new shard behaves like any other SSH shard (command history, approval cards, click-to-open) but its git state is on the remote.
When a Remote Create Cannot Go Ahead
A remote create that is refused says why. Some refusals, like a directory that is not a git repository or a stale leftover branch, close the placeholder shard and report the reason as a card. Others, like a worktree creation that failed partway, keep a working shell open with the reason written into it. Either way a shard never appears and vanishes with nothing said.
A create that stalls is bounded. crystl gives the host 30 seconds, and 10 on the first connection check, then stops waiting and reports the reason.
Non-Git Remotes
If git rev-parse --show-toplevel fails on the remote (not a git repo), crystl refuses the isolated shard and tells you why: isolated shards need git on the remote too. Open a regular SSH shard with the normal “+” instead if a plain session is all you need.
Agent Context
When your agent starts in an isolated shard (local or remote), crystl injects isolation context through the agent’s system prompt. For Claude Code, that goes in via --append-system-prompt and reads:
You are working in an isolated worktree on branch
{branch}. Other agents may be working on the same repo in parallel. Keep your changes scoped to your task. IMPORTANT: Do not merge, cherry-pick, or rebase your branch into the integration branch yourself. Do not push to or modify protected branches. Crystl handles branch integration when the shard closes.
The prompt also names the destructive git commands (git reset --hard, git clean -fdx, branch-changing checkouts, worktree removal) that would destroy the session, so the agent knows not to reach for them. You don’t need to add any of this to your prompt. The branch name is substituted in at session start, so the agent knows exactly which worktree it’s in. That reminder helps avoid agents cherry-picking commits, rebasing onto main, or pushing to the wrong branch; the prompt itself is guidance, not enforcement.
The Write Guard
The system prompt above asks the agent to stay in its worktree. The write guard adds a hook-based enforcement layer for the file operations crystl can identify before they run. It is not an OS filesystem sandbox: the agent process still has your user’s normal filesystem access, and the boundary is only as broad as the supported hooks and tool payloads described below.
Path confinement covers these structured file-writing tools: Claude Code’s Write, Edit, MultiEdit, and NotebookEdit, plus Codex’s apply_patch. When one of those tools targets a path inside the repository but outside the isolated shard’s own worktree, crystl denies it before the normal approval flow, including in automatic approval modes. Each path in a multi-file call is checked independently, so an out-of-bounds file is denied even when the same call names in-bounds files too.
Arbitrary Bash writes are not generally path-confined. Commands such as cp, shell redirection, sed -i, package installers, or a script can write anywhere your user account can write. There is a separate, deliberately narrow Bash self-destruction guard for the isolated shard’s own worktree. It blocks recursive forced removal of the worktree or current directory (rm -rf), git clean -fdx, git reset --hard, branch-changing git checkout / git switch (including whole-tree checkout discards), and git worktree remove, move, or prune when they would remove, move, or invalidate the current session’s worktree. It is a targeted command matcher, not general shell containment.
Only the Claude Code and Codex integrations currently enforce these denials: their pre-tool hooks return the bridge’s deny response to the agent. Antigravity’s current hook adapter does not pass the bridge’s denial back to the agent, and an arbitrary CLI agent running in a shard has no enforcement unless it implements a compatible denying hook. The injected isolation instructions still apply to every agent, but instructions are not a security boundary.
For structured writes, paths are resolved the way the filesystem resolves them, not by string matching. That matters because a .. inside a symlinked directory applies to the link’s target: worktree/link/../file looks in-tree to a lexical check while the filesystem lands it somewhere else, and a plain worktree/link → repo/file needs no .. at all. Local hooks resolve against the local filesystem. Remote hooks resolve paths on the remote filesystem before the bridge checks containment, so a symlink on the server is judged by its server-side target rather than by the Mac running crystl.
The structured-write check fails closed when it cannot establish a target: a broken or looping symlink, a path it cannot resolve to a real location, or an envelope that names no file at all (a malformed tool_input, an apply_patch with no file markers). The denial says which file and why, so your agent can correct itself instead of guessing.
The managed config links still work. crystl symlinks CLAUDE.md, AGENTS.md, .mcp.json, and .claude/ from the gem root into every worktree by design, and writes through them resolve to the gem root, in-repo but outside the worktree. Those are allowed, on a deliberately narrow rule: the link has to be one crystl created, be a real symlink at the worktree root, point at the same-named entry in the gem root, and the write has to land at or under that target. There is no blanket “it looked in-bounds” exemption, because a worker could create a link out of the worktree and use it to get through exactly that hole.
Structured writes to paths outside the repo entirely are not blocked by the worktree-containment rule, and neither is reading a credential file. Both still matter after the fact, which is what the touches tab of the agent activity panel records.
Use Cases
Parallel Feature Work
Give two agents different tasks on the same repo:
- Shard A (isolated): “Add user authentication to the API”
- Shard B (isolated): “Write integration tests for the payment flow”
Both agents work at full speed without stepping on each other’s changes.
Explore vs. Implement
Use one isolated shard for an agent to prototype an approach while you continue working in your main shard. If the prototype looks good, merge it in. If not, discard the worktree.
Code Review Assistance
Create an isolated shard to have an agent review and suggest improvements to a feature branch while you continue developing on another.
Automatic Workspace Setup
crystl detects your package manager from lockfiles and runs the install command when the workspace opens. .env files are symlinked from the main project so they’re available immediately.
Supported package managers: npm, pnpm, yarn, bun, pip, poetry, uv, pipenv, bundler, composer, cargo, go, mix, swift, and cocoapods.
Custom Setup
For projects that need additional setup steps (starting Docker containers, running migrations, etc.), add a setupCommand to .crystl/project.json:
{ "setupCommand": "docker compose up -d" }
crystl runs the custom command after dependency installation.