Commit Graph
3 Commits
Author SHA1 Message Date
sysadminandClaude Opus 4.8 b00e09a781 fix(mcp): bind post-merge moot-lease cleanup to the reconciler capability (Closes #745)
gitea_cleanup_post_merge_moot_lease (#515) posts a terminal `phase: released`
lease marker — a durable mutation of the PR lease ledger — but had no entry in
the canonical task-capability map and no role binding. Entry gated on
gitea.read, apply gated only on gitea.pr.comment, so any profile holding the
comment permission (author, reviewer, merger) reached the mutation path, while
the reconciler could not satisfy the operator-required resolve-exact-task ->
mutation sequence because no cleanup task was resolvable at all. The apply path
also called verify_preflight_purity(remote) with no task/org/repo, skipping the
resolved-task, canonical-root and explicit-target checks its siblings perform,
and passed request org/repo straight into _resolve.

Capability map and router:

- Map `cleanup_post_merge_moot_lease` and the tool-name alias
  `gitea_cleanup_post_merge_moot_lease` to gitea.pr.comment + reconciler.
  Both names carry an identical contract; unknown names keep failing closed on
  the map's KeyError.
- Add both to role_session_router RECONCILER_TASKS and TASK_REQUIRED_ROLE so
  the map and the router cannot disagree (the #723 defect-A class).

Tool enforcement (apply path only):

- Require the session to have resolved exactly the cleanup task; resolving a
  different task, including a sibling reconciler task, does not authorize it.
- Require the reconciler role, checked independently of the permission gate.
- Require gitea.pr.comment.
- Validate explicit org/repo against the canonical repository identity derived
  from the session binding, so request parameters can never redirect the
  mutation, and forward worktree_path/task/org/repo to verify_preflight_purity
  for canonical-root, workspace and anti-stomp binding (#733/#739).
- Require matching dry-run evidence proving lease_moot and cleanup_allowed for
  the same PR, lease session, candidate head and lease marker id, with optional
  caller expectations checked against the live lease.

New post_merge_moot_lease_gate module holds the pure authorization logic and an
append-only dry-run ledger: entries are only ever appended, lookup is
newest-wins, and dry_run_history hands out copies. Live, non-moot, superseded,
mismatched, malformed and foreign-repository leases all fail closed;
already-terminal cleanup stays idempotent.

The read-only apply=false assessment deliberately stays reachable under
gitea.read with no role gate, matching gitea_cleanup_stale_review_decision_lock
and gitea_cleanup_obsolete_reviewer_comment_lease, so an operator can diagnose a
stuck lease from any attached namespace. This choice is documented in the map,
the tool docstring and docs/gitea-execution-profiles.md, and is directly tested.

_delete_branch_repository_binding_block is generalized into
_repository_binding_block(required_permission=...) and retained as a thin
delete-path alias so #733/#739 coverage keeps exercising its permission label.

Tests: new tests/test_issue_745_moot_lease_reconciler_gate.py (39 tests, 35
subtests) covers the map/router contract, alias parity, unknown-name rejection,
role and task gates, dry-run/apply sequencing, superseded and malformed leases,
repository binding, ledger append-only behavior, idempotency, and asserts no
production PR/session/marker is referenced. tests/test_post_merge_moot_lease.py
is updated from the permission-only model to reconciler + dry-run evidence, with
a new negative test pinning that a merger can no longer apply.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-18 12:59:51 -04:00
sysadmin dc899d23c8 fix(session): require config-backed mutation authority and workspace-bound targets (#714)
Close the #714 remediation gap left at 943d402 where env-only mutation
profiles, REMOTES Timesheet defaults treated as explicit caller input, and
machine-dependent Git remotes produced false greens.

- Fail closed when mutations lack a config-backed profile with non-empty
  allowed_repositories; env ops cannot invent mutation authority.
- Preserve omitted-vs-explicit org/repo provenance through the mutation
  gate; omitted targets bind to the verified workspace repository.
- Prefer workspace-aligned Git remotes over historical Timesheet defaults
  in MCP _resolve and CLI resolve_remote.
- Centralize deterministic config-backed mutation fixtures; migrate
  mutation tests off env-only authority without weakening security
  assertions.

Full suite: 2744 passed, 6 skipped.
2026-07-15 21:13:21 -04:00
sysadminandClaude Opus 4.8 2faf18a802 feat: add safe post-merge moot lease cleanup for already-merged PRs (Closes #515)
Refuse merge-oriented reviewer-lease acquisition/adoption on an already
merged/closed PR, and add a read-first cleanup path for a lingering moot lease.

- reviewer_pr_lease.assess_acquire_lease: new pr_merged_or_closed flag; fail
  closed with a post_merge_moot reason and no lease body when the PR already
  merged/closed.
- reviewer_pr_lease.assess_post_merge_moot_lease: pure assessment. Only treats a
  lease as moot when the PR is merged/closed; never proposes touching an active
  lease on an open PR; provides a terminal phase:released (blocker:
  post-merge-moot) body to neutralise the moot lease append-only; idempotent
  once the released marker is the newest entry.
- gitea_acquire_reviewer_pr_lease: fetch live PR state, pass the flag, surface
  post_merge_moot on refusal (no comment posted).
- gitea_cleanup_post_merge_moot_lease: new read-first tool. Reports merged
  state, merge_commit_sha, linked-issue closure, lease-moot, cleanup
  performed/skipped, and no_merge_or_adoption. apply=True posts the released
  marker only when merged/closed with an active lease; refuses on open PRs.
- tests/test_post_merge_moot_lease.py: 10 tests covering acquire refusal on
  merged PR (no post), safe/idempotent cleanup, and open-PR foreign-lease
  protection.

No relaxation of #407 reviewer-lease or #16 gated-merge gates for open PRs.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-08 04:51:10 -04:00