alden

The review queue

alden queue finds the open PRs that request a review from you, or from a team you're on, and ranks them. It only orders the queue. It never approves, comments on or changes anything on GitHub.

Which PRs are included

Open, non-archived PRs where your review is requested, directly or through a team. Each row shows which one. Drafts are left out unless you pass --include-drafts or set queue.includeDrafts; then each is tagged Draft before its title. Narrow the queue with --repo and --org.

Narrowing and sorting

alden queue --author bots, --label <name> and --search <text> show only the PRs that match, and --sort age|waiting|updated|size re-sorts them (oldest first, longest wait first, last updated first, smallest first). These work on the ranked queue: ranking doesn't change, sorting happens inside Needs you and Fast-track separately, and a line above the table says what's narrowed ("12 of 144 PRs: author bots"). alden open 3 and alden review 3 take the numbers as shown. The web UI has the same search, filters and sort, and more (assignee, how you were asked, CI, age, drafts); see Search, filters and sort.

Every PR's facts include its assignees (up to 10), read in the same request as the rest of its details: one more small field per PR, no extra request. Queues cached before Alden read assignees are fetched again once.

Your turn

Above Needs you: the PRs you reviewed where something happened that you haven't seen. Each says what, in one line, and when:

Your turn (2)
  acme/shop#12  Charge in cents                                    3h ago
    Ana replied to 2 of your comments · 1 resolved
  acme/api#88   Retry the webhook                                  1d ago
    A push outdated your comment
    alden open <pr> or alden review <pr> opens one, and it leaves Your turn.

What counts: a reply to one of your comments (after your last word in that thread), a thread of yours resolved by someone else, and a push that makes one of your comments outdated. Counts are of threads: two replies in one thread are "replied to your comment". A reply you've already answered (on GitHub or from Alden) and a thread you resolved yourself aren't news.

What it costs. It's the desktop app's follow-up check, now also run when the web UI or alden queue fetches a fresh queue: one read-only GraphQL request finds the PRs you reviewed and when each last changed, and only a PR whose updatedAt moved since the last look has its review threads read (one request each, a few at a time). Every Alden on your machine shares one check a minute: a queue fetched within a minute of the last check (here, in another terminal, or by the desktop app) asks GitHub nothing for it. With every level Off it asks nothing at all. The queue waits up to 8 seconds for it, then shows what's stored, and the next queue has the rest. A cached queue (the UI's "As of 3 min ago") shows what the last check found without asking.

Needs you

Ranked by three factors, in this order of weight. Blocking beats waiting beats risk.

  1. Blocking. The PR has an urgent or hotfix label, another PR is stacked on it, or its author has several PRs waiting on you. Bots (such as Dependabot) don't get the "author waiting" boost.
  2. Waiting. How long ago your review was requested, or how long the PR has been open when GitHub didn't record the request. It's the one waiting time every surface shows: the terminal's Waiting column, the UI's row and the assistant's queue ("waiting 7h since you were asked").
  3. Risk.
    • Size. Lockfiles don't count.
    • Sensitive paths, such as migrations, auth, payments, infrastructure, CI config and lockfiles. See queue.sensitivePaths.
    • Code changed without test changes.
    • Files CODEOWNERS assigns to you.
    • A major dependency bump, for example major bump: nesbot/carbon 3 → 4: a breaking upgrade can be a one-line diff. See below.

Every row gives its reasons, for example waiting 3d · touches migrations/ · no tests.

Failing CI sinks a PR. When a PR's CI is failing, the author has to act before you do, so it drops to the bottom with CI failing: ping @author. A cancelled run isn't failing: GitHub's summary counts one as a failure, so Alden reads the runs themselves. A cancelled run of a check that ran again counts as that other run; when a cancelled run is all that kept CI from passing, the row says CI cancelled (cancelled in alden queue) and the PR keeps its place, neither sunk nor ready to approve, since CI didn't finish. Any other run that failed (or timed out) still makes CI failing. Skipped and neutral runs pass, as GitHub counts them. When a commit has more than 100 runs, Alden can't read them all, and goes by GitHub's summary.

What you do moves PRs. Alden learns which PRs you open ahead of their rank and which you mark useful or not useful, and ranks PRs like them higher or lower, with the reason on the row ("ranked higher: you usually open PRs touching app/api first", "ranked lower: you usually skip Dependabot PRs (4 marked not useful)"). See Learning and memory.

You can change the order. Each signal is a weight you can turn up, down or off, and rules can put PRs first or last. See Your ranking.

Re-reviews

A PR is a re-review when you've submitted a review on it (approved, requested changes, commented, or one later dismissed; not one you've started and not sent) and a review was requested from you, or one of your teams, after that. Submitting a review clears GitHub's request for you, so a newer one means someone asked you again, usually once they've acted on what you said.

Alden reads this from what the queue already fetches: your reviews on the PR (the last 20, each with the commit it was on) and when you were last asked. It costs no extra requests. A PR you reviewed and weren't asked again carries the same follow-up on --json (followUp) without the re-review mark.

Your ranking

Set it in Settings → Ranking in the web UI or the desktop app, where a preview shows your current queue's top 5 with what's on screen before you save, or in ~/.alden/config.toml under [queue.rank]. It's yours: it's read from your user config only, so a repo's .alden.toml can't change how you rank.

Weights

Each of the queue's signals adds points, and its weight multiplies them: 0 ignores the signal (no points, and its reason leaves the row), 1 is the default, 2 doubles it. Weights go from 0 to 10.

Weight Signal Points at 1
urgentLabel A label like hotfix, urgent, critical, blocker, p0 or sev1. 6
stacked Other open PRs are built on top of it. 4
sameAuthor Its author (a person, not a bot) has several PRs waiting on you. 2
ownsFiles CODEOWNERS gives you some of its files. 1
waiting How long you've been asked. 1.5 after 4 hours, 3 after a day, 4.5 after three days
size Lines to read, lockfiles aside. 1 past the fast-track size, 2 past 250 lines, 3 past 1,000
risk Sensitive paths, and a major dependency bump. 2 each
noTests Code changed without test changes. 1
learned What Alden learned from the PRs you open first and mark useful. up to 3, either way
rereview You reviewed it and were asked again: a re-review. 2

With every weight at 1, the order is exactly the one Alden has always used, re-reviews aside (a test pins it over the recorded PRs). Failing CI isn't a weight: a PR whose CI fails always comes after the others, since its author has to act first. Conflicts and drafts are shown on the row but don't score.

[queue.rank]
waiting = 2     # stale PRs matter twice as much to me
size = 0.5
learned = 0     # rank by the signals alone

Rules

A rule picks out PRs and says what to do with them:

when takes any of these, and every one given must hold:

Condition Matches
path A changed file. A glob (**/*.sql), or a plain folder or file (payments/, src/app.ts) anywhere in the repo. Any case.
label A label, in any case.
author A login (sam, dependabot, copilot), or people, bots or agents (AI coding agents), the way Bots and AI agents tells them apart.
repo owner/name, or an organisation for all of its repos.
base The branch it merges into, or a glob: main, release/*.
title Text the title contains, in any case.
[[queue.rank.rules]]
when = { path = "payments/" }
then = "first"

[[queue.rank.rules]]
when = { author = "bots" }
then = "last"

[[queue.rank.rules]]
when = { label = "security", base = "main" }
then = "boost"
by = 8
name = "security fixes"   # how the row names it

Rules apply in the order you list them. A PR takes the first first or last rule it matches; every boost and lower it matches adds up. Each applied rule leads the row's reasons: "↑ your rule: payments/ first", "↓ your rule: bots last", "↑ your rule: security fixes +8".

Rules come after learning, and learning never overrides them: first and last order the lane before any score does, and when learning would pull a PR against a boost or lower rule ("ranked lower: you usually skip bot PRs" on a PR your rule boosts), Alden sets that learning aside for the PR.

Rules are plain data on purpose, described by packages/schema/json/rank-settings.v1.json, so a tool (or, later, Alden Cloud turning "payments first, bots last" into rules) can write them the same way the settings page does.

Why a PR ranks where it does

alden queue --explain adds each row's score by weight and rule, named as [queue.rank] names them:

 1.  acme/shop#412     Charge in the customer's currency   @sam   2d   +120/-30   pass
     ↑ your rule: payments/ first · touches app/payments/ · no test changes
     score 6: waiting +3 · risk +2 · noTests +1 · ↑ your rule: payments/ first

A weight that isn't 1 shows as waiting +6 (×2). --json carries the same breakdown on every row (breakdown, with pinned for a first or last rule).

What the desktop app briefs

The desktop app briefs the top PRs in Needs you while you're away, in this order, your weights and rules included: a first rule's PRs are briefed before anything else. How many it keeps briefed is background.prepare (3 by default, 0 to 10), or "Keep the top PRs briefed in the background" in Settings → Ranking. See While you're away.

Bots and AI agents

Alden tells four kinds of author apart, and learns how you treat each, and each bot or agent:

A person's PR that's an agent's work is still the person's too: learning counts it for both ("Claude PRs" and "@ana's PRs"), and its row says whose work it is and how Alden knew: "by Claude (from its branch)", "by Claude (from its description)", "by Claude (from its last commit)". An agent's own account needs no note; its login says it.

Agents' markers. For a PR whose author and branch don't already say who wrote it (a person's PR, or yours), Alden reads its description and its last commit's message for what agents write there:

Agent In the description In the last commit
Claude Code A line "🤖 Generated with Claude Code" Co-Authored-By: Claude … <[email protected]>, or that line
Copilot coding agent <!-- START COPILOT CODING AGENT … --> A co-author at …[email protected]
Cursor A link to cursor.com/background-agent?bcId=bc-… Co-authored-by: Cursor Agent <[email protected]>, or Made-with: Cursor
Devin A link to app.devin.ai/sessions/<id> ("Link to Devin run")
Codex A link to chatgpt.com/codex/tasks/task_…
Jules "PR created automatically by Jules for task …", or a link to jules.google.com/task/<n>
OpenHands, Aider [email protected], [email protected]
Any agent app above A co-author at the app's bot noreply address, e.g. …+amazon-q-developer[bot]@users.noreply.github.com

It's conservative: a PR that mentions Claude, or quotes a marker in code or inline code, isn't an agent's. A footer counts only as a whole line, a link only with the run's id, and a Co-authored-by trailer only by its address, never its name. The description comes first, then the commit.

The lists live in packages/schema/src/authors.ts and agent-markers.ts (with where each marker comes from), so the web UI's author filter uses them too. Markers cost one small request per batch of up to 25 PRs that need them, alongside the details, and the answer is remembered by the PR's head commit, so a PR is read again only after a new push. A description edited without a push isn't re-read until then. Bots' PRs, agents' own and PRs on an agent's branch are never read. Only which agent was found is kept; see Privacy.

Fast-track

A PR is fast-tracked when all of these hold:

In a repo with no CI, only docs-only PRs qualify, because nothing has built or tested the others.

Fast-track means "you can probably clear this quickly". It's still your review.

Major dependency bumps

When a PR changes a dependency manifest (package.json, composer.json, go.mod, pyproject.toml, requirements*.txt or Cargo.toml), Alden reads just those files, as they are on the base branch and at the PR's head, in one small request per PR. A dependency whose major version goes up (or whose minor version does, below 1.0, where semver counts that as breaking) adds risk and a reason. Only upgrades count: a PR branch that's behind its base can otherwise look like a downgrade. Go modules that move to a new /vN path count as a bump. The result is remembered by the PR's head commit, so a PR is only read again after a new push. A major bump is never fast-tracked.

Nothing else in the diff is read for the queue; briefings read the whole diff.

Speed and caching

A fresh queue is one search for the PRs' ids, then their details in parallel batches, alongside CODEOWNERS (one lookup per repo and base commit, remembered for a week) and the stacked-PR counts. Paging through every PR's full details one page at a time is what made a large queue slow.

Results are cached in ~/.alden/cache. alden queue uses a queue up to 60 seconds old; --refresh skips it. The web UI and the desktop app's window keep the last queue for a day: they show it at once, marked "As of 3 min ago, refreshing…", and swap in the current one when it arrives.

To see where a fresh queue's time goes, set ALDEN_DEBUG=queue:

ALDEN_DEBUG=queue alden queue --refresh

It prints each step (viewer, search, details, CODEOWNERS, stacked PRs, manifests) with its time and number of requests to stderr. The steps overlap, so their times add up to more than the total.

JSON

--json prints the ranked queue in the format described by packages/schema/json/ranked-queue.v1.json, for scripts and other tools. Each row's breakdown is its score by weight and rule, as --explain shows it. A PR you reviewed before has followUp: when you last reviewed (lastReviewedAt), the commit you reviewed when GitHub says (lastReviewedHead), rereviewRequestedAt when it's a re-review, and news when something happened you haven't seen (text, at, the replies, resolved and outdated thread counts, the thread to open on, and its follow-up events). yourTurn is the Your turn group, newest first: each PR's pr, repo, number, title, url and news.