The docs say how the merge queue blames a failure: a state is held while entries ahead of it are still being tested, smaller combinations narrow down which entries ahead a change breaks with within three builds, who is told and with what (the timeline, the issue, the session, an integration_failure decision and integration.failed), and that a fixed pull request rejoins the queue by itself once its checks pass. Sessions and why-blame gains decision records (their kinds and confidences, what g1t records itself, record_decision and list_decisions) and why over the API; the webhooks guide lists integration.failed, integration.passed and decision.recorded; the MCP reference, bring your own agent and llms.txt name the new actions.
6 files+192−130/6 viewed
| 107 | 107 | into the fork and push. The pull request can then be merged, or joins the | |
| 108 | 108 | [merge queue](/guides/merge-queue/) when the repository uses one. | |
| 109 | 109 | ||
| 110 | + | When the merge queue takes an agent's pull request out because it fails | |
| 111 | + | combined with the pull requests ahead of it, g1t | |
| 112 | + | [says which ones and which check failed](/guides/merge-queue/#who-is-told): | |
| 113 | + | in the pull request's session, as an `integration_failure` decision | |
| 114 | + | (`pull_request` `list_decisions`), and as the `integration.failed` event. | |
| 115 | + | The agent reads the other pull request's changes, fixes its own, and | |
| 116 | + | pushes; once the checks pass, the pull request rejoins the queue by | |
| 117 | + | itself. It can say what it changed with `pull_request` `record_decision` | |
| 118 | + | (`kind` `fix`), and `repository` `why` explains any line it is unsure of. | |
| 119 | + | ||
| 110 | 120 | ### Repository instructions | |
| 111 | 121 | ||
| 112 | 122 | Each agent reads a repository's instructions from a file at its root: |
| 134 | 134 | An entry fails when its `merge_group` workflows or required checks fail | |
| 135 | 135 | on the combined state, when it does not merge cleanly with what is ahead of it, or | |
| 136 | 136 | when the state cannot be built. | |
| 137 | − | It leaves the queue, and: | |
| 138 | 137 | ||
| 139 | − | 1. Its pull request records the failure, saying why: which workflow failed | |
| 140 | − | on the state, which required check did not report, or, for a conflict, | |
| 141 | − | the pull request ahead it collided with and the files. | |
| 142 | − | 2. Its conversation records that it was taken out of the queue, and why: a | |
| 143 | − | conflict links the pull request it collided with and each conflicting | |
| 144 | − | file, which opens in the pull request's changes. | |
| 145 | − | 3. The entries that were tested on top of it are tested again without it. | |
| 138 | + | ### Who answers for it | |
| 139 | + | ||
| 140 | + | A state that fails while an entry ahead of it is still being tested is | |
| 141 | + | held until those ahead are settled. If one of them fails too, the held | |
| 142 | + | entry is tested again without it and is not blamed. Once every entry ahead | |
| 143 | + | has passed, the held entry answers for the failure. So when #41 is | |
| 144 | + | broken, it leaves the queue, and #44 and #46, which were tested with #41 | |
| 145 | + | in them, are tested again without it rather than taken out with it. | |
| 146 | + | ||
| 147 | + | The entry that answers for a failure leaves the queue, and g1t works out | |
| 148 | + | what it failed with: | |
| 149 | + | ||
| 150 | + | | What happened | Who it failed with (`suspects`) | | |
| 151 | + | | --- | --- | | |
| 152 | + | | Tested on `main` alone | Nobody: the change is the cause. | | |
| 153 | + | | Its own checks fail on its head too | Nobody: the change is the cause. | | |
| 154 | + | | It conflicts with an entry ahead | That entry, and the conflicting files. | | |
| 155 | + | | A required check did not report, or the state could not be built | Nobody: the note says what to fix. | | |
| 156 | + | | Its own checks pass, one entry ahead | That entry. | | |
| 157 | + | | Its own checks pass, several entries ahead | Found by building smaller combinations, below. | | |
| 158 | + | ||
| 159 | + | ### Narrowing it down | |
| 160 | + | ||
| 161 | + | An entry whose own checks pass but which fails combined with several | |
| 162 | + | entries ahead breaks *with* some of them. g1t builds `main` with the first | |
| 163 | + | half of those entries and the failing one, and runs the `merge_group` | |
| 164 | + | workflows on it, as for any queued state. If that fails, the cause is in | |
| 165 | + | that half; if it passes, in the other half. It halves again until one entry | |
| 166 | + | is left, or it has built three extra combinations. With the default four | |
| 167 | + | entries to a batch, at most three are ahead of a failing entry, and two | |
| 168 | + | builds always find the one it breaks with; three builds find one among up | |
| 169 | + | to eight. | |
| 170 | + | ||
| 171 | + | These combinations are built in sandboxes as the queue's states are, on | |
| 172 | + | `g1t-queue/<id>` branches that are deleted afterwards, and never land. They | |
| 173 | + | do not hold up the queue: the entries behind are tested again at once. When | |
| 174 | + | a combination cannot be built or checked, or its checks are still running | |
| 175 | + | past the rule's timeout when the queue next moves, g1t stops and reports | |
| 176 | + | the entries still suspected. | |
| 177 | + | ||
| 178 | + | The halving assumes one entry ahead is the cause. When the failure needs | |
| 179 | + | two of them together, what is reported is the half that still failed, and | |
| 180 | + | can name more than one pull request. | |
| 181 | + | ||
| 182 | + | ### Who is told | |
| 183 | + | ||
| 184 | + | 1. Its pull request records the failed check, saying why: which workflow | |
| 185 | + | failed on the state, which required check did not report, or, for a | |
| 186 | + | conflict, the pull request ahead it collided with and the files. | |
| 187 | + | 2. Its conversation records that it was taken out of the queue, then what | |
| 188 | + | g1t found: the pull requests it fails with, whether that was narrowed | |
| 189 | + | down, and the check that failed. | |
| 190 | + | 3. Its issue's conversation says the same in one line, and so does the | |
| 191 | + | pull request's [session](/guides/why-blame/#sessions), so the agent | |
| 192 | + | working on it reads it there. | |
| 193 | + | 4. A [decision record](/guides/why-blame/#decision-records) of kind | |
| 194 | + | `integration_failure`, confidence `tested`, keeps it, with the | |
| 195 | + | combination, the check and the suspects as evidence. | |
| 196 | + | 5. The `integration.failed` [event](/guides/webhooks/#events) carries the | |
| 197 | + | same to webhooks and to anyone following the repository's events: | |
| 198 | + | `pull` (the entry that failed, also `number`), `issue`, `combination` | |
| 199 | + | (the pull requests tested together, in order), `check` (`name`, | |
| 200 | + | `conclusion`, `url`), `bisected`, `suspects`, `other_pulls` and `commit`. | |
| 201 | + | 6. The entries that were tested on top of it are tested again without it. | |
| 202 | + | ||
| 203 | + | A combined state that passes is recorded too: an `integration_passed` | |
| 204 | + | decision and the `integration.passed` event, with `pull` and `combination`. | |
| 205 | + | ||
| 206 | + | ### Getting back in the queue | |
| 207 | + | ||
| 208 | + | The merge stays asked for. Push a fix to the pull request, starting from | |
| 209 | + | `main` as it is now: once every check on the new commit has finished, none | |
| 210 | + | failed and every required check passed, g1t merges it again as whoever | |
| 211 | + | queued it, which puts it back in the queue. Everything a merge needs is | |
| 212 | + | checked again then, so if an approval was dismissed by the push, the | |
| 213 | + | conversation says so and you merge it again once it is approved. Until a | |
| 214 | + | new commit's checks pass, merging it again is refused: "It failed in the | |
| 215 | + | merge queue; push a fix to try again." | |
| 146 | 216 | ||
| 147 | − | Its author fixes it, starting from `main` as it is now, and merges it | |
| 148 | − | again to put it back in the queue. | |
| 217 | + | A pull request whose checks are not reported by workflows (none run on its | |
| 218 | + | pushes) is not put back by itself: merge it again once it is fixed. | |
| 149 | 219 | ||
| 150 | 220 | ## The Merge queue page | |
| 151 | 221 | ||
| ⋯ | |||
| 209 | 279 | | `results` | What building the state recorded, each with `command`, `passed` and `output`. The workflow runs on it are on its commit, `combined_commit`. | | |
| 210 | 280 | | `error` | Why it failed: a conflict, a workflow that failed on it, a required check that did not report, or what could not be built. | | |
| 211 | 281 | | `enqueued_by` | Who added it: a username, or `g1t` when it was merged automatically. | | |
| 282 | + | ||
| 283 | + | An agent working on a pull request learns of a failure without polling the | |
| 284 | + | queue: from the `integration.failed` event, from the note in the pull | |
| 285 | + | request's session (`pull_request` `read_session`), or from its decisions | |
| 286 | + | (`pull_request` `list_decisions`, or | |
| 287 | + | `GET /repos/{owner}/{name}/pulls/{number}/decisions`). | |
| 111 | 111 | | `deployment_status.created` | A deployment got a status, from any source. `data.repo_id`, `data.deployment` without its `payload`, and `data.deployment_status`. | | |
| 112 | 112 | | `release.created`, `.published`, `.released`, `.prereleased`, `.edited`, `.unpublished`, `.deleted` | A [release](/guides/releases/) changed; one change can send several, as the [release activity types](/guides/actions/#releases) say. `data.release_id`, `data.tag_name`, and `data.release` (`tag_name`, `target`, `name`, `body`, `draft`, `prerelease`, `author`, `created_at`, `published_at`), as it was for `release.deleted`. On `release.edited`, `data.changes` holds the old `name` and `body`. | | |
| 113 | 113 | | `queue.changed` | The merge queue gained, lost or settled an entry. | | |
| 114 | − | | `session.appended` | A pull request's [session](/guides/why-blame/#sessions) grew: a coding agent recorded entries with `record_session`. Busy: choose it only if you need it. | | |
| 114 | + | | `integration.failed` | A pull request failed in the [merge queue](/guides/merge-queue/#when-an-entry-fails) combined with the ones ahead of it. `pull` (also `number`) is the one that answers for it, `issue` its issue, `combination` the pull requests tested together, `check` the check that failed (`name`, `conclusion`, `url`), `suspects` the pull requests ahead it fails with, `bisected` whether smaller combinations were built to find them, and `other_pulls` the rest of the combination. | | |
| 115 | + | | `integration.passed` | A pull request's combined state passed in the merge queue: `pull` and `combination`. | | |
| 116 | + | | `decision.recorded` | A [decision record](/guides/why-blame/#decision-records) was kept on an issue or pull request: `decision`, with `number` and `issue`. | | |
| 117 | + | | `session.appended` | A pull request's [session](/guides/why-blame/#sessions) grew: a coding agent recorded entries with `record_session`, or g1t noted a merge queue failure. Busy: choose it only if you need it. | | |
| 115 | 118 | ||
| 116 | 119 | [Managing a repository](/guides/managing-repositories/) says what each of | |
| 117 | 120 | the `repo.` and `branch.` changes does. |
| 86 | 86 | request when it caught up with `main` is credited to whoever wrote it on | |
| 87 | 87 | `main`, not to the merge. | |
| 88 | 88 | ||
| 89 | + | ### Why over the API | |
| 90 | + | ||
| 91 | + | The `repository` tool's `why` action, or | |
| 92 | + | `GET /repos/{owner}/{name}/why?path=&line=&ref=`, answers the same for one | |
| 93 | + | line, with the decision records too. `ref` is a branch, tag or commit; the | |
| 94 | + | default branch when left out. | |
| 95 | + | ||
| 96 | + | ```sh | |
| 97 | + | curl "https://api.g1t.sh/repos/acme/web/why?path=src/config.rs&line=42" \ | |
| 98 | + | -H "Authorization: Bearer $G1T_TOKEN" | |
| 99 | + | ``` | |
| 100 | + | ||
| 101 | + | | Field | | | |
| 102 | + | | --- | --- | | |
| 103 | + | | `commit` | The commit that last changed the line: `hash`, `message`, `author`, `parents`, `authored_at`. | | |
| 104 | + | | `pull` | The merged pull request it arrived in: `number`, `title`, `author`, `agent`, `merged_by`, `merged_at`. Absent when it did not arrive through one. | | |
| 105 | + | | `issue` | That pull request's issue: `number`, `title`, `state` and `body` (cut at 900 characters). | | |
| 106 | + | | `decisions` | The [decision records](#decision-records) on the pull request, oldest first. | | |
| 107 | + | | `sessions` | Up to eight entries of the pull request's session behind that commit: the messages, and the tool calls that touched the file. | | |
| 108 | + | ||
| 109 | + | A pull request is found for a commit as on the site: the one whose head it | |
| 110 | + | is, or the one whose stretch of the default branch's last 300 commits holds | |
| 111 | + | it. It is public for a public repository, and needs the `code:read` scope | |
| 112 | + | otherwise. | |
| 113 | + | ||
| 114 | + | ## Decision records | |
| 115 | + | ||
| 116 | + | A decision record is a reason kept on an issue or pull request, with how | |
| 117 | + | sure it is. Sessions say what happened; decisions say why the result is | |
| 118 | + | the way it is, in a sentence, with what it rests on. | |
| 119 | + | ||
| 120 | + | | Confidence | Meaning | | |
| 121 | + | | --- | --- | | |
| 122 | + | | `claimed` | Someone said so: an agent or a person recorded it. | | |
| 123 | + | | `tested` | A check or the merge queue proved it. | | |
| 124 | + | | `approved` | A person approved it. | | |
| 125 | + | ||
| 126 | + | g1t keeps these itself: | |
| 127 | + | ||
| 128 | + | | Kind | When | Confidence | | |
| 129 | + | | --- | --- | --- | | |
| 130 | + | | `integration_failure` | The [merge queue](/guides/merge-queue/#when-an-entry-fails) found a combination of pull requests fails: which ones, and the check. | `tested` | | |
| 131 | + | | `integration_passed` | A combined state passed in the merge queue. | `tested` | | |
| 132 | + | | `approval` | Someone approved the pull request. | `approved` for a person, `claimed` for an agent's token | | |
| 133 | + | | `merge` | It was merged. | `tested` through the queue or a repository's own auto-merge, `approved` when a person merged it, `claimed` for an agent's token | | |
| 134 | + | ||
| 135 | + | Agents and people record their own, of kind `proposal` (why the change is | |
| 136 | + | made this way), `claim` (something true of it), `fix` (what fixed a | |
| 137 | + | failure) or `note`, always as `claimed`, with the `pull_request` tool's | |
| 138 | + | `record_decision` action or | |
| 139 | + | `POST /repos/{owner}/{name}/pulls/{number}/decisions`: | |
| 140 | + | ||
| 141 | + | ```sh | |
| 142 | + | curl -X POST https://api.g1t.sh/repos/acme/web/pulls/14/decisions \ | |
| 143 | + | -H "Authorization: Bearer $G1T_TOKEN" \ | |
| 144 | + | -H "Content-Type: application/json" \ | |
| 145 | + | -d '{"kind": "fix", | |
| 146 | + | "summary": "Renamed the config key #12 also introduced, so both read retry_limit.", | |
| 147 | + | "evidence": {"commit": "7d3e9a1c"}}' | |
| 148 | + | ``` | |
| 149 | + | ||
| 150 | + | `summary` is at most 2,000 characters and `evidence` an object of at most | |
| 151 | + | 8 KB. Whoever opened the pull request can record one, and so can anyone | |
| 152 | + | who may comment on it, once their email address is confirmed. Each is | |
| 153 | + | attributed to whoever recorded it, carries | |
| 154 | + | the issue the pull request is for, and is published as `decision.recorded`. | |
| 155 | + | ||
| 156 | + | Read them with the `pull_request` tool's `list_decisions` action, with | |
| 157 | + | `GET /repos/{owner}/{name}/pulls/{number}/decisions`, or for an issue and | |
| 158 | + | all its pull requests with | |
| 159 | + | `GET /repos/{owner}/{name}/issues/{number}/decisions`. A decision is as | |
| 160 | + | visible as the repository, so do not put secrets in one. | |
| 161 | + | ||
| 89 | 162 | ## Commit pages | |
| 90 | 163 | ||
| 91 | 164 | Every commit has a page of its own, `g1t.sh/<workspace>/<repo>/commit/<hash>`, |
| 300 | 300 | | [`security_alerts`](/reference/api/security/list-security-alerts/) | Its security alerts: secrets found in pushes and history (`kind` `secret`) and dependencies with known vulnerabilities (`dependency`), each `open`, `dismissed` or `fixed`. `state` and `kind` filter them. Write role. | `repo` | `repo:read` | | |
| 301 | 301 | | [`dismiss_alert`](/reference/api/security/dismiss-security-alert/) | Dismiss one by `id` with a `reason` and an optional `comment`. A secret takes `false_positive`, `used_in_tests`, `revoked` or `wont_fix`, and needs the Admin role, since a dismissed secret is let through push protection; a dependency takes `fix_started`, `no_bandwidth`, `tolerable_risk`, `inaccurate` or `not_used`, and needs Write. | `repo`, `id`, `reason` | `repo:admin` | | |
| 302 | 302 | | [`reopen_alert`](/reference/api/security/reopen-security-alert/) | Open a dismissed alert again. The same roles as dismissing. | `repo`, `id` | `repo:admin` | | |
| 303 | + | | [`why`](/reference/api/decisions-and-why/why/) | Why a line is the way it is: the `commit` that last changed `line` of `path` (at `ref`, or the default branch), the merged `pull` request it arrived in, its `issue`, the `decisions` recorded on the pull request, and the `sessions` entries behind the commit. | `repo`, `path`, `line` | `code:read` | | |
| 303 | 304 | ||
| 304 | 305 | `update_settings` takes `required_checks` (at most 20 names), | |
| 305 | 306 | `required_approvals`, `allow_ignoring_checks`, `require_up_to_date`, | |
| ⋯ | |||
| 351 | 352 | | [`update`](/reference/api/pull-requests/update-pull-request/) | Change its `base` (the branch it merges into; Write role), `labels`, `milestone`, `assignees` or `reviewers`. `state` `open` reopens it and `closed` closes it. | `repo`, `number` | `pull_requests:write` | | |
| 352 | 353 | | [`record_session`](/reference/api/sessions/record-session/) | Append entries to a pull request's session. Each has `kind` and `text`, and `tool` for tool entries. | `repo`, `number`, `entries` | `pull_requests:write` | | |
| 353 | 354 | | [`read_session`](/reference/api/sessions/read-session/) | The recorded session, oldest first. `after` skips to entries after a sequence number. | `repo`, `number` | `pull_requests:read` | | |
| 355 | + | | [`record_decision`](/reference/api/decisions-and-why/record-decision/) | Record a decision on a pull request, kept as `claimed`: a `kind` (`proposal`, `claim`, `fix` or `note`), a `summary`, and optional `evidence` (an object) and `session_id`. | `repo`, `number`, `kind`, `summary` | `pull_requests:write` | | |
| 356 | + | | [`list_decisions`](/reference/api/decisions-and-why/list-decisions/) | The decision records on a pull request, or on an issue and its pull requests, oldest first, each with its `kind`, `confidence` (`claimed`, `tested` or `approved`), `summary` and `evidence`. | `repo`, `number` | `pull_requests:read` | | |
| 354 | 357 | | [`ready`](/reference/api/pull-requests/mark-pull-request-ready/) | Mark a draft ready for review. The summary becomes its description. | `repo`, `number`, `summary` | `pull_requests:write` | | |
| 355 | 358 | | [`draft`](/reference/api/pull-requests/convert-pull-request-to-draft/) | Turn a pull request that is ready for review back into a draft. It leaves the merge queue. Its author, or the Triage role. | `repo`, `number` | `pull_requests:write` | | |
| 356 | 359 | | [`request_reviewers`](/reference/api/pull-requests/request-reviewers/) | Ask more people (`reviewers`, by username) or teams (`team_reviewers`, as `workspace/team` or a slug of the repository's workspace) to review it, added to whoever is asked already. Its author, or the Triage role. | `repo`, `number` | `pull_requests:write` | | |
| ⋯ | |||
| 364 | 367 | `record_session` takes a list of `entries`, each with a `kind` (`prompt`, | |
| 365 | 368 | `message`, `tool_call`, `tool_result` or `note`) and `text`, and `tool` for | |
| 366 | 369 | tool entries. See [sessions and why-blame](/guides/why-blame/) and the | |
| 367 | − | [merge queue](/guides/merge-queue/). | |
| 370 | + | [merge queue](/guides/merge-queue/). g1t records `integration_failure`, | |
| 371 | + | `integration_passed`, `approval` and `merge` decisions itself; see | |
| 372 | + | [decision records](/guides/why-blame/#decision-records). | |
| 368 | 373 | ||
| 369 | 374 | ## `memory` | |
| 370 | 375 | ||
| 212 | 212 | into the fork, push, and merge again. | |
| 213 | 213 | With the merge queue on, merging adds the pull request to the queue | |
| 214 | 214 | instead; `GET {repo}/queue` shows it being tested with the pull requests | |
| 215 | − | ahead of it, and it lands only if that combination passes. | |
| 215 | + | ahead of it, and it lands only if that combination passes. When it fails | |
| 216 | + | combined with them, g1t says which pull requests ahead it breaks with | |
| 217 | + | and which check failed: in its conversation, its issue's, its session, | |
| 218 | + | `GET {repo}/pulls/{number}/decisions` (an `integration_failure` | |
| 219 | + | decision) and the `integration.failed` event. Read the other pull | |
| 220 | + | request's changes, fix yours, and push: once its checks pass, it rejoins | |
| 221 | + | the queue by itself. | |
| 222 | + | - **Record a decision**: why the change is made this way, or what fixed a | |
| 223 | + | failure: `POST {repo}/pulls/{number}/decisions` with `kind` (`proposal`, | |
| 224 | + | `claim`, `fix` or `note`) and `summary`, and `evidence` (an object). | |
| 225 | + | - **Why is a line the way it is:** `GET {repo}/why?path=src/a.rs&line=42` | |
| 226 | + | answers with its commit, the pull request it arrived in, that pull | |
| 227 | + | request's issue, decisions and session. | |
| 216 | 228 | ||
| 217 | 229 | ## Where work runs | |
| 218 | 230 |