WorkQueueOps

WorkQueueOps is a pattern for processing a backlog incrementally across workflow runs. The native tools.work-queue feature provides a Git-backed queue with fair scheduling, task dependencies, and Claim-scoped effects. Issue checklists, sub-issues, cache-memory, and repo-memory are lightweight alternatives when simple progress tracking is enough.

ApproachChoose it when
Native work queueMultiple workers need fair scheduling, verified dependencies, and authorization for each task attempt.
Issue checklistA small backlog needs visible progress without deploying a native queue.
Sub-issuesEach work item needs its own discussion, assignee, and completion status under a parent tracking issue.
Cache-memoryA short-lived, branch-local backlog can be reconstructed if the cache is evicted.
Repo-memoryA file-based backlog needs Git history and persistence shared across workflow branches.
flowchart LR
    queue[(Queue)] --> process[Process next N items]
    process --> mark[Mark complete]
    mark --> queue

The native queue stores immutable tasks and lifecycle events in work-queue.jsonl on a dedicated Git branch. A trusted producer submits tasks, a dispatcher requests assignments, and workers process them. On an absent branch, the first producer submit atomically installs its compiler-approved Policy with Work. On an existing AW-managed queue, trusted submission and dispatch processing install missing configured pools and worker profiles on demand, before deployment synchronization. Publish new workers’ Markdown sources and compiled locks to the default branch and recompile the producer or dispatcher; manual registration and draining are not required for additions. The separate PoolInstall operation verifies new workers against that immutable default revision while preserving existing authority, deployments, scheduling limits, repository allowlists, accepted work and the Policy epoch. Changes to existing authority or scheduling economics remain administrator-only and require a quiescent queue. The scheduler selects eligible tasks under Policy; the agent does not choose which task receives a Claim.

A Claim authorizes one attempt at a task. Workers stage safe outputs and a finish intent for each original Claim. Trusted processing records Completion before authorizing those effects and records Result only after independently verified delivery. Dependent tasks wait for their predecessors’ Results, not merely for a worker run to finish.

flowchart LR
    producer[Producer] --> queue[(Git-backed queue)]
    queue --> dispatcher[Fair dispatch]
    dispatcher --> worker[Worker with Claim]
    worker --> completion[Completion]
    completion --> effects[Deliver effects]
    effects --> result[Verified Result]
    result --> queue

Native tools.work-queue supports Git storage only. Issues and pull requests can be dependency nodes, but they are not queue-storage backends. Use the operator reference to inspect and recover queue state, and the protocol specification for implementation coverage and remaining security requirements. The Linter Factory shows dispatcher and worker workflows with Claim-scoped outputs.

This example processes one read-only README check. The operator CLI acts as producer; a dispatcher requests one assignment, and a worker processes it. Start with a repository set up for agentic workflows.

Create an assignment-only worker. Its result stays in the run summary; it cannot create Issues or pull requests.

.github/workflows/queue-worker.md
---
on: workflow_dispatch
permissions:
contents: read
copilot-requests: write
engine: copilot
tools:
github:
toolsets: [repos]
work-queue:
require-assignment: true
worker: true
safe-outputs:
noop:
---
# README check worker
Process only the original claims in work_queue_assignment.
For each Claim, read its work.task and inspect the repository README.
Do not modify repository resources.
Report your findings with noop, using the Claim's original handle as claim_handle.
Then call work_queue_claim_finish with that claim_handle and outcome "completed".
If the task cannot be completed, finish that Claim with outcome "cancelled".

Create the dispatcher with an explicit worker allowlist and a one-dispatch limit:

.github/workflows/queue-dispatcher.md
---
on: workflow_dispatch
permissions:
contents: read
copilot-requests: write
engine: copilot
tools:
work-queue: true
safe-outputs:
dispatch-workflow:
workflows: [queue-worker]
max: 1
---
# README check dispatcher
Call work_queue_dispatch_next once with:
{"pool":"default","max_claims":1,"max_dispatches":1}
The scheduler selects the Work and worker. Do not call dispatch_workflow
directly or submit additional Work.

The compiler supplies the worker’s assignment input and keeps queue-write credentials outside agent execution. The selected trusted dispatch credential needs the dispatch permissions.

Defaults are sufficient. To limit this example to one concurrent worker, merge the following into .github/workflows/aw.json:

.github/workflows/aw.json
{"work_queue": {"concurrency": 1}}

Do not replace settings for other queues or pools. Field defaults and later Policy changes belong in the scheduling reference.

Terminal window
gh aw compile queue-worker queue-dispatcher

Publish both Markdown sources, their generated .lock.yml files, and any scheduling configuration to the repository’s default branch. Configure engine authentication before running them. Compilation alone does not create the queue.

Save this immutable task as work.json:

work.json
{
"task": "Check whether the README explains installation and report missing steps.",
"effect_contract": {"kind": "none"}
}

The explicit no-write contract permits a checked completion without resource changes. Replace OWNER/REPO with the published repository:

Terminal window
gh aw work-queue --repo OWNER/REPO submit-work \
--file work.json --worker-profile queue-worker --request-id readme-check-1
gh aw work-queue --repo OWNER/REPO dispatch queue-dispatcher --wait

The first accepted submission installs Policy and Work together; there is no separate seed command. Keep the same request ID and payload when retrying an uncertain submission. The dispatcher can select older eligible Work first, and --wait waits for the dispatcher, not its workers.

Terminal window
gh aw work-queue --repo OWNER/REPO state
gh aw work-queue --repo OWNER/REPO stats --json

Inspect the worker run’s summary for its findings. Completion and reservation release are not proof of Result: the queue records Result only after verifying the Claim’s permitted effects. See delivery rules and operator commands for diagnosis and recovery.

For write-capable examples, use Linter Factory or Daily Report Portfolio. Worker updates, Issue mirroring, and compatibility requirements are covered by the reference, not additional deployment steps here.

Use GitHub issue checkboxes as a lightweight, human-readable queue. The agent reads the issue body, finds unchecked items, processes each one, and checks it off. Best for small-to-medium batches (< 100 items). Use Concurrency controls to prevent race conditions between parallel runs.

Issue checklist frontmatter
tools:
github:
toolsets: [issues]
safe-outputs:
update-issue:
body: true
concurrency:
group: checklist-queue
cancel-in-progress: false

Give the workflow a specific queue Issue. Each run reads its unchecked items, processes a bounded batch, and updates successful items from - [ ] to - [x]. Leave failed items unchecked. Add reporting or closure safe outputs only if needed.

flowchart LR
    issue[Issue checklist] --> process[Process unchecked items]
    process --> check[Check off completed]

Checklist progress markers do not provide native fair scheduling, Claim authority, or verified dependency graphs. The concurrency group serializes runs in that group, but does not protect against other workflows or humans editing the issue.

Create one sub-issue per work item under a parent tracking issue. Open sub-issues are pending work; closing a sub-issue marks that item complete. This keeps individual discussions, assignees, and results separate while the parent provides a backlog overview.

Use the GitHub issues toolset to read the parent’s sub-issues and safe outputs to report and close completed items:

Sub-issue queue frontmatter
tools:
github:
toolsets: [issues]
safe-outputs:
add-comment:
max: 6
close-issue:
max: 5
concurrency:
group: sub-issue-queue
cancel-in-progress: false

Each run lists all pages of the parent’s open sub-issues, processes at most five, and adds a result comment before closing each successfully completed item. Leave failed items open with an explanation, then add one progress comment on the parent. Use a shared concurrency group for workflows processing the same parent and check for existing results before retrying work.

Sub-issue relationships organize the backlog; they do not enforce processing order, verified dependencies, or Claim-scoped authorization. Other workflows and human edits remain outside the concurrency group’s protection.

Enable cache-memory and store a small JSON queue at /tmp/gh-aw/cache-memory/workqueue.json:

Cache-memory frontmatter
tools:
cache-memory: true
workqueue.json
{
"pending": ["item-1", "item-2"],
"completed": ["item-0"],
"failed": []
}

Each run loads the file, processes a bounded batch of pending items, and moves them to completed or failed before saving the updated file. The compiler restores and saves the cache automatically. Serialize runs that update the same queue and check whether an item’s effects already exist before retrying it.

Cache-memory is branch-scoped and unused caches can be evicted after seven days. Keep a way to reconstruct pending work from repository state; do not use the cache as the only record of irreplaceable tasks.

Use repo-memory for the same JSON queue when progress needs Git history and persistence across workflow branches:

Repo-memory frontmatter
tools:
repo-memory: true

Store workqueue.json at /tmp/gh-aw/repo-memory-default/workqueue.json. The compiler restores files from memory/default and commits and pushes updates after workflow completion. Use the same bounded processing loop as the cache-memory alternative, with a shared concurrency group for workflows writing the same queue. Concurrent updates can overwrite progress during conflict resolution.

Neither memory alternative provides native fair scheduling, Claim authority, or verified dependency graphs. Standalone tools.repo-memory persistence is not supported in native work-queue workers; these are separate queue patterns, not storage backends for tools.work-queue.

All WorkQueueOps patterns should be idempotent: running the same item twice should not cause double processing.

TechniqueHow
Check before actingQuery current state (label present? comment exists?) before making changes
Atomic state updatesWrite queue state in a single step; avoid partial updates
Concurrency groupsUse concurrency.group with cancel-in-progress: false to prevent parallel runs
Retry budgetsTrack failed items separately; set a retry limit before giving up
  • BatchOps — Process large volumes in parallel chunks rather than sequentially
  • ResearchPlanAssignOps — Research → Plan → Assign pattern for developer-supervised work
  • Work queues — Native queue roles, Claim-scoped effects, and operator commands
  • Cache Memory — Short-lived, branch-scoped file storage
  • Repo Memory — Git-backed file storage with persistent history
  • Concurrency — Prevent race conditions in queue-based workflows