Skip to content

feat(gate): force a fresh rebase + CI recheck immediately before merge when base has advanced #2552

Description

@JSONbored

Parent: #1936

Problem

The update_branch action (which merges the current base into head and would naturally re-trigger check-migrations.mjs/CI on a truly-rebased branch) only fires when liveMergeState === 'behind' inside the review-gating pass (maybeSkipForRebaseOrCi, src/queue/processors.ts) — not immediately before the merge action itself in runAgentMaintenancePlanAndExecute. The merge decision's own gate (mergeableClean = input.pr.mergeableState === 'clean', src/settings/agent-actions.ts) treats GitHub's mergeable_state as the sole base-freshness signal, and mergeable_state only detects git-level TEXTUAL conflicts — a base that has advanced with a new, non-conflicting sibling commit (e.g. a second PR's distinct-but-colliding migration file) still reads clean.

So a PR sitting green + approved + clean gets merged without ever being forced through a fresh rebase-and-CI-recheck cycle against whatever landed on main moments earlier from a concurrent merge. mergePullRequest pins the merge to the PR's own head SHA (protects against the PR's OWN branch moving) but has no equivalent base-freshness parameter — GitHub's merge API offers none, so this has to be enforced by the gate itself.

Requirements

  • Add a config-driven gate.requireFreshRebaseWindow (minutes, off by default) setting: if main has advanced within that window of the actual merge-decision moment, force an update_branch + fresh CI-recheck cycle before merging, rather than trusting a mergeable_state: clean read from before the base moved.
  • Must not create a live-lock scenario where a high-velocity main (frequent merges) causes a PR to never clear the freshness window — cap retries or fall back to a normal merge after N attempts with a clear audit trail.
  • Reuse the existing update_branch autonomy class and its existing write-permission/dry-run/kill-switch gate stack — this is not a new action class, just a new trigger condition for the existing one.

Deliverables

  • The gate.requireFreshRebaseWindow setting wired through the full config-as-code chain (migration, schema, type, DB round-trip, .gittensory.yml, OpenAPI, docs).
  • A base-freshness check added to runAgentMaintenancePlanAndExecute immediately before the merge action executes, using the same live-facts pattern as the existing refreshLiveMergeState.
  • Tests: a PR merged normally when main hasn't moved recently; a PR forced through update_branch when main advanced within the configured window; a bounded-retry test proving no infinite loop under a fast-moving main.

Acceptance criteria

  • With requireFreshRebaseWindow configured, a PR whose base advanced within that window in the moments before merge is rebased and CI-rechecked before the actual merge call, not merged on stale mergeable_state.
  • Off by default — zero behavior change for any repo that hasn't opted in.
  • No infinite-rebase-loop failure mode under a fast-moving main.

Expected outcome

A PR can no longer merge on a mergeable_state: clean read that's technically true but stale relative to a sibling commit that landed on main in the intervening seconds — closing the base-side half of the same race that already has head-SHA pinning on the PR's own side.

Metadata

Metadata

Assignees

Labels

maintainer-onlyOwner-only work — yields no Gittensor points.roadmapOn the Wave-2 agent-layer roadmap board (project 9)

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions