Harden gitea_acquire_merger_pr_lease with required candidate_head, live-head match, and fail-closed POST handling. Add capability-map aliases for reviewer and merger lease acquire/adopt. Make unknown tasks return structured unknown_task (no ValueError escape). Expand tests for roles, head scoping, aliases, and unknown_task. Closes #718. Addresses #723 resolver unknown_task and lease task mapping.
This commit is contained in:
+98
-6
@@ -10004,8 +10004,21 @@ def gitea_acquire_merger_pr_lease(
|
||||
"permission_report": _permission_block_report("gitea.pr.merge"),
|
||||
}
|
||||
|
||||
head_pin = (candidate_head or "").strip()
|
||||
if not head_pin:
|
||||
return {
|
||||
"success": False,
|
||||
"acquired": False,
|
||||
"reasons": [
|
||||
"candidate_head is required for merger lease acquisition "
|
||||
"(exact-head scoping; fail closed)"
|
||||
],
|
||||
}
|
||||
|
||||
try:
|
||||
_verify_role_mutation_workspace(remote, worktree=worktree, task="acquire_merger_pr_lease")
|
||||
_verify_role_mutation_workspace(
|
||||
remote, worktree=worktree, task="acquire_merger_pr_lease"
|
||||
)
|
||||
except RuntimeError as e:
|
||||
return {
|
||||
"success": False,
|
||||
@@ -10022,13 +10035,31 @@ def gitea_acquire_merger_pr_lease(
|
||||
|
||||
comments = _fetch_pr_comments(
|
||||
pr_number, remote=remote, host=host, org=org, repo=repo)
|
||||
|
||||
|
||||
pr_live = api_request(
|
||||
"GET", f"{repo_api_url(h, o, r)}/pulls/{pr_number}", auth) or {}
|
||||
live_head = (
|
||||
(pr_live.get("head") or {}).get("sha")
|
||||
or pr_live.get("head_sha")
|
||||
or ""
|
||||
)
|
||||
live_head = str(live_head).strip()
|
||||
if live_head and live_head != head_pin:
|
||||
return {
|
||||
"success": False,
|
||||
"acquired": False,
|
||||
"reasons": [
|
||||
f"candidate_head {head_pin[:12]}… does not match live PR head "
|
||||
f"{live_head[:12]}… (exact-head scoping; fail closed)"
|
||||
],
|
||||
"live_head": live_head,
|
||||
"candidate_head": head_pin,
|
||||
}
|
||||
|
||||
pr_merged_or_closed = bool(
|
||||
pr_live.get("merged") or pr_live.get("merged_at")
|
||||
) or (str(pr_live.get("state") or "").strip().lower() == "closed")
|
||||
|
||||
|
||||
assessment = reviewer_pr_lease.assess_acquire_lease(
|
||||
comments,
|
||||
pr_number=pr_number,
|
||||
@@ -10038,7 +10069,7 @@ def gitea_acquire_merger_pr_lease(
|
||||
repo=repo_label,
|
||||
issue_number=issue_number,
|
||||
worktree=worktree,
|
||||
candidate_head=candidate_head,
|
||||
candidate_head=head_pin,
|
||||
target_branch=target_branch,
|
||||
target_branch_sha=target_branch_sha,
|
||||
pr_merged_or_closed=pr_merged_or_closed,
|
||||
@@ -10065,6 +10096,16 @@ def gitea_acquire_merger_pr_lease(
|
||||
):
|
||||
posted = api_request("POST", comment_url, auth, {"body": body})
|
||||
|
||||
if not isinstance(posted, dict) or not posted.get("id"):
|
||||
return {
|
||||
"success": False,
|
||||
"acquired": False,
|
||||
"reasons": [
|
||||
"lease comment POST failed or returned no comment id "
|
||||
"(no partial session lease recorded)"
|
||||
],
|
||||
}
|
||||
|
||||
session_lease = reviewer_pr_lease.record_session_lease({
|
||||
"pr_number": pr_number,
|
||||
"issue_number": issue_number,
|
||||
@@ -10073,7 +10114,7 @@ def gitea_acquire_merger_pr_lease(
|
||||
"profile": profile.get("profile_name"),
|
||||
"worktree": worktree,
|
||||
"phase": "claimed",
|
||||
"candidate_head": candidate_head,
|
||||
"candidate_head": head_pin,
|
||||
"target_branch": target_branch,
|
||||
"target_branch_sha": target_branch_sha,
|
||||
"repo": repo_label,
|
||||
@@ -10089,6 +10130,8 @@ def gitea_acquire_merger_pr_lease(
|
||||
"session_id": sid,
|
||||
"comment_id": posted.get("id"),
|
||||
"session_lease": session_lease,
|
||||
"candidate_head": head_pin,
|
||||
"repo": repo_label,
|
||||
"reasons": [],
|
||||
}
|
||||
|
||||
@@ -13951,11 +13994,56 @@ def gitea_resolve_task_capability(
|
||||
TASK_MAP = task_capability_map.TASK_CAPABILITY_MAP
|
||||
|
||||
if task not in TASK_MAP:
|
||||
raise ValueError(f"Unknown task/action: '{task}' (fail closed)")
|
||||
# #723: structured fail-closed unknown_task (never raise into internal_error).
|
||||
profile = get_profile()
|
||||
h = host or (REMOTES.get(remote, {}).get("host") if remote in REMOTES else None)
|
||||
username = _authenticated_username(h) if h else None
|
||||
active_role_kind = _profile_role_kind(profile)
|
||||
switching = gitea_config.is_runtime_switching_enabled()
|
||||
result = {
|
||||
"requested_task": task,
|
||||
"required_operation_permission": "unknown",
|
||||
"required_role_kind": "unknown",
|
||||
"active_profile": profile.get("profile_name", "unknown"),
|
||||
"active_identity": username,
|
||||
"active_role_kind": active_role_kind,
|
||||
"active_profile_allowed_operations": profile.get("allowed_operations") or [],
|
||||
"active_profile_permission_allowed": False,
|
||||
"allowed_in_current_session": False,
|
||||
"available_in_session": False,
|
||||
"configured": False,
|
||||
"restart_required": False,
|
||||
"stop_required": True,
|
||||
"task_role_guidance": [
|
||||
f"STOP: Unknown task/action: '{task}' (fail closed)"
|
||||
],
|
||||
"matching_configured_profile": [],
|
||||
"runtime_switching_supported": switching,
|
||||
"different_mcp_namespace_required": False,
|
||||
"exact_safe_next_action": (
|
||||
f"None; task '{task}' is unrecognized by the capability map."
|
||||
),
|
||||
"reason": f"Unknown task/action: '{task}' (fail closed)",
|
||||
"reason_code": "unknown_task",
|
||||
"mutation_performed": False,
|
||||
}
|
||||
role_session_router.sync_route_from_capability(result)
|
||||
was_terminal = capability_stop_terminal.is_active()
|
||||
terminal = capability_stop_terminal.sync_from_capability_result(result)
|
||||
if terminal:
|
||||
result["terminal_mode"] = True
|
||||
result["terminal_report"] = (
|
||||
capability_stop_terminal.build_terminal_report(result)
|
||||
)
|
||||
elif was_terminal:
|
||||
result["cleared_stale_denial"] = True
|
||||
return result
|
||||
|
||||
required_permission = task_capability_map.required_permission(task)
|
||||
required_role = task_capability_map.required_role(task)
|
||||
role_exclusive_tasks = {
|
||||
"acquire_reviewer_pr_lease",
|
||||
"gitea_acquire_reviewer_pr_lease",
|
||||
"review_pr",
|
||||
"approve_pr",
|
||||
"request_changes_pr",
|
||||
@@ -13963,6 +14051,10 @@ def gitea_resolve_task_capability(
|
||||
"pr_queue_cleanup",
|
||||
"pr-queue-cleanup",
|
||||
"merge_pr",
|
||||
"acquire_merger_pr_lease",
|
||||
"gitea_acquire_merger_pr_lease",
|
||||
"adopt_merger_pr_lease",
|
||||
"gitea_adopt_merger_pr_lease",
|
||||
"create_branch",
|
||||
"push_branch",
|
||||
"create_pr",
|
||||
|
||||
Reference in New Issue
Block a user