fix: exclude foreign-claimed work from allocation instead of blockading the queue (Closes #765)

Recovered preserved candidate d06198b onto current master 0c2f45a via clean
cherry-pick. Active foreign leases are excluded before allocator ranking;
controller_instance_id ownership is persisted; dashboard matches allocator
exclusion. Stable patch-id bacafc5f… preserved.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
2026-07-20 13:52:39 -05:00
co-authored by Claude Opus 4.8
parent 0c2f45abb7
commit ad13d872df
5 changed files with 735 additions and 22 deletions
+40 -4
View File
@@ -14,7 +14,7 @@ Design rules:
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any, Iterable, Sequence
from typing import Any, Iterable, Mapping, Sequence
from allocator_service import (
ROLE_AUTHOR,
@@ -22,7 +22,11 @@ from allocator_service import (
ROLE_MERGER,
ROLE_RECONCILER,
ROLE_REVIEWER,
OWNERSHIP_FOREIGN,
OWNERSHIP_UNKNOWN,
SKIP_CLAIMED_BY_OTHER_SESSION,
WorkCandidate,
classify_claim_ownership,
classify_skip,
expected_role_for_candidate,
sort_candidates,
@@ -265,9 +269,13 @@ def _entry_for_candidate(
c: WorkCandidate,
*,
terminal_pr: int | None,
claim_ownership: str | None = None,
) -> QueueEntry:
expected = expected_role_for_candidate(c)
badges = _badges_for_candidate(c, terminal_pr=terminal_pr)
claimed_by_other = claim_ownership in (OWNERSHIP_FOREIGN, OWNERSHIP_UNKNOWN)
if claimed_by_other and "claimed" not in badges:
badges = tuple(list(badges) + ["claimed"])
safe_roles: list[str] = []
block_reason: str | None = None
@@ -276,6 +284,12 @@ def _entry_for_candidate(
block_reason = "status blocked"
elif c.dependency_unmet:
block_reason = c.dependency_reason or "unmet dependency"
elif claimed_by_other:
# #765: never advertise another controller's active task as safe work.
block_reason = (
f"{SKIP_CLAIMED_BY_OTHER_SESSION}: active lease held by another "
"controller"
)
elif c.already_claimed_elsewhere:
block_reason = "already claimed elsewhere"
elif c.kind == "pr" and not (c.head_sha or "").strip():
@@ -291,7 +305,12 @@ def _entry_for_candidate(
if block_reason is None:
# Safe only for the expected role, and only when classify_skip agrees.
skip = classify_skip(c, role=expected, terminal_pr=terminal_pr)
skip = classify_skip(
c,
role=expected,
terminal_pr=terminal_pr,
claim_ownership=claim_ownership,
)
if skip is None:
safe_roles.append(expected)
else:
@@ -450,8 +469,16 @@ def build_workflow_dashboard(
terminal_lock: dict[str, Any] | None = None,
inventory_complete: bool = True,
inventory_reasons: Sequence[str] | None = None,
claims: Mapping[tuple[str, int], dict[str, Any]] | None = None,
session_id: str | None = None,
controller_instance_id: str | None = None,
) -> DashboardSnapshot:
"""Build a full dashboard snapshot from injected inventory (pure)."""
"""Build a full dashboard snapshot from injected inventory (pure).
*claims* (#765) maps ``(kind, number)`` to the live lease holding that work
item. Items claimed by a different controller are never presented as safe
next work for this one.
"""
reasons = [str(r) for r in (inventory_reasons or ()) if str(r).strip()]
if not inventory_complete:
reasons.append(
@@ -461,7 +488,16 @@ def build_workflow_dashboard(
ranked = sort_candidates(list(candidates))
entries = [
_entry_for_candidate(c, terminal_pr=terminal_pr) for c in ranked
_entry_for_candidate(
c,
terminal_pr=terminal_pr,
claim_ownership=classify_claim_ownership(
(claims or {}).get((c.kind, int(c.number))),
session_id=session_id,
controller_instance_id=controller_instance_id,
),
)
for c in ranked
]
open_prs = [e for e in entries if e.kind == "pr"]