Skip to content

Worktrees and pull requests

Kangentic uses git worktrees to give each agent an isolated copy of your repository. This lets multiple agents work on different tasks simultaneously without stepping on each other’s changes.

What a worktree is, how Kangentic gives each task one, and where the agent’s process runs.

A git worktree is a linked working copy of your repository that shares the same .git directory. Each worktree can have a different branch checked out, so multiple branches can be worked on at the same time without cloning the repo multiple times.

When an agent is spawned for a task with worktrees enabled, Kangentic:

  1. Creates a new branch from the configured base branch (e.g., fix-login-a1b2c3d4)
  2. Creates a worktree in .kangentic/worktrees/{task-number}/ inside your project - the directory is named after the task’s ticket number (e.g., .kangentic/worktrees/42/), chosen once and never renamed. Tasks created before this naming keep their original {slug}-{id} folders. Branch names are unrelated to folder names and keep the slug form.
  3. Launches the agent in the worktree directory

The agent makes all its changes in the worktree. The project folder, the checkout you have open in your editor, is never touched.

Since v0.44.0 the worktree’s files are written in one checkout pass instead of two on git 2.35 or newer, which makes creating one about three times faster on Windows.

Each task chooses between two places to run, with a Worktree | Project control beside the Branch field in the New Task dialog and the task detail edit form. Worktree gives the task its own branch and directory as described above. Project runs the agent in the project folder itself: the checkout you have open in your editor, and the one Kangentic runs from. Use it for a quick config change you want landing directly on your working copy while keeping worktrees on for everything else. The choice is a per-task override of the Worktrees setting (git.worktreesEnabled). With that setting off, the control is not offered and every task runs in the project folder, including one created earlier with Worktree picked; a task that already has a worktree keeps it.

Some projects cannot have a worktree at all, and the dialog says so before you create the task rather than silently falling back: the Worktree option is disabled with the reason in its tooltip, and Project reads selected. Choosing Worktree cannot override any of these.

ReasonWhy
Not a git repositoryThere is nothing to branch from
No commits yetA freshly initialized repo has no ref for git worktree add to start from. Worktrees start working on their own after the first commit
The project folder is itself a git worktreeGit cannot nest worktrees
The agent runs on a remote serverA remote-execution agent works in its server directory, so a local worktree would go unused. This is the one case where the agent is not in the project folder either

Where an agent works is always visible:

  • Before the spawn, the line under the Branch row states the outcome: “Auto-generated branch will be created from main in a new worktree” versus “Runs in the project folder on main”, naming the branch the folder actually has checked out. A pinned base or a custom branch without a worktree reads “x will be checked out in the project folder”, since that is a real branch switch in the folder you have open. When the project cannot have a worktree the line reads “Runs in the project folder”, or “Runs on the remote server”.
  • After it, the folder button in the task detail header shows a worktree glyph for a task in its own worktree and a plain git-folder glyph otherwise, and the kebab’s folder item reads Open worktree or Open project folder.

The New Task dialog: a title field, a description editor that takes dropped files, priority and labels, the Branch row with the branch it will create from main in a new worktree, and the choice between the column's settings and an agent override. v0.46.0 The real app on a sample install. The projects and terminals are recordings, connected to nothing, so click anything.

Tasks running in the project folder all share one checkout, and nothing stops two of them from editing the same tree at once; what Kangentic guards is the branch. If a task would switch branches in a directory where another task’s agent is already live, Kangentic refuses the checkout and tells you which task is holding it and what to do: stop that task, or turn worktrees on so each gets its own copy. A Command Terminal counts as an occupant too, since it runs a real agent at the project root. Queued sessions count as well, because a queued task has already claimed the directory it will start in.

The refusal does not throw the whole operation away. Creating, unarchiving, or promoting a task still succeeds; the card simply arrives without an agent, and the toast says why rather than leaving you to guess. Worktrees are on by default precisely so this case does not come up.

How a task’s branch is named, which base it starts from, and how it stays current.

Auto-generated branch names are the task’s slug and an 8-character ID, whatever the base branch:

<task-slug>-<8-char-id>
  • fix-login-a1b2c3d4
  • prune-stale-worktrees-d4e5f678

The 8-character ID prevents collisions when tasks have similar titles. The base is not part of the name: the task’s worktree records it in its own git config as kangentic.baseBranch, and the task detail window shows it so you can confirm what the worktree branched from without leaving the app.

Before v0.44.0 a task whose base differed from the project default got the base as a namespace prefix (release-1.2/fix-regression-g7h8i9j0). That collided with git’s ref rules whenever the base itself had no slash, failing the create or the push, so the prefix is gone. Branches named the old way are still recognized.

An agent creating a task over MCP can name the branch explicitly with branchName, which is how it hands work it has already committed to a new task. Git allows a branch in only one working tree at a time, so a task naming a branch that is already checked out somewhere can never build its worktree.

Since v0.36.0, kangentic_create_task checks for that before writing anything and refuses the call, naming the branch and the path holding it. Nothing is created, so you no longer end up with a card that looks healthy but has no worktree and no session. The check fails open: if git cannot be probed, the create goes ahead.

Relatedly, a worktree or checkout failure on any spawn path now raises a notification instead of failing quietly, and a failed move no longer force-deletes an agent-supplied branch while cleaning up. Auto-generated branch leftovers are still cleaned as before.

The branch each worktree is cut from follows a priority order:

  1. Task’s base branch - per-task override set in the task creation dialog
  2. Board default - defaultBaseBranch in the team-shared kangentic.json, so everyone on a board forks from the same branch
  3. git.defaultBaseBranch - project or global setting (default: main)

Spawns and Update from base resolve the same ladder: the task’s own base, then the board default, then git.defaultBaseBranch, and finally main or master, whichever the repository has. A per-task base is the only candidate tried; it is never substituted. There is no per-automation override: before v0.42.0 a create_worktree action could carry its own base branch, but automations never create worktrees, because the move path already does.

A task never starts from a base branch that is quietly behind origin (since v0.37.0).

  • A new worktree is cut from a fetched origin/<base>, not from whatever your local ref happened to be at your last pull.
  • A task running in the project folder gets the same treatment. A task with worktrees off still checks a branch out in the project folder when it names one: a custom branch name is fetched and checked out, and otherwise its base branch is checked out and fast-forwarded from origin. That fast-forward is non-fatal: local-only commits or a diverged branch keep your local state rather than failing the spawn. A task with neither a custom branch nor a base branch has nothing to check out, so git is left alone.
  • A spawn that reuses an existing worktree is measured, not moved. A background probe counts how far behind the base that tree is and marks the card’s spawn-progress labels with (base 3 behind). It never rewrites your branch and never delays the spawn - a worktree with work in it is not something to fast-forward behind your back.
  • A failed fetch says so. When the fetch genuinely fails on network or credentials, the remaining spawn labels read (base fetch failed) and a single toast per project explains it, rather than one per task. A repository with no origin remote stays quiet, since there is nothing to be behind.

Every “behind” number Kangentic shows is measured against remote-tracking refs, and before v0.41.0 those refs were only refreshed when something happened to fetch - opening a Changes panel, or moving a task to Done. A project nobody had touched in a while reported stale counts.

Since v0.41.0 a background sweep runs git fetch --all --prune on the open project’s remotes when the project opens and then again 5 minutes after the project’s last full fetch. Any full fetch resets that clock: opening a Changes panel, the check a move to Done makes, or the sweep itself. Turn it off with Auto-fetch remote in Settings > Git (git.autoFetch, on by default); the on-open fetch still runs either way, and creating a worktree always fetches its base branch first. Before v0.44.0 the setting was an interval (git.autoFetchIntervalMinutes), and the first launch of v0.44.0 converts it. The sweep only fetches: it never pulls, merges, or rebases, so keeping a worktree current stays an explicit action (Update from base). It also runs with credential prompts disabled - a fetch on a timer has no gesture behind it, so an expired credential degrades to “no refresh” rather than popping a dialog. Only the focused project is swept.

The same fetch gets a head start from a drag: picking up a worktree-backed card pre-warms it, so the pending-changes check a drop on Done may need finds the fetch already cached or in flight instead of starting one after the card lands. It shares the sweep’s throttle and its setting, so with auto-fetch off, dragging fetches nothing either.

When a card reports it is behind, Update from base in the task detail window’s kebab menu is the one-click fix: it fetches the task’s effective base and fast-forwards the worktree. It never merges and never rebases, so it can only ever move a branch that has no work of its own.

It appears for tasks that have a worktree and are not archived, and it is disabled while a session is active - updating a branch underneath a working agent is not something to do by accident. Every outcome reports back as a toast:

ResultWhat it means
Fast-forwarded N commitsThe worktree now matches the base
Already up to dateNothing to do
Cannot fast-forwardThe branch has commits of its own. The toast reports how far ahead and behind it is; rebase or merge inside the session instead
Uncommitted changesThe worktree is dirty. Commit or stash first, then re-run
Could not fetchOrigin was unreachable, so nothing was changed
No origin remoteThere is nothing to fetch from
Task ActionSessionWorktreeBranch
Move to active columnSpawns/resumesCreated (if new)Created (if new)
Move to To DoKilled, history wipedRemovedRemoved when Auto-cleanup is on (the default)
Move to Done / ArchiveSuspended, history keptRemoved (disk reclaimed)Kept, so the work stays reachable on its ref
Delete taskKilledRemovedRemoved when Auto-cleanup is on (the default)
Where a task's agent runs and what happens to its worktree A task runs in one of two places. With Worktree, the default, a task leaving To Do gets a branch named after its slug and the first eight characters of its id, cut from the base branch fetched from origin, and a git worktree under .kangentic/worktrees named after the task number; the agent runs in that checkout. Moving the task to Done removes the worktree and keeps the branch and the session; moving it back out of Done recreates the worktree at the same folder and resumes the session. Moving the task to To Do, or deleting it, removes the worktree and, with Auto Cleanup on, deletes the branch as well. With Project, the agent runs in the project folder itself, the checkout you have open, on the task's branch. Kangentic guards that shared checkout: a task that would switch branches while another task's agent or a Command Terminal is live there is refused, and its card arrives without an agent. A project that is not a git repository, has no commits yet, is itself a git worktree, or runs a remote agent cannot have a worktree and uses the project folder. WORKTREE Task leaves To Do Worktree fix-login-a1b2c3d4 cut from origin/main Agent session in worktrees/42/ its own checkout Move to Done worktree removed branch, session kept Reset or delete worktree removed branch (Auto Cleanup) back out: same folder PROJECT Task leaves To Do Branch checkout one agent at a time second switch refused Project folder your open checkout on the task's branch
Worktree is the default: the task gets its own branch and folder, Done reclaims the folder but keeps the branch and session, and only a reset (a move back to To Do) or a delete takes the branch too. Project runs the agent in your open checkout after a guarded branch checkout, and what Kangentic guards there is the branch, not the files.

Moving a task to Done frees its disk space but keeps everything needed to resume: the branch and the conversation history are preserved, and dragging the task back to an active column recreates the worktree at the same folder and resumes the agent with full context. In the normal case your work is already committed and merged (or pushed), so removing the worktree loses nothing.

When a move would destroy real work - uncommitted files, or commits that exist only on the local branch about to be deleted - Kangentic shows a confirmation dialog first that red-flags exactly what would be lost. A clean move skips the prompt entirely. Moving to To Do is a full reset: it is treated as “not started”, so the session history is wiped along with the worktree. Since v0.46.0 a task that did its own work also drops its pull request link, so the next run links its own PR instead of finding the old one again. A PR link you typed onto a task that never started stays.

The Auto-cleanup toggle (Settings > Git, git.autoCleanup) controls whether branches are deleted along with their worktrees. Turn it off to keep every task branch until you delete it yourself.

Moving a task to To Do or Done, or deleting it, also stops what its agent left running in the task’s folder. An agent that backgrounds a dev server leaves it running when the session ends, and on Windows that process then holds the worktree directory as its current directory and blocks the removal. Kangentic stops those processes before the worktree is removed. Pressing Stop, or parking a task in a column that does not spawn, is deliberately not one of those moments: the task is paused rather than finished, so a dev server stays up for manual testing.

Since v0.44.0 every task session’s processes carry the task’s id in their environment, so a dev server the agent detached with nohup or Start-Process is still recognized as the task’s. A process is stopped only when it carries that tag, works inside the task’s worktree or project folder, and nothing under it is shared, so a task that runs in the project folder gets the same cleanup. A window the agent opened, a tmux server, and a tool other work also uses are kept. A server counts as used by other work when, at the moment of the cleanup, another task’s agent or a command in one of your terminals holds a TCP connection to it. An adb server that another task is installing through is the case this was built for. A process that only launched something kept, such as an emulator’s launcher, is kept with it. An idle server that only this task used still stops at Done, and its next client restarts it. Since v0.46.0 a program the agent started in a Windows Terminal tab is stopped too: Windows Terminal draws an invisible window for each tab, and Kangentic used to read that as an app on screen and keep the tab’s shell and everything under it.

The cleanup reports what it did. When everything stopped, a toast says so and closes on its own. When something is still running, or could not be stopped, the toast leads with that, stays until you close it, and shows how long ago the cleanup ran. Its Review link lists each process that kept running with the reason (“Other work uses it too. Stopping it can break that work.”) and a Stop button. Stopping one that was holding a Done task’s worktree removes that worktree as soon as the process is gone. A process that exits on its own reads Ended in the list, and a toast with nothing left running closes. A Done move whose worktree removal fails tries once more 10 seconds later before leaving it to the next project open. If the app quits or crashes between a task’s move and its cleanup, the first open of the project on the next launch stops what is still running for its archived and To Do tasks. Turn it off with Stop leftover processes in Settings > Behavior; the toast then lists what kept running and stops nothing.

If a removal is still blocked, Kangentic scans for the process holding the directory, reaps it when it is an orphan, and retries once. A holder that is still supervised is named rather than killed, and the error the next worktree on that path raises reads Held by node.exe (pid 12345). Close it and retry. Every removal is time-bounded, so one stuck delete cannot hold the project’s git queue for minutes.

Removing a worktree also cleans up after the agents that ran in it. Several CLIs record folder trust per directory, keyed on the absolute path, and some of them do not inherit trust from a parent folder: Codex writes a trust_level entry into ~/.codex/config.toml, and Gemini, Qwen Code, Grok Build, and Antigravity keep their own trusted-folder stores. Kangentic pre-approves each worktree on spawn so you are never prompted per task, then drops the entry again when the worktree goes, so those five stores track live directories instead of accumulating one dead entry per task forever. Qwen Code joined them in v0.43.0, having been the one trust-writing adapter that wrote an entry and never took it back. Only an entry Kangentic could have written itself is removed, so a trust decision you made at that path, or a refusal, survives it. The cleanup is best-effort throughout: the worktree is already gone by then, and the worst case is a stale entry left behind.

Claude Code is the one that still accumulates. Its per-directory trust lives under projects in ~/.claude.json, keyed by absolute path the same way, and Kangentic pre-accepts it on spawn but does not reap it, so that file keeps one entry per past worktree. Nothing breaks; the file grows.

The task window’s Changes pane shows the branch’s diff against its base, with a file tree, commit history, and blame; see Reviewing changes. The branch is an ordinary git branch, so plain git works too:

Terminal window
# See what the agent changed
git diff main..fix-login-a1b2c3d4
# Merge when satisfied
git merge fix-login-a1b2c3d4

Since Kangentic uses standard git worktrees, you can interact with them using normal git commands:

Terminal window
# List all worktrees
git worktree list
# Navigate to a worktree (folders are named after the task number)
cd .kangentic/worktrees/42/

When an agent runs gh pr create, gh pr view, or gh pr merge (GitHub), or az repos pr create, az repos pr show, or az repos pr update (Azure DevOps), in a task’s session, Kangentic detects the PR URL from the terminal output and links it to the task. You can also link PRs manually with the kangentic_link_pr MCP tool from within an agent session, or by pasting the URL into the PR field of the task detail edit form.

Once a PR is linked, the task card shows a clickable PR status badge (open, draft, merged, or closed) that opens the PR. The same badge appears in the task detail header, the Changes panel, and on Agent Monitor cards. Kangentic checks every linked PR when you open a project, and then keeps each one current from a queue: an open PR is checked again about 2 minutes after its own last check, one call at a time and at least 10 seconds apart. That caps the cost at 360 calls an hour however many PRs are open; past 12 open PRs, each PR’s interval stretches instead. Any check resets a PR’s clock, including the 30-second re-poll while CI runs, an agent’s gh pr create, and a manual refresh. Turn it off with Auto-refresh PRs in Settings > Git (git.prAutoRefresh, on by default). Before v0.44.0 this was a sweep on a fixed interval (git.prRefreshIntervalMinutes), which the first launch of v0.44.0 converts.

An open PR’s badge answers a second question (since v0.41.0): would clicking Merge succeed right now? When the host can judge it, the chip word changes from open to one of:

ChipMeaning
readyA merge would succeed right now
blockedA required review, failed check, or branch rule stops it
conflictsThe PR conflicts with its base branch
queued / runningA blocking check has not finished - the merge would not succeed yet, and this says why. On GitHub, queued also covers a required check that has not reported at all, which is how a PR looks for the minute or two before CI creates its runs. That reading needs classic branch protection. A repository on rulesets reads blocked until its checks report

A PR the host has no verdict for keeps the plain open chip, and draft, merged, and closed PRs are untouched. “Mergeable” on its own is not the bar: both hosts report “no conflicts” separately from reviews and required checks, and the chip only reads ready when the whole gate is clear.

The verdict is as fresh as the PR’s last check - the background queue, or Refresh PR in the task’s kebab menu - and the chip’s tooltip says so. A PR whose chip reads queued or running is the exception. Since v0.43.2 Kangentic re-checks it every 30 seconds until its checks settle, so the chip changes within seconds of CI finishing instead of waiting for its next turn in the queue. After 30 minutes in flight the re-check stops and leaves the PR to the queue. It skips drafts, and it is off whenever background refresh is off.

Azure DevOps PRs always have their branch policies (reviewer minimums, required builds, work-item linking) evaluated since v0.44.0, so a clean Azure PR reads ready or blocked the same way a GitHub one does; the Evaluate branch policies switch is gone. Kangentic does not read Azure DevOps bypass permissions yet. On GitHub, Count merge bypass as ready in Settings > Git folds a PR that only your own branch-protection bypass would merge to ready (on by default, because that is how the default board’s Merge column lands PRs; it never folds past a failing or unreported check). With the bypass setting on, the verdict is yours: two people watching the same PR can correctly see different chips.

Two providers resolve PR state (since v0.39.0), each through its own CLI, and which one answers is decided by the repository’s origin remote rather than by trying each in turn:

ProviderCLINotes
GitHubghAny host with github in its name, so GitHub Enterprise at github.mycorp.com resolves. An Enterprise host with no github in its name is not claimed and its PRs are not resolved; the error names the unmatched remote
Azure DevOpsaz with the azure-devops extensiondev.azure.com and legacy visualstudio.com remotes, over SSH or HTTPS. A pasted .../pullrequest/<id> URL links with its number, so the refresh sweep can confirm it. Azure records a PR’s commits only when it completes, so a commit lookup cannot see an active PR at all; one is usually still found by branch instead (see below)

A repository neither provider claims never has a link cleared: the resolver reports that it could not check, the task keeps whatever link it has, and the Link / refresh PR toast names the actual reason (which CLI is missing, or that no provider owns the remote) instead of telling every user to run gh auth login.

Since v0.36.0 the reconciles Kangentic performs itself are silent: the background sweep, the auto-link when a session goes idle, a PR URL spotted in the scrollback, the re-resolve that follows a link write, and the task detail’s own Link / refresh PR button all update the card without raising a toast. The discriminator is provenance, not volume: an agent’s own kangentic_update_task or kangentic_link_pr call is still announced, because that is genuinely news. Landing one PR used to raise three “Task updated by agent” toasts per task, two of which announced a round trip that netted to no change.

When the pushed branch has a different name

Section titled “When the pushed branch has a different name”

Kangentic normally looks for a PR under the task’s own branch name, or under the commit it last saw. Neither finds anything when the agent pushed the work under a team-convention name instead: the PR exists, but not on any name the task knows.

Since v0.40.0 there is one more place to look. If a remote branch’s tip is exactly the task’s last-seen commit, Kangentic resolves a PR from that branch too, and records the name on the task. Later refreshes go straight to the recorded name, so the link survives once the task commits past what it pushed and the tip no longer matches. In this shape it is also the only thing that finds an active Azure DevOps PR once the worktree is gone, since Azure answers a commit lookup only for completed PRs and every branch name Kangentic already had on file is the wrong one.

The lookup stays quiet on tasks that do not need it. A worktree still sitting on its base branch’s tip is skipped outright, because every branch on that commit belongs to whatever last landed on the base rather than to the task. Kangentic now records which base each worktree was actually cut from, so that check measures against the real branch on a project that does not build on main.

Since v0.41.0 the pushed name is also captured at the source: when the agent’s own git push names an explicit destination branch, Kangentic records it on the task the moment the push finishes. That is what lets a task running without a worktree link its PR at all - every other anchor comes from a worktree read, and reading the shared checkout instead would hand every concurrent no-worktree task the same branch. Today only Claude Code forwards the full command to the capture; for any other agent, hand the branch to kangentic_link_pr instead.

One guard keeps the inference honest: a PR, or a branch, that another task on the board already holds is refused as an inferred answer. A task fast-forwarded onto a sibling’s branch to build on its work is identical in git to the shape this lookup exists for, and before v0.41.0 it could be stamped with the sibling’s PR. The board can tell the two apart - the sibling linked first - so the inferred lookups check that before they answer. An explicitly entered PR number is never refused, which is how a review task and the authoring task share a PR on purpose.

  • Creating tasks - choose worktree or project folder, the branch, and the base when you create a task
  • Columns - what each move does to the session and its worktree
  • Session persistence - what happens to worktrees across crashes and restarts
  • Reviewing changes - the diff, commit history, and blame for a task’s branch

Kangentic is free and open source. A star helps other people find it.

Star on GitHub