No repo requires a passing status check to merge, so 'do not merge red' is a habit rather than a gate #927

Open
opened 2026-08-25 09:26:13 +00:00 by coilyco-ops · 0 comments
Owner

Raised by Kai noticing that merge behavior looked inconsistent between repositories. It is not: the repositories are uniform, and what varies is whoever is driving.

What I measured

Eight repositories across four orgs, reading main's branch protection rather than inferring from behavior:

repo protected enable_status_check required_approvals
coilyco-flight-deck/agentic-os true false 0
coilyco-flight-deck/ward true false 0
coilyco-flight-deck/infrastructure true false 0
coilyco-gaming/sirens-echo true false 0
coilyco-gaming/galaxy-gen true false 0
coilyco-bridge/agentic-os-kai true false 0
coilyco-bridge/deploy true false 0
coilysiren/coilysiren true false 0

status_check_contexts is null everywhere. Merge settings are uniform too: squash default, fast-forward-only allowed, delete-branch-after-merge on.

So no repository blocks a merge on red CI. Every one of them will merge a failing pull request today.

Why it matters, with a worked example

coilyco-flight-deck/ward sat red on main from 2026-08-17 to 2026-08-25 and kept accepting merges the whole time. Nothing stopped anyone, and promote.yml re-runs the gate on every push to main, so the red propagated to release by simply never advancing it: eight days with no release cut while the tap stayed pinned at 0.887.0.

What actually held the line was a person. ward#1681 records its author writing "I am leaving it open and unmerged rather than merging a red PR" about ward#1680. That is exactly the right call and it is completely unenforced. The next agent or the same agent on a worse day merges it.

I did the same thing four times in one session and unevenly: waited for green on four pull requests, held one back for being red, and held another for a reason that was not CI at all. Consistent-looking behavior, entirely dependent on me remembering.

Two mechanisms, not one

Branch protection is the gate: set enable_status_check with the right status_check_contexts per repo, and the forge refuses the merge. This is the durable half, and it is what makes the rule a property of the repository.

merge_when_checks_succeed is a flag on Forgejo's merge API that neither I nor, as far as I can tell, any tooling here passes. It schedules the merge for when checks go green instead of merging now. That is the built-in version of the polling loops agents hand-roll, and it removes a whole class of "waited, got distracted, merged stale".

The two compose: protection refuses a red merge, the flag stops anyone needing to sit and watch.

Why this is filed here

Per the authoring-versus-rollout split, a fleet-wide convergence of forge settings is an ansible rollout rather than a per-repo edit, and this repository owns rollout. The per-repo context-name list is the real work: status_check_contexts has to name the checks each repository actually produces, and getting it wrong locks a repository rather than guarding it.

What I did not do

I did not turn it on anywhere. It is a fleet policy change that could wedge every repository at once if the context names are wrong, and Kai should decide the shape before anything converges. Filed rather than flipped.

Suggested sequence

  1. Decide whether required checks apply fleet-wide or only to repositories with a release train (ward, agentic-os, sirens-echo are the ones where red on main stalls a pipeline)
  2. Derive status_check_contexts per repository from its actual workflow job names rather than a hand-written list
  3. Roll out through ansible, one repository first, and confirm a red pull request is genuinely refused before widening
  4. Separately, teach the agent merge path to pass merge_when_checks_succeed so waiting stops being a polling loop
Raised by Kai noticing that merge behavior looked inconsistent between repositories. It is not: the repositories are uniform, and what varies is whoever is driving. ## What I measured Eight repositories across four orgs, reading `main`'s branch protection rather than inferring from behavior: | repo | protected | enable_status_check | required_approvals | | --- | --- | --- | --- | | coilyco-flight-deck/agentic-os | true | **false** | 0 | | coilyco-flight-deck/ward | true | **false** | 0 | | coilyco-flight-deck/infrastructure | true | **false** | 0 | | coilyco-gaming/sirens-echo | true | **false** | 0 | | coilyco-gaming/galaxy-gen | true | **false** | 0 | | coilyco-bridge/agentic-os-kai | true | **false** | 0 | | coilyco-bridge/deploy | true | **false** | 0 | | coilysiren/coilysiren | true | **false** | 0 | `status_check_contexts` is `null` everywhere. Merge settings are uniform too: squash default, fast-forward-only allowed, delete-branch-after-merge on. So **no repository blocks a merge on red CI.** Every one of them will merge a failing pull request today. ## Why it matters, with a worked example `coilyco-flight-deck/ward` sat red on `main` from 2026-08-17 to 2026-08-25 and kept accepting merges the whole time. Nothing stopped anyone, and `promote.yml` re-runs the gate on every push to `main`, so the red propagated to `release` by simply never advancing it: eight days with no release cut while the tap stayed pinned at `0.887.0`. What actually held the line was a person. `ward#1681` records its author writing "I am leaving it open and unmerged rather than merging a red PR" about `ward#1680`. That is exactly the right call and it is completely unenforced. The next agent or the same agent on a worse day merges it. I did the same thing four times in one session and unevenly: waited for green on four pull requests, held one back for being red, and held another for a reason that was not CI at all. Consistent-looking behavior, entirely dependent on me remembering. ## Two mechanisms, not one **Branch protection** is the gate: set `enable_status_check` with the right `status_check_contexts` per repo, and the forge refuses the merge. This is the durable half, and it is what makes the rule a property of the repository. **`merge_when_checks_succeed`** is a flag on Forgejo's merge API that neither I nor, as far as I can tell, any tooling here passes. It schedules the merge for when checks go green instead of merging now. That is the built-in version of the polling loops agents hand-roll, and it removes a whole class of "waited, got distracted, merged stale". The two compose: protection refuses a red merge, the flag stops anyone needing to sit and watch. ## Why this is filed here Per the authoring-versus-rollout split, a fleet-wide convergence of forge settings is an ansible rollout rather than a per-repo edit, and this repository owns rollout. The per-repo context-name list is the real work: `status_check_contexts` has to name the checks each repository actually produces, and getting it wrong locks a repository rather than guarding it. ## What I did not do I did not turn it on anywhere. It is a fleet policy change that could wedge every repository at once if the context names are wrong, and Kai should decide the shape before anything converges. Filed rather than flipped. ## Suggested sequence 1. Decide whether required checks apply fleet-wide or only to repositories with a release train (ward, agentic-os, sirens-echo are the ones where red on `main` stalls a pipeline) 2. Derive `status_check_contexts` per repository from its actual workflow job names rather than a hand-written list 3. Roll out through ansible, one repository first, and confirm a red pull request is genuinely refused before widening 4. Separately, teach the agent merge path to pass `merge_when_checks_succeed` so waiting stops being a polling loop
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coilyco-flight-deck/infrastructure#927
No description provided.