# Give each Claude Code worktree its own database

URL: https://briq.cloud/docs/guides/claude-code-worktree-database

> Parallel agents on one repo, each in a Claude Code or git worktree with its own fork of your database, made with the worktree and destroyed with it.

Claude Code agents working in parallel on one repository, each in its own worktree, each need a
database they can migrate without breaking the others. Fork your main stack for each worktree's
branch: every worktree gets a copy of the data and its own env file.

```bash
briq fork --stack main --branch feat/login --env .env.local --expose
```

1. Every briq of `main` is forked into a new stack, `feat-login`, with its data.
2. `--expose` makes the fork reachable from your machine: its databases over TLS, its web
   services at private URLs.
3. `.env.local` gets the fork's outputs (`DATABASE_URL`, `REDIS_URL`…), pointing there. Its other
   lines are kept, so `pnpm dev` in the worktree runs against the fork.
4. `briq destroy --branch feat/login` destroys the fork and its endpoints when the branch is done.

To start from a repo that has all of it (a stack file, the Claude Code and git hooks below, a
small app), use the template
[briq-cloud/claude-parallel-worktree-template](https://github.com/briq-cloud/claude-parallel-worktree-template).

## Prepare main

* Fork main while it sleeps or is stopped: the copies are then of one moment, and the fork does
  not wake it. See [Forks](https://briq.cloud/docs/concepts/fork#a-stack-at-one-moment).
* Make main once, from the repo's [stack file](https://briq.cloud/docs/stack-file) (`briq up`) or with `briq run`.
  `briq up --env .env.local --expose` also gives the main checkout outputs it can use from your
  machine.
* Keep seed or staging data in it, never production: every fork copies all of it.
* Add `.env.local` to `.gitignore`: it holds passwords, and git refuses to remove a worktree
  with untracked files.
* Sign the [CLI](https://briq.cloud/docs/cli) in once: `briq login`. Where no one can open a link (CI, a server),
  export `BRIQ_API_KEY` instead.

## With git: a post-checkout hook

`git worktree add` runs `post-checkout` with the null commit as the previous `HEAD`. The hook
forks only then, not on every checkout. Worktrees share the repository's hooks, so install it once.

```sh title=".git/hooks/post-checkout"
#!/bin/sh
# Fork the main stack for a new worktree's branch.
[ "$1" = 0000000000000000000000000000000000000000 ] || exit 0
branch=$(git rev-parse --abbrev-ref HEAD)
case "$branch" in HEAD | main) exit 0 ;; esac
briq fork --stack main --branch "$branch" --env .env.local --expose
```

```bash
chmod +x .git/hooks/post-checkout
git worktree add ../app-login -b feat/login    # forks main into feat-login
git worktree remove ../app-login && briq destroy --branch feat/login
```

Git runs no hook when a worktree is removed: destroy the fork in the same command.

## With Claude Code: WorktreeCreate and WorktreeRemove

Claude Code runs `WorktreeCreate` instead of its own `git worktree add` (`claude --worktree`, a
subagent with `isolation: "worktree"`), and `WorktreeRemove` when it removes that worktree. Both
get the worktree's `name` and `cwd` on stdin; newer versions also send `worktree_path`, and
`base_ref` to `WorktreeCreate`. The first hook makes the worktree and forks; it prints the
worktree's path last, on stdout.

```sh title=".claude/hooks/briq-worktree-create.sh"
#!/bin/sh
set -e
input=$(cat)
name=$(printf '%s' "$input" | jq -r .name)
root=$(git -C "$(printf '%s' "$input" | jq -r .cwd)" rev-parse --show-toplevel)
# Claude Code's default place when the input does not say
dir=$(printf '%s' "$input" | jq -r --arg d "$root/.claude/worktrees/$name" '.worktree_path // $d')
base=$(printf '%s' "$input" | jq -r '.base_ref // empty')
branch="worktree-$name"
# without git's hooks: the fork is made here, once
git -C "$root" -c core.hooksPath=/dev/null worktree add -b "$branch" "$dir" ${base:+"$base"} >&2
# a refused fork still leaves a worktree: say so, and hand it back
(cd "$dir" && briq fork --stack main --branch "$branch" --env .env.local --expose >&2) ||
  echo "briq: no fork for $branch; run briq fork in $dir" >&2
echo "$dir"
```

```sh title=".claude/hooks/briq-worktree-remove.sh"
#!/bin/sh
input=$(cat)
root=$(git -C "$(printf '%s' "$input" | jq -r .cwd)" rev-parse --show-toplevel)
dir=$(printf '%s' "$input" |
  jq -r --arg d "$root/.claude/worktrees/$(printf '%s' "$input" | jq -r .name)" '.worktree_path // $d')
branch=$(git -C "$dir" rev-parse --abbrev-ref HEAD)
main=$(git -C "$dir" rev-parse --path-format=absolute --git-common-dir)/..
git -C "$main" worktree remove "$dir" >&2 || exit 1
briq destroy --branch "$branch" >&2 || echo "briq: destroy stack $branch yourself" >&2
```

```json title=".claude/settings.json"
{
  "hooks": {
    "WorktreeCreate": [
      { "hooks": [{ "type": "command", "command": "sh \"$CLAUDE_PROJECT_DIR/.claude/hooks/briq-worktree-create.sh\"" }] }
    ],
    "WorktreeRemove": [
      { "hooks": [{ "type": "command", "command": "sh \"$CLAUDE_PROJECT_DIR/.claude/hooks/briq-worktree-remove.sh\"" }] }
    ]
  }
}
```

* Claude Code keeps a subagent's worktree when the subagent finishes, and runs no
  `WorktreeRemove` for it: remove it, and its fork, yourself.
* The remove hook keeps the branch, as Claude Code does.

## Only inside briq

Leave out `--expose` when nothing outside briq uses the fork: an agent that works through
`briq exec`, or an app that runs as a briq of the same stack. The outputs are then the fork's
internal addresses, which only the briqs of its stack reach, and nothing gets a hostname on the
internet.

```bash
briq fork --stack main --branch feat/login --env .env.briq
```

To expose one database later, see
[Connect to a database from outside](https://briq.cloud/docs/guides/connect-to-a-database-from-outside).

## When it does not fork

| The CLI says                                                        | Fix                                                                       |
| ------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| `invalid_state: a briq named db already exists in stack feat-login` | The branch has a fork. Use it, or `briq destroy --branch <branch>` first  |
| `invalid_state: briq … is stopping`                                 | A briq of main is mid-move. Fork again once it lands                      |
| `quota_exceeded`, `payment_required`, `plan_limit`                  | A fork counts as new briqs and new disk. Destroy forks of merged branches |

## Good to know

* One fork per branch at a time. The stack is named after the branch (`feat/Login_v2` →
  `feat-login-v2`), with a hash of the branch added when another stack has that name.
* `--branch` names the stack after the branch on both `fork` and `destroy`.
* An agent connected over MCP forks the same way: `briq_fork({ stack: "main", into: "feat-login" })`.
* A fork sleeps when unused and keeps its disk until it is destroyed.
* `--env` replaces only the keys it writes. A new file is readable by you only.
