Queue rules
A rule maps items to a skill; the first match wins, top first. Only skill is required:
queue:
- skill: fix
where: { type: pr, mine: true, checks: failing }
- skill: implement
source: jira
where: { status: Ready }
deliver: pr| Key | Does |
|---|---|
skill, source, where | What runs, on which items: Matching items |
provider, model | Overrides routing for this rule; two models retry |
skills, mcp, instructions, with | Extra skills, MCP servers, instruction files, parameters |
deliver | draft_pr (default), pr, push, report, pending_review or plan; see Delivery |
branch, order, units | Branch template, item order, units per run |
then, quota | More steps, quota checks |
plan_tasks | On a deliver: plan rule, the most tasks its plan holds; 8 by default |
on_done | Comment and move your items |
gates | The rule's own gates, on top of the top-level and repo ones |
wait, post, thread | Plan and report posts: waiting, lean or full, where they go |
run | window (default) or soon |
Types, defaults and full meanings are in the reference.
An item goes down the list and stops at the first rule whose source and where it matches. One that matches none is skipped as no_rule, and plan --why <key> names the closest rule and the conditions it failed:
Four kinds of item go their own way: awaywork's own PRs reach only ours rules, a plan's tasks only the first planned rule for their skill, a card carrying awaywork's plan stays on its plan rule, and an item whose report you approved goes on to the next rule.
The default queue
With no queue, or an empty one, nothing runs: plan says so and doctor warns. awaywork init writes these rules, minus those for sources you didn't configure:
| Skill | GitHub | Trackers* |
|---|---|---|
fix | Your PR, checks failing | QA |
fix | Your PR with unresolved threads; your awaywork PR with threads you started | |
review | A trusted author's PR requesting your review | |
implement | In progress | |
plan | Current sprint (Asana), Todo (Linear), To Do (Jira) |
* An Asana section, a Linear or Jira status, matched ignoring case.
Read it row by row, left to right: that's the rule order, so broken things get fixed before new work starts. On Bitbucket only the tracker rules apply.
The section and status names are guesses; the /away setup writes the queue out with your tracker's real ones.
Your reviewed awaywork PRs
awaywork's own PRs match only rules whose top-level where names ours (in any it's an error), so they wait for your review. After it:
- skill: fix
where: { type: pr, mine: true, ours: true, my_unresolved_threads: { not: 0 } }- The new PR stacks on awaywork's, on a branch named after it (
away/acme-api-9for #9). - Merge it, and the next pickup takes
-2. While it's open or closed unmerged, #9 is skipped asawaywork tried this in <PR>; delete away/acme-api-9 to run it again. - Resolve the threads it answered, or the rule takes #9 up again for them.
- The rule runs on threads you started, but the agent reads every unresolved one.
oursis the GitHub source's: a custom source'soursfield holds nothing back.
A plan's tasks
A task matches only rules whose top-level where names planned: true, the first whose skill is the task's (implement by default), and those rules take only tasks. Tasks carry their card's fields:
- skill: implement
where: { planned: true }
deliver: pr
- skill: fix
where: { planned: true, labels: bug } # a plan's `skill: fix` tasks, on bug cards only- A plan naming a skill no
plannedrule takes isn't posted. - A
plannedrule deliversdraft_pr,prorreport. A task is done when its PR merges, or, under a rule that reports, once its result is on the card: a report task, which nothing can wait on. - Config errors:
planned: false,plannedinsideany, and adeliver: planrule with noplannedrule.
Finished items
Rules see open items until one names state: open, merged or closed (closed unmerged, done, canceled). closed is when it finished, a date field:
- skill: review
where: { type: pr, mine: true, state: merged, closed: { newer_than: 14d } }
deliver: report$ awaywork plan --rules
Queue
#1 review type=pr mine=true state=merged closed:newer_than=14d deliver=report
85 taken: acme/api#329, acme/api#328, acme/api#327, …Details:
- A
where, or ananyentry under one, that never namesstatematches open items only:any: [{ state: merged, label: release }, { label: hotfix }]takes mergedreleasePRs and openhotfixones. closedwithout astatethat can bemergedorclosedis an error; so isclosed: { older_than }without anewer_than.- Only when a rule can match finished items (in
anyandnottoo) does its source read them: back to the widestclosed: { newer_than }, or 7 days. - GitHub reads your merged or closed PRs and closed assigned issues, plus your
searchANDed withis:closed; search stops at 1,000 results, andplanwarns near it. Jira reads resolved issues, Linear completed or canceled ones (archived too), Asana completed tasks; custom sources print their own. - A finished item links no PR: its unit branches from the default branch. So
pushandpending_revieware errors on a rule matching only finished items, and a rule matching both skips them. - Attempts work as for open items; a PR merged since its last unit counts as changed.
order
Units run in the source's order, newest first for GitHub, so with little quota the oldest can wait. order: oldest, newest or updated sorts them; items without the time go last. A card's tasks run back to back where the first ranks, in dependency order.
Retry on the next model
- skill: fix
where: { type: pr, mine: true, checks: failing }
model: { claude: [sonnet, opus] }A step that fails on sonnet (a failed turn or the turn cap) runs once more, right away, on opus: in a one-step rule and in each step of a chain, where a step can bring its own pair. Details:
- The retry starts from the clone as the failed step found it, its commits, edits and
.away/out/changes dropped (gitignored files stay), on the same provider, without recheckingwhenorquota. - Only a failure retries: not a quota stop, the time limit or Ctrl-C. No budget left, and the failure stands.
- Both attempts show in the report and cost budget. For giving up on an item the retry counts, unless a quota, session or window stop cut it short.
notifyhears only the retry.
on_done
Moving the card tells the next person it's their turn; a comment tells them what happened:
- skill: implement
source: jira
where: { status: Ready }
on_done: { comment: result, move: In Review }comment: result posts, before the move, the result headed with the rule's skill: a report rule's report, else what it published (Opened <PR>, Opened <PR> as a draft, Pushed 2 commits onto <PR>, Left your pending review on <PR>), then a Status: line and — awaywork.
move is in the tracker's words, ignoring case: a Jira status (via its transition), a Linear state of the team, an Asana section (in the first of the task's projects that projects names and has it), a GitHub label (created if new), or a custom source's write. Both show under the unit in the report:
1 ENG-12 implement done, 7 turns https://github.com/acme/api/pull/41
result posted on https://acme.atlassian.net/browse/ENG-12
on_done moved ENG-12 to "In Review"When it runs:
- Once the chain published: a PR opened, a push landed, a report written, a pending review left. Not after a failed or stopped step, a run that ended first, a loop out of rounds, a step that left nothing or asked questions. A passed-over step, fired gates or a failed
verifydon't hold it back. deliver: plan: once the plan is posted. It takes nocomment, as the plan is the result.- A report whose post waits: the result posts, and the move waits for your
approve, made while the card is still where the post saw it. - A report task posts its result on the card regardless; a PR task only when asked. A
plannedrule moves the task's card, which stays with its plan rule.
What it writes to:
- The unit's items that are yours and from the rule's tracker (its
source, else the matched item's). A Jira rule leaves the PR in the unit alone. - Yours: your PR, an assigned issue, a Jira, Linear, Asana or custom source item. Never a PR you're reviewing or a
search-only item (not moved, not yours,got no result, not yours). - Ownership is reread right before the move:
couldn't move ENG-12 to In Review: ENG-12 is no longer yours: not assigned to you. A customwritechecks it itself. - A missing status names the ones there are:
ENG-12 has no transition to "In Review", only to "QA", "Done". - Bitbucket and a custom source without
writecan't move;doctorwarns. The token needs write scope (write:jira-work, a Linear key with write); Asana's and gh's have it. - A result over 30 KB isn't posted. Neither write fails the unit; errors are masked in the report.
- Each write changes the item's updated time. awaywork reads it before and after; if it's still the plan's (or the result's), it records the new one so its own writes don't look like a change, while someone else's still counts. A custom
writeprints both times for this; otherwise make the rule stop matching where the item moved to.
run: soon
Some work shouldn't wait for the night: a PR gone red, an answer to awaywork's questions. awaywork watch starts a soon rule's units within minutes of them coming due, a new item or one changed since its last attempt:
watch: { every: 5m } # the default
sources:
jira: { site: acme.atlassian.net, email: me@acme.dev, every: 15m } # a slow tracker
queue:
- skill: fix
where: { type: pr, mine: true, checks: failing }
run: soonDetails:
- The daemon polls the soon rules' sources every
watch.every, a source with its own longereveryless often (both 1m or more). A poll plans only the soon rules and reads no quota. - Window runs, manual
runandplantake every rule;planmarks soon units#1 soon. - GitHub Actions has no daemon: soon rules run in the windows. Costs and backoff are in Scheduling.