picatz / picatz/flowstate

Merging PRs back to back can break main, and every check passed on the way in

Open
#489 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug ci kind/bug status/blocked
Dominant language
Go
Stars
9
Forks
0
Avg merge
3h 3m
Merged PRs (30d)
509

Description

What happened

On 2026-08-12, #481, #485, #486 and #487 were merged within about ninety seconds of each other. All four were green: 7 of 7 checks each, on their current heads, against a main that at the time contained none of the others.

main did not build afterwards:

pkg/flowstate/v1/eval.go:1440:56: not enough arguments in call to runNodes
	have (context.Context, []*Node, *Scope, *UndoLog, UndoScope, int)
	want (context.Context, []*Node, *Scope, *UndoLog, UndoScope, int, map[string]struct{})

#485 added runSwitch, which calls runNodes with six arguments. #481 gave runNodes a seventh parameter, the set of body step ids whose failure was tolerated. Neither change conflicts textually, so git merged both without complaint, and neither PR's CI ever compiled the other's tree. The first thing to notice the breakage was an unrelated docs-only PR (#488), whose five red jobs had nothing to do with its diff.

The code fix rides in #483, which threads tolerated through runNodeWithVars, runNode and runSwitch.

Why the existing gates cannot catch this

Every check we have answers a question about one tree: this branch, merged with the main that existed when CI started. A semantic merge conflict is a property of a tree that has never been built anywhere, and it appears at the moment of the second merge. make check, the diff-scoped gate, and the required CI jobs are all blind to it by construction, and adding more checks to any of them does not help.

The failure is also silent in the worst way: it does not show up on the PR that causes it. It shows up later, on whoever pushes next, which is how a docs change to three markdown files came to have a red test job.

Options

  1. Require branches to be up to date before merging (branch protection setting). GitHub then forces each PR to absorb main and re-run CI before it can land, so the second merge in a batch cannot happen until the combined tree has actually been built. Costs one CI round trip per PR in a batch, which is exactly the cost of knowing. This is an owner decision, not something an agent can set.
  2. A merge queue, which does the same thing without the manual rebase, at the price of more moving parts.
  3. A rule for whoever is merging: after each merge, rebase the next PR onto the new main and wait for green before merging it. Free, and only as reliable as the person or agent following it.

Option 1 is the one that cannot be forgotten.

Related

  • The four PRs: #481, #485, #486, #487
  • The repair: #483
  • The false alarm it produced: #488

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the failure in pkg/flowstate/v1/eval.go and the repair described in #483, then review the branch-protection option in this issue. The intended outcome is that a pull request must update from main and pass CI before merging, preventing back-to-back merges from producing an unbuilt tree; repository-owner access is required to apply this setting.

Written by the indexing model from the issue text.

Assessment

Tech stack
github, go
Domain
ci-cd, devops
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.