Branches
A branch is the primary unit of work in Agor. It’s an isolated git working directory (a checkout of your repo at a specific branch) paired with a metadata record (issue/PR links, owners, environment, sessions) that gives it identity on the board.
Best practice: 1 branch = 1 issue = 1 PR = 1 feature.
Agor uses git branches under the hood, so multiple branches of the same repo can be checked out simultaneously without stashing or switching. Agor manages all of that for you. Just create a branch in the UI or CLI and it handles the git plumbing.
~/.agor/worktrees/<repo>/<name>
├─ feature/oauth2-auth ← branch checked out here
├─ Sessions: Claude #1, Codex #2 (review fork)
├─ Issue: github.com/me/repo/issues/123
└─ PR: github.com/me/repo/pull/456

Left: a branch’s static anatomy (branch, PR, env, files, sessions in one tile). Right: the same primitive packed with a coordinator and eight running children.
Why a workspace, not just a git branch
A git branch is a ref. An Agor branch is a workspace.
- Parallel work: Run 5 features in 5 branches simultaneously. No
git stash, no “kill your dev server so I can test mine.” - Spatial identity: Each branch is a card on a board. You can see, drag, group, and zone them.
- Session containers: Every session belongs to exactly one branch. The conversation tree lives on the card.
- Environment scope: Dev servers, ports, and processes are scoped to the branch, not the repo.
Each branch is fully isolated. Changes in one don’t affect another. AI agents can work on three features in parallel without stepping on each other’s filesystem.
Anatomy
A branch is a filesystem directory plus a database record. The record tracks:
| Field | Purpose |
|---|---|
name | Human-readable label shown on the card |
branch | Git branch checked out in the working directory |
issue_url, pull_request_url | GitHub linkage, auto-injected into zone prompts |
owners / others_can | RBAC permissions (when branch RBAC is enabled) |
board_id + (x, y) | Where the card lives on the canvas |
environment | Per-branch dev environment (start/stop, ports, URL) |
Behind every card: the branch details modal (General / Sessions / Environment / Files / Schedule tabs). The same fields the table above describes, rendered as actual inputs.
Sessions, tasks, comments, and artifacts all reference a branch. The branch is the spine.
Storage modes
When you create a branch, Agor can materialize its working directory in two ways:
- Worktree: native
git worktree add, using the repo’s shared.gitmetadata. - Clone: a self-standing
git clonewith its own.git/directory, isolating branch-local git config and credentials from sibling branches.
Operators can choose which modes are available and which one the UI selects by default:
execution:
branch_storage:
default_mode: clone
allowed_modes:
- clone
# Optional: reject clone_depth and require complete history.
allow_shallow_clones: false
# Optional: stop clone-mode branches from borrowing objects from the
# daemon's base clone. Usually inferred — see "Borrowed objects" below.
borrow_base_objects: falseIf branch_storage is omitted, Agor keeps the backwards-compatible default: worktree is selected by default and both worktree and clone are available. When an operator restricts allowed_modes, the create-branch UI disables unavailable options and the daemon rejects disallowed API/MCP requests.
allow_shallow_clones defaults to true. Setting it to false rejects any positive
clone_depth; callers must omit the field to create a full clone. This is independent of mount
topology: both full and shallow clones are self-standing, while a native worktree always requires
the registered base repository’s Git metadata.
Borrowed objects (borrow_base_objects)
By default, clone-mode branches are created with git clone --reference against the daemon-managed
base clone at <data_home>/repos/<slug>/. Instead of copying the whole object store, the new branch
gets an alternates pointer into it — per-branch .git/ drops from “full pack copy” to a few MB.
Only immutable objects are shared; .git/config, remotes, and credentials stay branch-local, so the
isolation clone mode buys is preserved.
That pointer is baked in at create time and re-read by every later git command in the branch.
It therefore requires the base clone to be readable from wherever sessions run — not merely from
wherever the branch was materialized. If sessions execute in a different mount (a containerized or
templated executor that only binds the branch workspace and never repos/), git in that branch
breaks as soon as a session touches it:
error: unable to normalize alternate object path: /…/.agor/repos/<org>/<repo>/.git/objects
error: Could not read <sha>
fatal: Failed to traverse parents of commit <sha>On a freshly created branch that is total: git status, git log, git show and git commit all
exit non-zero on fatal: bad object HEAD before the agent can do anything. A later git fetch or
git pull against a reachable remote can quietly backfill the missing objects and make the branch
look healthy again — but the pointer is still there, still unresolvable, and still erroring on every
command, and anything git could not re-download from the remote stays lost.
Set borrow_base_objects: false on those deployments. Every clone-mode branch then carries its own
complete object store: more disk, no cross-mount dependency.
Agor turns the borrow off by itself in two cases, so most operators never touch the setting:
execution.sandboxis enabled withhome_mode: per_user— which the namedunix_user_mode: sandboxmode forces. The owner-home overlay deliberately hides the entire daemon.agortree —repos/included — and re-exposes only the branch and the managed agentic-tools directory. Branch creation itself is never sandboxed, so without this the daemon would happily write a pointer its own sessions cannot follow.execution.executor_storage.base_repositoryisunavailable— already an operator assertion that executors cannot see the base checkout.
Setting borrow_base_objects: true explicitly overrides the sandbox inference, for operators who
have made the base clone reachable another way (for example
execution.sandbox.extra_allow_write: ['<data_home>/repos'] — note that is a read-write bind,
so a session could rewrite the object store every other borrowed branch depends on).
base_repository: unavailable is not overridable.
Repairing a branch that already has an unresolvable borrow
The setting only affects branches created after the change. An existing branch keeps its pointer.
Do not just delete .git/objects/info/alternates. The borrowed objects were never copied into
the branch, so dropping the pointer strands everything that lived behind it. On a branch with no
in-session commits that is every object (fatal: bad object HEAD). On one that has been worked in,
the session’s own commits survive as local objects but their history does not, so git can no longer
walk them out — git push and git format-patch both die on Could not read <sha> /
fatal: Failed to traverse parents of commit <sha>, and the unpushed work is unrecoverable.
Materialize the objects first, from a context where the base clone is readable (the daemon host,
not a sandboxed session):
cd <data_home>/worktrees/<repo>/<branch>
git repack -a -d # copies borrowed objects into a local pack — no -l/--local
rm -f .git/objects/info/alternates
git fsck --connectivity-only # verify before trusting itRecreating the branch also works, but it allocates a new branch_id and orphans anything bound to
the old one (schedules, gateway channels).
GitHub-native workflow
Because each branch carries issue_url and pull_request_url, AI agents have unambiguous context for what they’re working on. Zones can use that context directly:
deeply analyze this github issue: {{ branch.issue_url }}When the agent runs, it sees the link, fetches the issue, and works against the right branch. No copy-paste, no “which ticket was this for again?”
Environments
Each branch can have its own dev environment, long-running processes (dev server, database, watchers) scoped to that branch, with auto-assigned unique ports so multiple branches can run simultaneously without collisions.
Configure once on the repo, then everyone on your team gets one-click start/stop on every branch.
# Per-repo template, ports derived from branch's unique_id
up_command: 'PORT={{add 9000 branch.unique_id}} pnpm dev'
down_command: "pkill -f 'vite.*{{add 9000 branch.unique_id}}'"
app_url_template: 'http://localhost:{{add 9000 branch.unique_id}}'→ Environments guide for the full setup.
Archival
When a feature ships and the PR merges, you usually want to clean up disk without losing the conversation history, analytics, or issue/PR linkage.
Filesystem options:
- Leave untouched: Metadata-only archival. Files stay on disk.
- Clean workspace: Runs
git clean -fdx(removesnode_modules, builds, untracked files). - Delete completely: Removes the branch directory from disk.
Metadata options:
- Archive (recommended): Hides from board, preserves all sessions and conversations for analytics and history.
- Delete permanently: Removes all data including sessions and conversations. No undo.
Best practice: Archive with filesystem cleanup after PR merge. Only delete permanently for experimental branches with no valuable data.
Branch-level features
The branch is also where several other features attach:
- Sessions & Trees: Every session lives in exactly one branch. The fork/spawn tree appears on the branch card.
- Boards & Zones: Branches are placed on boards; dropping into zones triggers templated prompts.
- Multiplayer & Social: Spatial comments pin to branches; shared terminal sessions are branch-scoped.
- Scheduler: Templated prompts can fire on a schedule against a specific branch (great for teammate heartbeats).
Permissions (RBAC)
When execution.branch_rbac: true is set, branches enforce ownership and a per-tier others_can permission:
| Tier | What others can do |
|---|---|
none | No access, completely private to owners |
view | Read branches, sessions, tasks, messages |
session | Default. Create their own sessions on this branch |
prompt | Prompt any session, including others’ (but the session still runs as its creator’s identity) |
all | Full control |
See Security for deployment modes and the trust boundary discussion.
Related
- Sessions & Trees: Conversations and genealogy that live inside a branch
- Boards & Zones: How branches are organized spatially
- Environments: Per-branch dev environments
- Scheduler: Time-based prompts targeting branches