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.

g1t.sh
178 linesCodeBlameRaw
1---
2title: Work and the merge queue
3description: How the work service keeps issues, 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: issues, 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## Issues and pull requests
16
17Issues and pull requests share one sequence of numbers per repository
18(the `counters` table), so `#12` names exactly one of them. A pull request
19records where its head is: a fork of its own, `pulls--<id>` in the git
20store, or a branch in the repository ([Git](/architecture/git/#pull-request-forks)).
21
22`work` reads every push from its event queue. A push to a pull request's
23head updates it: the new head, the files it changes, whether it is behind
24the base, and which other open pull requests change the same files, the
25overlap shown on each.
26
27When a pull request merges, its issue closes and records which pull
28request resolved it, and the issue's other open pull requests close as
29superseded ([How g1t works](/concepts/overview/#several-pull-requests-for-one-issue)).
30
31## Coordination
32
33Many agents and people can work on one repository at once. `work` keeps
34what lets them see each other, around the issue, which is the unit of
35work (`services/work/src/coordination/`):
36
37| Table | Holds | Module |
38| --- | --- | --- |
39| `sessions` | One run of someone's own harness (`claude_code`, `codex`, `cursor`, `copilot` or `other`), attached to an issue, a pull request and a branch | `sessions.rs` |
40| `session_entries` | What a session recorded: prompts, reasoning, tool calls, notes, each with an idempotency `key` | `sessions.rs` |
41| `claims` | An advisory, expiring "I'm working on #12" | `claims.rs` |
42| Issues' `blocked_by` | The issues that have to land first, refused if it would make a cycle | `dependencies.rs` |
43| `overlap_pairs` | Pairs of open pull requests changing the same files, announced once per pair | `overlaps.rs` |
44
45<Diagram name="coordination" />
46
47Three choices shape it:
48
49- **Nothing is a lock.** A claim is advice: anyone may claim an issue
50 someone else holds, is told about them, and decides. Nothing is ever
51 refused because of a claim (step 9 in the diagram).
52- **Nothing waits on a harness.** No cron sweeps for quiet sessions or
53 stale claims. The next read or change of a repository's coordination
54 finds them (`sweep`) and announces them then: a session quiet for 15
55 minutes is idle, one quiet for 2 hours has ended, and a claim not renewed
56 within its time (30 minutes unless asked, at most 240) has expired.
57- **Everything can be sent twice.** Starting a session is idempotent on the
58 harness's own session id, and each entry on its `key`, so a hook can call
59 every time without checking first, and a retry after a lost answer does
60 no harm.
61
62`get_work` gathers an issue's whole context in one call (`context.rs`): the
63issue, its definition of done, the required checks, claims, sessions,
64pull requests with their checks, overlaps and place in the queue, what it
65is blocked by and blocks, its decision records, and its latest
66coordination events. A session's entries on a pull request are what
67**Blame** reads to show the agent's account of a line ([Sessions and
68why-blame](/guides/why-blame/)). [Sessions, claims and
69dependencies](/guides/sessions/) has the calls.
70
71## Checks and statuses
72
73Anything can report on a commit: a workflow, an integration, g1t itself.
74Each report is a status with a `context` (its name) and a state, kept per
75commit in `commit_statuses`; richer reports are check runs and suites.
76
77Actions reports one status per run, named `<workflow> / <event>`, such as
78`CI / pull_request` (`services/actions/src/plan.rs`). A required check
79named `CI` is met by a status of that name for any event:
80`check_name` in `crates/contracts/src/work.rs` reads the name before the
81last ` / `. One that failed fails the check, even if another passed.
82
83## Deciding a merge
84
85Rulesets are stored in `work`, and judged by `crates/rules`, which does no
86I/O and so can be tested in full. The same rules run in two places:
87
88| When | Who asks | What is judged |
89| --- | --- | --- |
90| A push | `repos`, before anything is stored | Which refs may move, and how: deletion, force-push, signed commits, file paths |
91| A merge | `work` | Required reviews, required checks, CODEOWNERS, up to date, the merge queue |
92
93`requirements` gathers every rule that applies to the target branch, and
94`judge` returns, for each, whether it passes, fails or is still waiting,
95and whether the person asking may bypass it. A merge happens only when
96none fails or waits, for people and coding agents alike.
97
98When everything passes, `work` asks `repos` to land the pull request's
99head on the base, and publishes `pull.merged`.
100
101## The merge queue
102
103Two changes can merge without a conflict and still break each other. A
104repository whose rules require the merge queue tests each pull request
105together with the ones ahead of it, and moves `main` only to a state whose
106required checks passed (`services/work/src/queue.rs`).
107
108<Diagram name="merge-queue" />
109
1101. **Build.** Up to `max_entries_to_build` entries (4 by default) are
111 tested at once. For each, a sandbox merges the default branch with that
112 entry and every entry ahead of it, and pushes the result to
113 `g1t-queue/<entry>` in the repository.
1142. **Test.** Workflows that run on `merge_group` run on each of those
115 commits, and the required checks have to pass there.
1163. **Land.** Entries land in order. Once everything ahead of it has landed,
117 an entry moves the default branch to the very commit that was tested.
1184. **Eject.** An entry whose checks fail leaves the queue with a failed
119 check run on its pull request, and the entries behind it are tested
120 again without it.
121
122| State | Means |
123| --- | --- |
124| `waiting` | In the queue, not yet being built |
125| `testing` | Built, its checks running |
126| `passed` | Its checks passed; it lands when those ahead have |
127| `failed` | Its checks failed; it leaves the queue |
128| `landed` | On the default branch |
129| `removed` | Taken out by hand |
130
131### Who answers for a failure
132
133A combined state can fail because of the entry itself, or because of an
134entry ahead of it. `integration.rs` works out which:
135
136- **Held, not blamed.** A state that fails while an entry ahead is still
137 being tested is held. If that entry fails too, the held one is tested
138 again without it and is not blamed. Only once everything ahead has
139 passed does the held entry answer for the failure, so a broken change is
140 blamed on itself, never on what was queued behind it.
141- **Narrowed down.** An entry whose own checks pass but whose combination
142 fails breaks *with* some of the entries ahead. `work` builds the default
143 branch with half of them and the entry, and halves again, within three
144 extra builds (`PROBE_BUDGET`), recorded in `queue_probes`. The builds run
145 on `merge_group` like any queued state and never land. What is left is
146 reported as the `suspects`.
147- **Told everywhere.** The answer goes on the pull request's and the
148 issue's timelines, into the pull request's session, as a decision record
149 (`integration_failure`, confidence `tested`, in `decisions`), and as the
150 `integration.failed` event. A passing combination is recorded as
151 `integration.passed`.
152- **Rejoining.** A pull request the queue took out keeps its merge
153 request. Once a new commit's checks pass, it is merged again, into the
154 queue, as whoever queued it, by the rules as they are then.
155
156[The merge queue](/guides/merge-queue/#who-answers-for-it) has the table of
157outcomes from a person's side.
158
159<Aside title="How states are built">
160 `work` builds each candidate in the repository itself, through repos'
161 `build_queue_step`: the entry landed by its own merge method on the
162 state ahead of it, with the commits `land` would make there, pushed to
163 the attempt's `g1t-queue/` branch. Landing is then a fast-forward to
164 exactly that commit, and no sandbox is involved.
165 [The merge queue](/guides/merge-queue/) has the settings.
166</Aside>
167
168## Events it publishes and reads
169
170`work` publishes what happens to issues, pull requests, comments, checks
171and the queue (`issue.*`, `pull.*`, `comment.*`, `status.created`,
172`queue.changed` and the like), and the coordination events:
173`session.started`, `session.ended`, `work.claimed`, `work.released`,
174`work.claim_expired`, `work.handoff_requested`, `dependency.changed`,
175`overlap.detected`, `integration.failed`, `integration.passed` and
176`decision.recorded`. It reads
177`git.push` and a few repository events from `g1t-events-work`
178([Events](/architecture/events/#subscriber-queues)).