Skip to content

premerge: Phase 0 packs without checking the base is green, so a red main becomes the stack's problem #786

Open

Description

@TheFJK

The gap

Phase 0 discovers every open non-draft PR and packs them onto a combine branch. It never asks whether the base is green first.

If main is red, the stack inherits that red at commit zero. The gate then reports ci=red on work that did not cause it, the CI-repair arm classifies a failure belonging to a different change, and the run either parks red or gets quietly widened to fix someone else's bug.

Hit live on stack PR #768

main's own tip was already failing:

aa3e54cd  completed/failure  Tests      <- main's tip, the v0.55.1 landing

tests/vendor-provenance.test.sh V46 — six measured_diff_basis.uberdev_rev records pinned b7dbe74a…, the tip of the unmerged branch fix/746-agent-description-budget, whose work had been re-cut by the #752 stack landing. Verified rather than assumed:

  • git merge-base --is-ancestor b7dbe74a origin/main → rc=1 (unreachable from main)
  • the fixture fails identically on a clean origin/main worktree — 62 passed / 1 failed
  • git diff --name-only origin/main..HEAD | grep -c vendor.json → 0 (the stack touches no vendor register)

So the stack's only CI failure was 100% inherited. Attempt 1 spent its whole ci=red repair arm on it, the ci-failure-classifier correctly refused to route it (a ci-code-fixer dispatched against PR 768 would be editing a file the PR does not touch, to repair a pre-existing main red), and the run had to stop and ask the operator whether to widen scope past the five packed PRs.

The fix that unblocked it — re-pinning six register values to the reachable commit carrying the identical bytes — was main's work, carried in someone else's stack because there was nowhere else to put it.

Why this bites harder than it looks

  • Every /premerge run against a red base parks red, so the command silently becomes unusable until someone notices the base is the cause.
  • The repair budget is spent on the wrong change: one of three rounds here.
  • The blame is misattributed — the stack PR's checks go red, so the five packed PRs look like the cause.
  • The classifier's AMBIGUOUS route is stop the loop, which is correct but leaves the operator with no next step other than reading the log carefully enough to notice the red predates the stack.

Proposed change

Probe the resolved base in Phase 0a, right after review_consolidate_base resolves it and before 0b builds the branch:

  • Base green → proceed silently.
  • Base red → refuse by default, naming the failing check and the base SHA. A stack built on a red base cannot reach a green gate on its own merits, so packing is wasted work.
  • Base has no checks / unknown → proceed, but say so in the run summary, the same way the gate already distinguishes no_checks from unknown.

Add a --allow-red-base escape for the deliberate case (this run would have used it, with the operator's approval already on record). When used, put it in the stack PR body and the run summary, so the inherited red is attributed to the base rather than to the packed PRs.

The probe is the same gh pr checks / statusCheckRollup machinery Phase 3 already uses against the stack PR — pointed at the base branch instead.

Related

Activity

  1. added
    premerge-findingAuto-filed by /uberdev:premerge from deferred findings (RFC 0002)
    on Aug 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    premerge-findingAuto-filed by /uberdev:premerge from deferred findings (RFC 0002)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions