Merge lane: stop BEHIND PRs stalling

Task 5545 · Done · Coder · created · updated

Defect (found by the epic 5514 finish check; deferred by Task 5524's own report, never filed). Some repos protect their integration branch with "require branches to be up to date" (strict required status checks). On a lane-active project like that, the lane merges PR 1 of a batch, and GitHub then reports PR 2+ as `mergeStateStatus: BEHIND`. What happens next: 1. `jaah-forge pr merge --sha` is refused mid-batch, so `tools/merge_lane.py` requeues the rest (merge_tested, around :437-447). 2. Every later run sorts the row `not_ready`, because `apps/jaahd/forge_cli/adapters/github.py:57-64` folds BEHIND into `blocked`, and `tools/merge_lane.py:288-290` requires `merge_state == "clean"`. The row comes back after `RECHECK_SECONDS` (90 s, `apps/api/app/merge_lane.py:71`), every time, forever. 3. Nobody tells the runner: `not_ready` delivers nothing to its pane, and the lane prompt keeps it polling `jaah-forge pr view`. The PR never merges, and the runner never settles. jaah's own develop is not affected. `infra/github/rulesets/develop.json:39` is non-strict, and the live `GET repos/jaah-ai/jaah/rules/branches/develop` shows no required-status-checks rule. A tenant repo with strict protection is affected, and nothing in `merge_lane.lane_state` eligibility or in docs/policies/fleet-task-lifecycle.md §Merge lane mentions this. Reproduction (observed by reading the code): trace adapters/github.py `merge_state("BEHIND", None, "success")`. It returns `"blocked"`, which `merge_lane.py:288` turns into `not_ready`, which `apply_event` turns back into `queued` with `recheck_after`. Nothing in that path ends. Fix (smallest; no new forge write verb): 1. In `apps/jaahd/forge_cli/adapters/github.py` `merge_state`, return a distinct `"behind"` when status is BEHIND and the checks are not failing or pending. `policy.check_mergeable` keeps treating it as not mergeable. GitLab and Azure are unchanged, unless they expose the same state cheaply. 2. In `tools/merge_lane.py`, when a row's view says `merge_state == "behind"`, whether found while sorting or after a mid-batch merge refusal, report the row `failed` with `kind: "behind"` instead of `not_ready`/`requeue`. The failure text names the fix: "the integration branch moved and this repo requires PR branches to be up to date: merge `<integration_branch>` into your branch, push, and report again with task_pr_queue". This reuses the existing failed path: the PR comment, and the CP's `deliver_message` to the runner's pane. Add `behind` to the CP's accepted failure kinds and to the console's `failureLabel` (copy: "Branch behind"). 3. Add one line to docs/policies/fleet-task-lifecycle.md §Merge lane, at step 2 or the row-state rules: strict up-to-date protection costs a rebase round per PR after the first in a batch. Out of scope: do NOT add a `pr update-branch` forge verb or have the lane push to runner branches; do NOT change `check_mergeable` for other states, the batch cap, eligibility, the rulesets, CI, or the runner prompt; no Bitbucket work; no change when the lane is OFF. Done means: - Unit (forge_cli): `merge_state("BEHIND", None, "success") == "behind"`; BEHIND with failing checks is still `checks_failed`, and with pending checks still `pending`; CLEAN, BLOCKED and DIRTY are unchanged. - Lane unit, with the fake forge: a 2-PR green batch where the forge refuses PR 2's merge and then views it as `behind`. Expect PR 1 `merged` and certified, PR 2 `failed` with kind `behind` plus one `pr comment`, and no `requeue` or `not_ready` event for PR 2. - Lane unit: a row already `behind` at sort time gives `failed`/`behind` on the first run, not `not_ready`. - CP test: a `failed` event with kind `behind` is accepted and mints a `deliver_message` intent when the runner is live. - Web: the `failureLabel` test renders "Branch behind". - `./dc check full` green. Required checks: gate, review-lane

Outcome

A BEHIND PR now fails as kind `behind`, which comments on the PR and tells the runner in its pane to merge the base branch and push, so it no longer stalls. Full gate green, PR #4844 merged, Jaah Deploy ok. Deferred: the runner prompt doesn't name `behind` yet.

Pull request

Artifacts

Activity

  • Ready
  • Runner started
  • Runner ended (done)