feat: diagnose reviewer lease handoff next actions (#599)

Add read-only diagnose_reviewer_pr_lease_handoff and MCP tool
gitea_diagnose_reviewer_pr_lease_handoff so open-PR foreign leases,
instructed-lease replacement, reclaimable release, and worktree
binding mismatch return a canonical next_action without steal paths.

Closes #599
This commit is contained in:
2026-07-09 17:31:14 -04:00
parent 683649424b
commit 964505654f
3 changed files with 402 additions and 1 deletions
+56
View File
@@ -7154,6 +7154,62 @@ def gitea_assess_reviewer_pr_lease(
}
@mcp.tool()
def gitea_diagnose_reviewer_pr_lease_handoff(
pr_number: int,
proposed_worktree: str | None = None,
instructed_session_id: str | None = None,
instructed_comment_id: int | None = None,
remote: str = "dadeschools",
host: str | None = None,
org: str | None = None,
repo: str | None = None,
) -> dict:
"""Read-only: diagnose open-PR reviewer lease handoff and emit next action (#599).
Classifies no-lease / own / foreign / instructed-lease-missing-with-replacement
and worktree binding mismatch. Returns a canonical ``next_action`` without
mutating Gitea state. Never steals foreign leases.
"""
read_block = _profile_operation_gate("gitea.read")
if read_block:
return {
"success": False,
"reasons": read_block,
"permission_report": _permission_block_report("gitea.read"),
}
comments = _fetch_pr_comments(
pr_number, remote=remote, host=host, org=org, repo=repo)
h, _o, _r = _resolve(remote, host, org, repo)
try:
username = _authenticated_username(h)
except Exception:
username = None
session_lease = reviewer_pr_lease.get_session_lease() or {}
env_wt = (
(os.environ.get("GITEA_REVIEWER_WORKTREE") or "").strip()
or (os.environ.get("GITEA_ACTIVE_WORKTREE") or "").strip()
or None
)
# Prefer in-session lease id when present; otherwise diagnose as a fresh
# caller without inventing ownership of an on-thread lease.
session_id = (session_lease.get("session_id") or "").strip() or None
diagnosis = reviewer_pr_lease.diagnose_reviewer_pr_lease_handoff(
comments,
pr_number=pr_number,
current_session_id=session_id,
current_reviewer_identity=username,
proposed_worktree=proposed_worktree,
env_bound_worktree=env_wt,
instructed_session_id=instructed_session_id,
instructed_comment_id=instructed_comment_id,
)
diagnosis["success"] = True
diagnosis["remote"] = remote
diagnosis["authenticated_user"] = username
return diagnosis
@mcp.tool()
def gitea_cleanup_post_merge_moot_lease(
pr_number: int,