Skip to main content

Issues and Pull Requests

Reading a forge from inside Daintree: the repository stats toolbar, the issues and pull-requests panel, filtering and search, CI status, review state, and commit-author avatars.

Reviewed

Repo stats toolbar

With a forge connected, the toolbar shows live counts for the active project:

  • Open issues on the repository
  • Open pull requests
  • Unpushed commits on the current branch

The counts are real totals, not a capped "20+". A + suffix appears only when the number genuinely is approximate, which happens when a cached list is serving the count and no real total came with it. Two sources feed each pill (the periodic stats poll and the count the dropdown list reports when it loads), and whichever was read more recently wins, so the badge can't freeze on a stale list count.

Click any pill to open its dropdown panel. On a narrow toolbar the stats move into the ··· overflow menu instead.

Freshness

Each count carries a small indicator of how current it is, so you know whether you're looking at fresh data or a cached snapshot:

  • Fresh (under ~90 seconds old, three times the active poll): no extra marking.
  • Aging (a few minutes old): a clock glyph and a note like · updated 3m ago.
  • Cached from a previous session: shown right after launch, before the first live refresh lands.
  • Couldn't reach the forge: the last known counts stay on screen with a note that the refresh failed.

Hovering a stat warms its dropdown in the background after a brief delay, so the panel is ready by the time you click. If the cached data is recent enough, Daintree skips the prefetch.

Activity chip

When a count goes up while its dropdown is closed, Daintree flags the new activity. A small chip appears in the corner of the pill, and the count animates as it changes. It's a glance-able cue that something arrived, not a literal "+N" badge and not an unread counter. The chip clears on its own after a few minutes, or as soon as you open that dropdown.

How often counts refresh

Four things drive the refresh cadence, and they compose rather than replace each other:

  • Base cadence. Roughly every 30 seconds while the app is in front of you, backing off to about every 5 minutes in the background.
  • The provider's own hint. Each poll can report an adaptive cadence (faster on a busy repo, slower on a quiet one) clamped to that same 30-second-to-5-minute range so a bad hint can't hammer the API or stall the counts.
  • Rate-limit health. When a provider is running low on quota, the background interval is multiplied out to give it room to recover.
  • Local git signals. A local ref moving the ahead count, or the origin count moving against you, enqueues a debounced recheck within about a second. This is what makes a commit or a fetch reflect in the toolbar immediately rather than at the next scheduled poll.

Errors get graduated backoff: the first failure retries in 30 seconds so a blip heals fast, doubling toward a two-minute ceiling for something that keeps failing. Waking your machine from sleep triggers an immediate refresh.

Rate limits

If the forge starts rate-limiting requests, a clock glyph appears on the affected pill. Hover it for a per-bucket breakdown: how many requests remain in each rate-limit bucket, each with a live countdown to its reset. Reading the breakdown doesn't itself cost any quota. When a reset time is known, the next poll is scheduled just past it rather than on the normal cadence.

Issues and PR panel

Click the issue or PR count in the toolbar to open the dropdown panel. It opens with the search input focused, so you can start typing right away.

State filter tabs at the top narrow the list. Issues show Open and Closed. PRs show Open, Merged, and Closed. The panel also takes number-based search patterns for quick lookups:

PatternFetches
#42 or 42A single issue or PR by number.
42, 45, 51Several specific items. Duplicates are collapsed.
42..50An inclusive range. The separator is two dots: a hyphen matches nothing.
42+An open-ended range from that number onwards.

A range is capped at 20 items, counted from its start. A result banner above the list says exactly what you're looking at: Showing range #42..#50, Showing first 20 of range #42..#90 (capped), or Showing #42 and above.

At the bottom, View on GitHub opens the filtered list in your browser, and New opens the forge's create page.

Instant open and caching

The panel opens without a spinner on a cold start. The first time you open it after launching the app, it shows the first page of results restored from a disk cache written by the previous session. That cache has a 10-minute freshness budget: anything older is dropped on read rather than shown, and the first live poll overwrites it within seconds anyway.

Within a session, reopening the panel shows recently cached results from memory while a background refresh runs quietly. The in-session cache is keyed by project, resource type, filter state, and sort order, so switching any of those triggers a fresh fetch. Search results always fetch live and aren't cached.

Persistent panel state

The panel keeps its state between opens within a session. Your scroll position, search text, and filters are still there when you reopen it, rather than resetting every time.

Keyboard navigation

The panel can be driven entirely from the keyboard:

  • / moves through the list from the search input
  • Enter creates a worktree for the focused item (if it's open and has no worktree yet) or switches to it (if a worktree already exists)
  • Command-Enter Control-Enter Control-Enter opens the focused item in your browser
  • Escape clears the current selection first, then closes the panel on a second press

Sort order

A filter icon next to the search input opens a sort popover with two options:

  • Newest (default): sorts by creation date, matching GitHub.com's default order
  • Recently updated: sorts by last activity

When Recently updated is active, a small accent dot appears on the filter icon. The choice applies to both the browse list and typed search results, and it's tracked separately for issues and PRs.

Note
Sort order is session-only. It resets to "Newest" when you restart Daintree.
The issues dropdown, showing this repository's open issues without leaving the habitat
The pull requests dropdown on its Merged tab: the same two-line row as issues, with the head branch in place of labels

Row details

Each row uses a two-line layout that keeps the key information visible at a glance:

  • Top row: state icon, title, CI status indicator (open PRs only), and the #number badge (click it to copy the number to your clipboard)
  • Bottom row: author, time since last update, branch name (PRs) or labels (issues, up to two shown with color dots), comment count, linked PR icon (issues with an associated PR), assignee avatar, worktree indicator, and a ··· context menu

The ··· context menu offers Open in GitHub, Switch to Worktree (if one exists), and Create Worktree (if the item is open and has no worktree yet). The create action gates on those two conditions only.

CI status icons

Open PR rows show a CI status indicator between the title and the PR number:

  • Green check: required checks passing
  • Amber dot: required checks pending
  • Red X: required checks failing

The status is derived from the repository's required checks rather than the raw rollup, so an optional job doesn't turn a mergeable PR red. Hovering names the counts: 3 required checks passing, 1 of 4 required checks failing, or No required checks on a repo without branch protection.

Daintree falls back to the raw rollup, without a count summary, in the cases where filtering to required checks would be a guess: the contexts page came back truncated, the contexts list is missing entirely, no required contexts are present, or a required check reports a state Daintree can't classify. Falling back is deliberate: a false green on a truncated page is worse than an unsummarized status. If the PR has no checks at all, no indicator appears.

When the forge is unreachable

The panel degrades rather than going blank. When a refresh fails or requests are paused, it keeps showing the last results it fetched, with an inline banner:

  • Rate-limited: GitHub requests are paused. Showing last known results., with when they'll resume, and how long ago the data was last updated.
  • Network error: Couldn't reach GitHub. Showing last known results., with a Retry button. If the failure is a token problem, the button opens settings instead.
  • No token: GitHub not connected, with an Add GitHub token button that takes you to the provider section in settings.

Whenever you're not actively searching, a footer line shows when the list was last updated, and a manual refresh control sits in the panel.

Commit-author avatars

Commit-author avatars on worktree cards resolve through whichever forge provider is active for that project, rather than being hardcoded to GitHub. Daintree picks an avatar in this order:

  1. A branded icon when the author is an AI agent
  2. The author's profile picture from the active forge provider
  3. A Gravatar match for the commit email
  4. Deterministic colored initials when nothing else resolves

Today only the GitHub provider resolves a forge profile picture. Other forges would fall through to Gravatar until they ship that capability.