Fix different project same PR number collision problem

Fix (two layers):
1. Root cause — pr_merge and rebase_pr_for_task now take a required project_id and scope the lookup where(pr_number == X AND project_id == Y). Required so no caller can forget — the bug class can't recur. All 4 call sites updated (choreographer cell_pm_complete, the rebase-retry, the superseded close_pull_request now passes project_id, and _verb_runner._do_pr_merge).
2. Crash guard — _finalize_cell_complete None-checks the complete() return and returns a clean invalid_state envelope (with a remediate hint) instead of dereffing None → 500 → respawn loop.
This commit is contained in:
Renn F
2026-06-27 07:19:35 +02:00
parent 53d60da37e
commit 5b931c367f
7 changed files with 193 additions and 20 deletions
@@ -62,7 +62,9 @@ async def test_superseded_closes_pr_and_completes_without_merge(
return_value=MagicMock(status="completed", parent_task_id=None, team="frontend")
)
choreo = _choreo(task, git, monkeypatch)
t = MagicMock(pr_number=159, parent_task_id=None, team="frontend")
t = MagicMock(
pr_number=159, project_id=uuid4(), parent_task_id=None, team="frontend"
)
env = await choreo._resolve_merge_conflict_on_complete(
uuid4(), uuid4(), t, "feature/frontend/root--cell", "notes", _EXC
@@ -93,7 +95,9 @@ async def test_rebased_retries_merge_and_completes(
return_value=MagicMock(status="completed", parent_task_id=None, team="frontend")
)
choreo = _choreo(task, git, monkeypatch)
t = MagicMock(pr_number=160, parent_task_id=None, team="backend")
t = MagicMock(
pr_number=160, project_id=uuid4(), parent_task_id=None, team="backend"
)
await choreo._resolve_merge_conflict_on_complete(
uuid4(), uuid4(), t, "feature/backend/root--cell", "notes", _EXC
@@ -124,7 +128,9 @@ async def test_genuine_conflict_escalates_to_ceo_and_does_not_loop(
notify = AsyncMock()
monkeypatch.setattr(choreo, "_notify_ceo_merge_conflict", notify)
tid = uuid4()
t = MagicMock(pr_number=160, parent_task_id=None, team="backend")
t = MagicMock(
pr_number=160, project_id=uuid4(), parent_task_id=None, team="backend"
)
env = await choreo._resolve_merge_conflict_on_complete(
uuid4(), tid, t, "feature/backend/root--cell", "notes", _EXC
@@ -155,7 +161,9 @@ async def test_unknown_rebase_outcome_escalates_rather_than_completing(
task.admin_set_status = AsyncMock()
task.get = AsyncMock(return_value=MagicMock(status="awaiting_ceo_approval"))
choreo = _choreo(task, git, monkeypatch)
t = MagicMock(pr_number=160, parent_task_id=None, team="backend")
t = MagicMock(
pr_number=160, project_id=uuid4(), parent_task_id=None, team="backend"
)
await choreo._resolve_merge_conflict_on_complete(
uuid4(), uuid4(), t, "feature/backend/root--cell", "notes", _EXC