Skip to Content
DocsBranches

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
A branch card on the board showing branch and PR badges, env / files / schedule tabs, and a list of sessions running insideA branch card with a parent coordinator session that fanned out to eight specialized security-review children

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:

FieldPurpose
nameHuman-readable label shown on the card
branchGit branch checked out in the working directory
issue_url, pull_request_urlGitHub linkage, auto-injected into zone prompts
owners / others_canRBAC permissions (when branch RBAC is enabled)
board_id + (x, y)Where the card lives on the canvas
environmentPer-branch dev environment (start/stop, ports, URL)
Branch details modal with tabs for General, Sessions (4), Environment, Files, and Schedule. The General tab shows Name, Repository, Branch, Base Branch, Path, plus a Work Context section with Board picker, Issue, Pull Request, Notes (markdown), MCP Servers, and an Owners & Permissions section with Owners and Others Can fields

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 .git metadata.
  • Clone: a self-standing git clone with 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: false

If 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.sandbox is enabled with home_mode: per_user — which the named unix_user_mode: sandbox mode forces. The owner-home overlay deliberately hides the entire daemon .agor tree — 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_repository is unavailable — 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 it

Recreating 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.

Archive or Delete Branch modal showing filesystem and metadata options

Filesystem options:

  • Leave untouched: Metadata-only archival. Files stay on disk.
  • Clean workspace: Runs git clean -fdx (removes node_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:

TierWhat others can do
noneNo access, completely private to owners
viewRead branches, sessions, tasks, messages
sessionDefault. Create their own sessions on this branch
promptPrompt any session, including others’ (but the session still runs as its creator’s identity)
allFull control

See Security for deployment modes and the trust boundary discussion.


Last updated on