Add explicit close_pr capability resolution and gated PR close path (Issue #216)
PR closure had no first-class capability: agents could close PRs through gitea_edit_pr(state=closed) with no close-specific capability proof, and gitea_resolve_task_capability(close_pr) failed as unknown, leaving the broad edit path as an untracked close fallback. Add close_pr to the resolver TASK_MAP (gitea.pr.close, author-side) and gate gitea_edit_pr(state=closed) on the same operation: without it the close fails closed before any auth or API call, with reasons and a structured permission_report; with it the close proceeds and is audited as a distinct close_pr action carrying the required capability. Reject invalid state values outright so case variants cannot bypass the gate. Generalize the profile gate helper (_profile_operation_gate) and document the PR comment / PR edit / PR close capability split. Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
@@ -203,6 +203,29 @@ remote/org/repo arguments. Create operations are audit-logged
|
||||
redacted, and normal output contains no endpoint URLs
|
||||
(`GITEA_MCP_REVEAL_ENDPOINTS=1` is the local admin opt-in for web links).
|
||||
|
||||
## PR edits versus PR closure (#216)
|
||||
|
||||
Editing a pull request and closing one are different capabilities:
|
||||
|
||||
- **PR edits** (`gitea_edit_pr` with `title`/`body`/`base`, or reopening with
|
||||
`state="open"`) stay on the ordinary edit path and need no dedicated
|
||||
capability.
|
||||
- **PR closure** (`gitea_edit_pr` with `state="closed"`) requires the
|
||||
distinct `gitea.pr.close` operation. The resolver task is `close_pr`
|
||||
(`gitea_resolve_task_capability(task="close_pr")`, author-side). Without
|
||||
`gitea.pr.close` the close attempt fails closed — no API call, structured
|
||||
`permission_report` — so the broad edit path can never be used as an
|
||||
untracked close fallback.
|
||||
- Closures are audited as a distinct `close_pr` action with
|
||||
`required_permission: gitea.pr.close` in the request metadata, so final
|
||||
reports can prove exactly which mutation capability was exercised (#191).
|
||||
|
||||
`gitea.pr.close` has no legacy alias; spell it canonically. It is not part of
|
||||
any default profile: the operator grants it deliberately (e.g. for an
|
||||
explicit operator-directed closure of a contaminated PR). If `close_pr` ever
|
||||
resolves as unknown, agents must fail closed rather than fall back to the
|
||||
edit path.
|
||||
|
||||
## Identity and fail-closed rules
|
||||
|
||||
Before **any** mutating action, a workflow must know both:
|
||||
|
||||
Reference in New Issue
Block a user