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.

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