Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

232 linesCodeBlameRaw
1---
2title: Work and the merge queue
3description: How the work service keeps work items, pull requests, checks and rulesets, coordinates sessions and claims, decides and lands a merge, and tests many changes at once in the merge queue.
4---
5
6import Aside from '../../../components/Aside.astro';
7import Diagram from '../../../components/Diagram.astro';
8
9`work` (`services/work`, Rust) keeps everything about a change that is not
10the code itself: work items, pull requests, comments, reviews, labels,
11milestones, rulesets, checks and commit statuses, CODEOWNERS, and the
12merge queue. Its database is `g1t-work`. The code lives in `repos`, and
13`work` asks `repos` whenever it needs it.
14
15## Work items and pull requests
16
17A work item belongs to a workspace, not to a repository
18([Work items](/work/work-items/)). `work_items` holds them, numbered per
19workspace, with no repository of their own. `work_item_repos` links each
20to any number of the workspace's repositories, one row per repository, in
21a role:
22
23| Role | Means |
24| --- | --- |
25| `origin` | The repository it was filed in, if any. For a work item that was an issue, the row keeps the old number as `legacy_number`. |
26| `target` | A repository the work is meant to land in. |
27| `touched` | A repository where a pull request or commit for it landed or is in progress, added by `work` itself. |
28
29Each row also keeps the repository's `owner/name`, so a deleted
30repository still reads, and when it landed.
31
32Work items and pull requests share one sequence of numbers per workspace
33(`work_counters`), so `#12` names exactly one of them in it. Pull requests
34still live in their repository and are unique there: a new one takes the
35workspace's next number, never at or below a number its repository already
36has. Labels and milestones belong to the workspace too, shared by its work
37items and pull requests.
38
39A pull request records where its head is: a fork of its own, `pulls--<id>`
40in the git store, or a branch in the repository
41([Git](/architecture/git/#pull-request-forks)). It records the work item it
42is for, the ones it closes and the ones it is only part of, by item id.
43
44`resolve_ref` is the one place that says what `#N` names, for closing,
45Markdown, chat and the CLI. In a repository, `#N` is that repository's pull
46request N, else the work item filed there as issue N (its
47`legacy_number`), else the workspace's work item N, else pull request N in
48another of the workspace's repositories. `owner/name#N` reads as it would
49in that repository, and `workspace#N` names another workspace's.
50
51Who can see and change a work item is `item_role`
52([Identity](/architecture/identity/)).
53
54`work` reads every push from its event queue. A push to a pull request's
55head updates it: the new head, the files it changes, whether it is behind
56the base, and which other open pull requests change the same files, the
57overlap shown on each.
58
59When a pull request merges into the default branch, or a commit pushed
60there says `Closes #N` (or `Fixes`, `Resolves`), `work` marks that
61repository as landed on each work item it names, and records a resolution:
62the repository, and the pull request or commit. The work item closes when
63it has no target, or once every target has landed. `Part of #N` (or
64`Refs #N`) links the pull request or commit, and its repository as
65touched, without landing anything. References reach every repository of
66the same workspace; a work item in another workspace is never closed. When a merged
67pull request lands its own work item, that work item's other open pull
68requests in the same repository close as superseded; those in its other
69repositories stay open ([How g1t works](/concepts/overview/#several-pull-requests-for-one-work-item)).
70
71Work items were issues, one repository's each, until a migration moved
72them into their workspaces: numbered above every number the workspace's
73repositories had used, with their repository as origin, and labels and
74milestones merged into the workspace's. A job on `work`'s cron
75(`work_migration.rs`) assigned the numbers and announced the mapping once,
76as `work.migrated`, for the other services to rewrite what they keep. Old
77`/<owner>/<repo>/issues/<n>` links and `#N` references resolve through
78`legacy_number` ([Issues became work
79items](/work/from-issues/)).
80
81## Coordination
82
83Many agents and people can work on one repository at once. `work` keeps
84what lets them see each other, around the work item, which is the unit of
85work (`services/work/src/coordination/`):
86
87| Table | Holds | Module |
88| --- | --- | --- |
89| `sessions` | One run of someone's own harness (`claude_code`, `codex`, `cursor`, `copilot` or `other`), recorded in the repository it is checked out in and attached to a work item of its workspace, a pull request and a branch | `sessions.rs` |
90| `session_entries` | What a session recorded: prompts, reasoning, tool calls, notes, each with an idempotency `key` | `sessions.rs` |
91| `claims` | An advisory, expiring "I'm working on #12" | `claims.rs` |
92| Work items' `blocked_by` | The work items that have to land first, refused if it would make a cycle | `dependencies.rs` |
93| `overlap_pairs` | Pairs of open pull requests changing the same files, announced once per pair | `overlaps.rs` |
94
95<Diagram name="coordination" />
96
97Three choices shape it:
98
99- **Nothing is a lock.** A claim is advice: anyone may claim a work item
100 someone else holds, is told about them, and decides. Nothing is ever
101 refused because of a claim (step 9 in the diagram).
102- **Nothing waits on a harness.** No cron sweeps for quiet sessions or
103 stale claims. The next read or change of a repository's coordination
104 finds them (`sweep`) and announces them then: a session quiet for 15
105 minutes is idle, one quiet for 30 minutes has ended, and a claim not renewed
106 within its time (30 minutes unless asked, at most 240) has expired.
107- **Everything can be sent twice.** Starting a session is idempotent on the
108 harness's own session id, and each entry on its `key`, so a hook can call
109 every time without checking first, and a retry after a lost answer does
110 no harm.
111
112`get_work` gathers a work item's whole context in one call (`context.rs`):
113the work item, its repositories with their roles, what landed where and
114which targets are still missing, suggested targets (each with why), its
115definition of done, the required checks, claims, sessions,
116pull requests with their checks, overlaps and place in the queue, what it
117is blocked by and blocks, its decision records, and its latest
118coordination events. A session's entries on a pull request are what
119**Blame** reads to show the agent's account of a line ([Sessions and
120why-blame](/guides/why-blame/)). [Claims and
121dependencies](/work/claims-and-dependencies/) has the calls.
122
123## Checks and statuses
124
125Anything can report on a commit: a workflow, an integration, g1t itself.
126Each report is a status with a `context` (its name) and a state, kept per
127commit in `commit_statuses`; richer reports are check runs and suites.
128
129Actions reports one status per run, named `<workflow> / <event>`, such as
130`CI / pull_request` (`services/actions/src/plan.rs`). A required check
131named `CI` is met by a status of that name for any event:
132`check_name` in `crates/contracts/src/work.rs` reads the name before the
133last ` / `. One that failed fails the check, even if another passed.
134
135## Deciding a merge
136
137Rulesets are stored in `work`, and judged by `crates/rules`, which does no
138I/O and so can be tested in full. The same rules run in two places:
139
140| When | Who asks | What is judged |
141| --- | --- | --- |
142| A push | `repos`, before anything is stored | Which refs may move, and how: deletion, force-push, signed commits, file paths |
143| A merge | `work` | Required reviews, required checks, CODEOWNERS, up to date, the merge queue |
144
145`requirements` gathers every rule that applies to the target branch, and
146`judge` returns, for each, whether it passes, fails or is still waiting,
147and whether the person asking may bypass it. A merge happens only when
148none fails or waits, for people and coding agents alike.
149
150When everything passes, `work` asks `repos` to land the pull request on
151the base by the merge method asked for: a merge commit, squash, rebase or
152fast-forward (`services/repos/src/merge_methods.rs`). Then it publishes
153`pull.merged`.
154
155## The merge queue
156
157Two changes can merge without a conflict and still break each other. A
158repository whose rules require the merge queue tests each pull request
159together with the ones ahead of it, and moves `main` only to a state whose
160required checks passed (`services/work/src/queue.rs`).
161
162<Diagram name="merge-queue" />
163
1641. **Build.** Up to `max_entries_to_build` entries (4 by default) are
165 tested at once. Each entry is landed by its own merge method on the
166 state of the entry ahead of it (the default branch, for the first), and
167 the result is pushed to `g1t-queue/<entry>` in the repository.
1682. **Test.** Workflows that run on `merge_group` run on each of those
169 commits, and the required checks have to pass there.
1703. **Land.** Entries land in order. Once everything ahead of it has landed,
171 an entry moves the default branch to the very commit that was tested.
1724. **Eject.** An entry whose checks fail leaves the queue with a failed
173 check run on its pull request, and the entries behind it are tested
174 again without it.
175
176| State | Means |
177| --- | --- |
178| `waiting` | In the queue, not yet being built |
179| `testing` | Built, its checks running |
180| `passed` | Its checks passed; it lands when those ahead have |
181| `failed` | Its checks failed; it leaves the queue |
182| `landed` | On the default branch |
183| `removed` | Taken out by hand |
184
185### Who answers for a failure
186
187A combined state can fail because of the entry itself, or because of an
188entry ahead of it. `integration.rs` works out which:
189
190- **Held, not blamed.** A state that fails while an entry ahead is still
191 being tested is held. If that entry fails too, the held one is tested
192 again without it and is not blamed. Only once everything ahead has
193 passed does the held entry answer for the failure, so a broken change is
194 blamed on itself, never on what was queued behind it.
195- **Narrowed down.** An entry whose own checks pass but whose combination
196 fails breaks *with* some of the entries ahead. `work` builds the default
197 branch with half of them and the entry, and halves again, within three
198 extra builds (`PROBE_BUDGET`), recorded in `queue_probes`. The builds run
199 on `merge_group` like any queued state and never land. What is left is
200 reported as the `suspects`.
201- **Told everywhere.** The answer goes on the pull request's and the
202 work item's timelines, into the pull request's session, as a decision record
203 (`integration_failure`, confidence `tested`, in `decisions`), and as the
204 `integration.failed` event. A passing combination is recorded as
205 `integration.passed`.
206- **Rejoining.** A pull request the queue took out keeps its merge
207 request. Once a new commit's checks pass, it is merged again, into the
208 queue, as whoever queued it, by the rules as they are then.
209
210[The merge queue](/guides/merge-queue/#who-answers-for-it) has the table of
211outcomes from a person's side.
212
213<Aside title="How states are built">
214 `work` builds each candidate in the repository itself, through repos'
215 `build_queue_step`: the entry landed by its own merge method on the
216 state ahead of it, with the commits `land` would make there, pushed to
217 the attempt's `g1t-queue/` branch. Landing is then a fast-forward to
218 exactly that commit, and no sandbox is involved.
219 [The merge queue](/guides/merge-queue/) has the settings.
220</Aside>
221
222## Events it publishes and reads
223
224`work` publishes what happens to work items, pull requests, comments, checks
225and the queue (`work_item.*`, `pull.*`, `comment.*`, `status.created`,
226`queue.changed` and the like), and the coordination events:
227`session.started`, `session.ended`, `work.claimed`, `work.released`,
228`work.claim_expired`, `work.handoff_requested`, `dependency.changed`,
229`overlap.detected`, `integration.failed`, `integration.passed` and
230`decision.recorded`. It reads
231`git.push` and a few repository events from `g1t-events-work`
232([Events](/architecture/events/#subscriber-queues)).