Skip to main content

Worktree Cards

The anatomy of a worktree card in the sidebar: lifecycle chips, row status signals, alarm salience, the quick state filter, collapsed states, filters, and per-card recovery.

Reviewed

Worktree Cards

Each worktree shows as a card in the sidebar. This page covers how a card is built: the lifecycle chip, row status signals, alarm salience, the quick state filter, collapsed states, filters, and per-card recovery. For creating, switching and deleting worktrees (and for what a deleted worktree's card does), see Worktrees.

A card carries:

  • Branch name, with a badge for the main worktree (it moves to the secondary row when an issue is linked)
  • Mood indicator: stable, active, stale or error
  • Issue title as the headline when one is linked, plus PR badges for PR state
  • Git status: changed file count, insertions and deletions
  • Last commit summary
  • Terminal list: every panel in this worktree with its kind and state
  • Quick actions: launch agents, run recipes, open in an editor

Local git state on cards (commits, branch switches, file changes) comes from file-system event watching rather than polling, so an agent writing files in a background worktree shows up live instead of staying invisible until it commits. A polling fallback covers the case where the OS-level watcher isn't available, which on Linux usually means the inotify watch limit is exhausted.

Tip

On Linux, sidebar updates that lag by tens of seconds usually mean the inotify watch limit is exhausted. Check the log (Settings > Troubleshooting > Open Log File) for a watcher initialization warning, then raise the limit with sudo sysctl -w fs.inotify.max_user_watches=524288. To persist it, add fs.inotify.max_user_watches=524288 to /etc/sysctl.d/99-inotify.conf and run sudo sysctl --system.

Double-click the card header to collapse it. A collapsed card takes less vertical space while still showing status. Double-click again to expand.

Worktree cards in the sidebar, expanded and collapsed

Lifecycle Stage Chip

A small triangular chip in a card's top-left corner names the next action that worktree needs. It only appears when there is something to act on, and it is absent while git data is still resolving.

ChipTooltipMeaning
AmberAgent waiting for inputAn agent in this worktree is blocked on you.
BlueComplete: in reviewA linked issue, an open PR, no uncommitted changes, and no active agent. It is ready for review.
PurpleReady for cleanupThe PR is merged. The worktree has served its purpose and can be deleted.

Blue, not green, for complete. Across the app green means working and blue means in review or finished; a green "done" chip would compete with the one signal that means work is happening right now.

When several conditions apply the chip shows the highest-priority state: cleanup, then complete, then waiting. A merged PR always wins, because deleting the worktree is the most definitive thing left to do.

Only cleanup is gated to secondary worktrees. Waiting and complete render on the main card too: the main worktree can have an agent waiting on it like any other.

Note
The blue "complete" chip needs both a linked issue and an open pull request. A PR with no linked issue won't trigger it. For linking issues and PRs to worktrees, see Code Forge.

A purple chip also adds a trash-icon button to the row toolbar, next to the three-dot menu, so Delete is one click rather than a menu dive. The icon stays muted red until hover. Once a worktree reaches complete, open the Review Hub from its menu to stage, review and push.

Row Status Signals

To the right of the branch name sits a set of small signals. Each is independent and degrades on its own, so a stale freshness pill never hides a fresh commit chip beside it.

SignalWhat it shows
Git state indicatorAn inline label for an in-progress git operation: rebasing, merging, cherry-picking, reverting or detached, amber; conflicted, red. It is a passive label, not a control.
Commit chipAn activity dot and the relative time of the last commit ("2m"). The dot fades from accent to idle as time passes. Hover for author, subject and absolute timestamp.
Upstream sync badge↑N / ↓N against the upstream remote. When upstream and the base branch have diverged from each other, a second pair of counts appears against the base branch.
Freshness pillHow recently the working tree status was read. Hidden under 30 seconds. Between 30 and 60 seconds, a dim relative-time pill; between one and five minutes it brightens; past five minutes it becomes a clickable Refresh.
External badgeA folder-out icon on a worktree git reports from outside the project directory, with the full path in its tooltip. See Worktrees outside the project.
PinA pin icon on a worktree you pinned to the top from the card menu.
EnvironmentThe resource-environment popover, when the worktree runs on non-local compute. See Remote Compute.
Dev serverA dev-preview indicator when one is running for this worktree. See Dev Preview.

The author avatar is deliberately left off the row: a column of identical "you" avatars adds noise and no information. The commit chip's tooltip surfaces the author when you need it.

Row-level status signals sit beside the branch name: branch type, churn, and distance from main

Where mid-rebase recovery lives

The git state indicator tells you a worktree is stuck mid-rebase or mid-merge. It does not offer to fix it. The Continue and Abort buttons that used to sit on the card were removed: a two-button recovery on a narrow row, with no view of the conflicted files, invited exactly the wrong call.

Recovery lives in the Review Hub's conflict panel instead, next to the conflicted file list, the per-file resolve controls and the rebase sequence rail. Continue is only enabled once no conflicts remain.

Status still resolving, and the review button

Before git has answered for a worktree, its subtitle reads Checking status… rather than "No recent activity", an all-clear the data can't yet support.

The card's review-opener tracks what the worktree actually needs:

  • Review & Commit when the tree has changes.
  • Review & Push when the tree is clean but commits are unpushed. The next step is still in the Review Hub, so the button stays; only the label shifts.
  • Nothing, when the tree is clean and everything is pushed.

Alarm Salience and the Collapsed Pill

A row can carry a failing CI run, an upstream auth failure, a behind-upstream count and more at once. Salience tiers keep it from becoming a wall of badges: the most important signal renders first, on the left.

TierSignalTone
3 (highest)CI failedRed
2Auth failed (couldn't fetch from upstream)Amber
1Behind upstreamAmber
0No alarmNone

Only a failure CI state climbs above tier 0. Pending and neutral states stay quiet, so a long push session doesn't flicker the row between tiers as checks queue and finish. Detached HEAD is deliberately not an alarm either: the git state indicator already labels it in the title row, and a colored pill would double-label the same fact.

What makes CI "failed"

The CI signal is derived from the PR's required checks, not the raw status rollup, the same derivation the PR dropdown in Code Forge uses, so the two never disagree. Tooltips read like "2 of 5 required checks failing", or "No required checks" when the branch has none configured.

The derivation refuses to invent a green. If the checks page is truncated, if a context arrives in a shape it can't classify, or if no required checks are configured at all, it falls back to the raw rollup state and shows no summary rather than reporting a pass over checks it never saw. A stale required run counts as failing, because it has not resolved to a passing state.

Collapsed, the badge row reduces to a single pill in the header carrying the highest active tier's color and label: "CI failed" in red, "Behind" in amber. Expand the card to get the individual badges back.

Quick State Filter

The filter bar above the worktree list filters by agent state. Four segments: All, Working, Waiting, Finished. "All" keeps its text label; the other three are an icon plus a count, which is what keeps the bar legible in a narrow sidebar.

  • Working: a green spinner. When the count is above zero it turns slowly, gated by prefers-reduced-motion. It is a glance signal, not a change in polling cadence.
  • Waiting: an amber hollow circle.
  • Finished: a blue check circle.

The active chip carries a subtle inset bottom rule in neutral text color, not the accent. Accent stays scarce. Clicking an active chip resets to All. Each chip's accessible name carries noun and count together ("Working, 3 worktrees"), so dropping the visible text label costs screen-reader users nothing.

"Finished" is not "Completed." Finished is a worktree-level label covering complete and cleanup worktrees. Completed is a session-level state: one agent that finished a turn. Both labels appear on the same card: the filter bar counts worktrees, the collapsed card indicators count sessions.

Counts are disjunctive. Each chip's count is what the list would show if only that chip were active, not the count within the current filtered set. With All selected and "Waiting (2)" on the bar, clicking Waiting reliably shows two worktrees, regardless of what you clicked before.

The trailing arm button

A small lightning-bolt button sits at the trailing edge of the bar, past a divider. One click arms every unarmed agent in the currently filtered set and opens the Fleet Arming Ribbon, ready for an Accept, Interrupt or broadcast.

It addresses agents only. Plain shells are excluded (they stay armable individually and through the header fleet picker), which matches the state filters beside it, since those count agents too. The tooltip and accessible name track the state:

StateLabel
No filter, nothing armedArm all N agents
Filter active, nothing armedArm N matching agents
Some already armedArm N more agents / Arm N more matching agents
Nothing eligibleNo agents to arm / No agents match the filter
Everything already armedAll agents are armed / All matching agents are armed

The button never disappears (it dims and disables), so its place in the bar stays predictable.

Tip
Filter to the state you want to act on, then arm. Click Waiting, click the arm button, press Command-Y Control-Y Control-Y , and every waiting agent in view is accepted. See Fleet.
Note
The quick filter is session-only. It isn't written to disk and resets on restart, so you never launch Daintree to find worktrees quietly filtered out. The advanced filters below do persist.

PR and Issue Badge Freshness

PR and issue badges come from cached provider responses. The badge's glyph tells you how much to trust the number right now, without opening a panel or reading a tooltip.

GlyphMeaning
NoneFresh: the cached value is recent and no refresh is queued.
ClockStale: older than the freshness threshold, with a refresh in flight or queued.
Cloud-offDisconnected: a rate limit, an open circuit breaker after repeated failures, or PR detection paused. The last known value stays on screen; the glyph says don't treat it as live.

The tooltip carries the relative timestamp ("Updated 4 minutes ago") and, when disconnected, the reason and a retry hint. With no forge token configured the badge grayscales and the tooltip says so.

Note
For tokens, providers and rate-limit behavior, see Code Forge. To force every title, PR and CI badge to re-resolve, use the sidebar's refresh button.

Collapsed Cards

Collapsing a card hides the body (git status, last commit, terminal list, quick actions), leaving the branch name and a row of compact session indicators. Each indicator is an icon plus a count.

StateIconToneAnimated
WorkingSpinner circleGreenYes
DirectingInteracting circleBlueNo
WaitingHollow circleAmberNo
DoneCheck circleSlateNo
ExitedExited circleDimNo

Hovering the group shows the full summary, such as "3 sessions: 2 working, 1 waiting". Idle sessions are never shown: when every session is idle, or there are none, the collapsed card is just the branch name. For what each session state means, see Agent States.

Focused Sub-Line

An expanded card reveals a thin line of context below the branch name when it has focus, from hover or keyboard. It is hidden by default, expands with a short height transition and a brief fade, and collapses again when focus leaves. The animation respects prefers-reduced-motion.

Up to three dot-separated segments, in order: the changed file count when the tree is dirty ("3 files"), the last activity time as a live relative timestamp, and the short status label the header uses. It stays out of the way until you're looking at the card, then gives you enough for a triage call without expanding the body.

Primary Worktree Card

The main worktree's card uses a two-row header that doubles as a project-level overview.

The top row shows the project name rather than the branch, a sprout icon, a collapse button and the actions menu. The second row, visible when expanded, shows the branch label, upstream sync, and an aggregate summary of the secondary worktrees.

  • Upstream sync: ↑N in green for commits ahead, ↓N in amber for commits behind, with the full sentence on hover.
  • Aggregate summary: a branch icon with the total worktree count, then colored counts for working and waiting. The finished count only appears once nothing is working or waiting, so finished work never drowns out work that still needs you.

Expanded, the body adds the same worktree and agent counts, plus a health row with CI status, open PR count and open issue count. That row needs a configured forge token.

The Worktree Sidebar

The sidebar is where you navigate worktrees. It is virtualized and keyboard-first: only the rows in view plus a small overscan buffer render to the DOM, so scroll performance holds on projects with hundreds of worktrees.

The main worktree renders outside the virtual surface and stays at the top. Below it, pinned worktrees, then everything else by the active sort, then external worktrees. Deleted-worktree rows sit at the position they were deleted from.

When rows sit fully offscreen, small pills appear at the top and bottom edges ("Scroll up, 12 more above"), and clicking one jumps to that end of the list.

The search box, filter popover and results list follow the ARIA combobox pattern, so filter changes and result counts are announced. See Search and Filters for what the controls do.

Refreshing

The refresh button in the sidebar header is a true full refresh, not a nudge. It discards cached forge data and re-resolves worktree state, issue and PR titles, and CI badges, re-reading from disk and from the provider. That costs provider round trips against your rate limit, so it is an escape hatch for when something changed outside the app, not a habit to build on top of routine polling.

The pass is watchdogged and each phase is isolated: a slow PR fetch can't sink the worktree refresh, topology re-discovery failing doesn't stop the status pass, and the button always resolves. If the host doesn't respond at all, a Refresh failed toast says so rather than the button quietly doing nothing.

Keyboard navigation

The sidebar has two focus modes. List mode is the default, with focus on a row. Toolbar mode moves focus into the focused row's inline action buttons. The grid holds a single tab stop and tracks the active row through aria-activedescendant.

List mode

KeyAction
/ (or j / k)Move row focus
PgUp / PgDnMove one viewport page
Home / EndFirst / last worktree
Enter or Enter the focused row's toolbar
SpaceSelect / switch to the focused worktree
Alt+ / Alt+Reorder the row in the custom sort

Toolbar mode

KeyAction
/ Move between toolbar buttons (wraps)
Home / EndFirst / last toolbar button
Ctrl+Home / Ctrl+EndReturn to list mode, jump to first / last row
/ Return to list mode and move row focus by one
EscReturn to list mode, keeping the active row

Leaving the window drops back to list mode, so re-entering the app never lands inside a toolbar. The Command Control Control modifier is ignored throughout: the grid has no bindings of its own that use it.

Note
For every keybinding across the app, see Keyboard Shortcuts.

Reordering

Alt+ and Alt+ move the focused worktree in the list. The custom order is per-project and survives restarts, but it only applies when Sort by → Custom order is selected in the filter popover. Reorder under any other sort and the keybinding does nothing.

Reordering is also off while the list is grouped by type or a search query is active. Both controls sit above the worktree list, and with either on the Alt+Arrow keys and drag-to-reorder do nothing. The order itself is kept, you just cannot edit it until you clear the filter.

Focus follows the card as it moves, and the screen-reader announcement reports the new position once the key release settles, so holding the key does not fire an announcement per step.

With the mouse, drag a card and a thin directional line shows where it will land. Escape mid-drag cancels, and the card snaps back with an assertive "drag cancelled" announcement.

Placeholder cards during creation

While a worktree is being created, single or bulk, a placeholder card holds its eventual position so the list doesn't reflow each time a bulk item finishes out of order. Pending placeholders show pulsing skeleton lines with aria-busy="true" and a label like "Creating worktree {branch}". A failure switches the card to an error state titled Couldn't create worktree, carrying the branch name, the error message, and Retry and Dismiss links.

Reconnecting and load errors

When the renderer loses contact with the worktree host (rare, and usually after a sleep/wake cycle or a host crash) the sidebar header shows a spinner and Reconnecting…. Past a short threshold it escalates to Reconnecting… last updated {relative time}, so you can tell fresh data from a stale snapshot. A failed initial load shows a top banner titled Couldn't load worktrees with the error and a Retry button.

Search and Filters

The search bar at the top of the sidebar finds worktrees by name or branch and filters as you type. Press Escape to clear the text and any active filters in one action; press it again on an empty search to blur the input. The X at the right edge does both.

The filter icon inside the search bar opens the advanced popover. A small green dot on the icon marks any active non-search filter.

CategoryOptions
Sort byDate created, Recently updated, Alphabetical, Custom order
Group by typeToggle: groups worktrees by branch-type prefix, plus an "Outside the project" section when external worktrees exist
StatusActive, Dirty, Stale, Idle
Branch TypeFeature, Bugfix, Refactor, Chore, Docs, Test, Release, CI, Deps, Perf, Style, WIP, Main, Detached, Other
Issues & PRsHas Issue, Has PR, PR Open, PR Merged, PR Closed
SessionsHas Terminals, Working, Waiting, Completed, Exited
Activity15m, 1h, 24h, 7d
Dev serverHas server, Running, Starting, Error

The "Issues & PRs" section is provider-neutral: it filters on whatever forge the project is connected to, not on GitHub specifically. "Custom order" is hidden while "Group by type" is on. A Clear all filters button appears at the bottom of the popover whenever anything is active.

Chip counts inside each category use the same disjunctive logic as the quick state filter: the number you see is the number you get if you click.

Activity recency is unified across worktrees, so "Recently updated" and the activity windows mean the same thing everywhere rather than each worktree tracking its own notion of recent.

Every filter category is collapsed until you need it: the popover stays one screen tall however many are open
Note
Advanced filters and the sort order persist across sessions. The quick state filter bar does not.

Visual Hierarchy

With many worktrees open, Daintree dims idle and stale cards so active work stays prominent. A card is muted when it is idle or stale, has no waiting agent, isn't the selected worktree, and isn't hovered or focused. Muted cards use a dimmer text color for the branch name and labels.

One exception matters: a card with a waiting agent is never muted, however idle the worktree otherwise looks. Waiting is the state that needs you.

Lifecycle Failure Recovery

A setup lifecycle script can fail on a missing dependency, a wrong path or a network blip. The failure surfaces in the card footer rather than in a toast you can miss after a context switch, and the worktree holds that state until you act on it.

The footer reads Setup didn't finish. Re-run when you're ready. with two controls:

  • Retry setup: re-runs the failed phase. The label becomes "Retrying…" while it runs, then the footer clears on success. The retry is pinned to the card's own worktree, not whichever one is focused, so you can trigger it from anywhere.
  • Show details: expands the captured error and output in a scrollable monospaced block. Daintree keeps the last 8192 bytes.

The footer survives card collapse and persists across restarts, so a half-set-up worktree never quietly disappears.

Restarting a Session

Restarting an agent session uses a two-click armed pattern, which guards against accidental scrollback loss without putting a modal in the way.

  1. Open the panel's three-dot menu and click Restart Session. The item arms: its background tints amber and the label becomes Confirm Restart (3s) with a ticking countdown.
  2. Click again within three seconds. The menu stays open between the two clicks, so there is no moving target to chase.

Without the second click the item disarms itself. It also resets if you switch panels or if restart eligibility changes mid-countdown. The armed item's accessible name reads "Armed: click again to confirm restart. N seconds remaining", so the countdown isn't visual-only.

The Card Menu

The card's three-dot menu gathers everything you can do to a worktree without leaving the sidebar: reordering, launching, browsing, dev-server control, forge links, review, and context export.

The card menu, grouped by what the action touches: order, sessions, files, dev server, forge, review

Sessions Submenu

The menu's Sessions submenu fans one action across every session in the worktree. Each item shows its eligible count and disables at zero.

ItemWhat it does
Dock AllMove every grid-resident session into the dock.
Maximize AllRestore every docked session to the grid.
Close AllClose every active session.
Terminate AllSend SIGTERM to every agent process, for when Close All would wait too long.
Reset All RenderersForce-reload the terminal renderer for every session here, useful after a GPU driver hiccup or a font-loading glitch.
Clear Session HistoryDrop this worktree's resumable session journal.
Select All TerminalsArm every agent terminal here and open the Fleet Arming Ribbon.
Select Waiting AgentsArm only the sessions currently waiting.
Select Working AgentsArm only the sessions currently working.

Close All and Clear Session History confirm from every dispatch source, including the action palette and agent-initiated calls, not only from the menu.

Tip
Select Waiting Agents then Command-Y Control-Y Control-Y accepts every waiting agent in a worktree in two clicks and a chord.

WSL Git Banner

On Windows, when a worktree lives inside a WSL distribution, Daintree probes whether git can be routed through the interop layer and shows a banner on the card describing what it found.

The WSL git banner does not appear on macOS. Daintree always runs git natively.

Note
For WSL setup notes and other platform-specific git tips, see Troubleshooting.

Worktrees Outside the Project

Git will happily report worktrees that live outside the project directory. Daintree identifies those and sorts them apart: they sink below everything the project owns, ahead of the pin check, so a stale pin can't lift one back to the top. In grouped mode they get their own Outside the project section.

On the card, a FolderOutput icon carries the tooltip "Outside the project directory" and the full path. External worktrees can't be pinned, and the pin entry is ignored if one exists.

Containment is only asserted on an explicit answer from the workspace host. When the host can't resolve the boundary, the worktree is treated as internal: a transient path-resolution failure shouldn't demote every worktree in the project at once.

Pinning is the counterpart: Pin to Top / Unpin on any non-main, non-external card's menu. Pinned worktrees sit directly under main and every sort honors that.