Closes#442.
gitea_lock_issue treated ANY remote branch containing issue-<n> as
duplicate competing work (#400) and failed closed — including the issue's
own already-pushed branch. If the in-memory lock was lost (e.g. MCP server
restart) after the branch was pushed but before the PR, there was no
non-destructive way to reacquire the lock, deadlocking PR creation. This
surfaced during recovery of #420 (feat/issue-420-server-code-parity).
Adds a safe own-branch adoption path.
Changes:
- issue_lock_adoption.py — assess_own_branch_adoption() distinguishes the
issue's exact requested branch (adopt) from a different same-issue branch
(competing, still fail-closed); ambiguous (own + other) fails closed.
build_adoption_proof() emits the required proof block.
- gitea_mcp_server.py — gitea_lock_issue consults the adoption assessment in
place of the blanket branch-match block; on adoption the result carries an
`adoption` proof (issue, branch, head commit, reason, no-existing-PR proof,
no-competing-live-lock proof, lock file path/status). Open-PR and active-
lease checks already run first, so adoption implies neither exists. Adds
_branch_entry_commit_sha helper.
- Tests: tests/test_issue_lock_adoption.py (8 cases) + 3 MCP-level cases in
test_mcp_server.py (adopt own branch, open PR blocks adoption, create_pr
proceeds after adopted lock). Existing competing-branch block preserved
(still raises "already has matching branch").
Safety:
- Different same-issue branch, competing branch, existing open PR, ambiguous
ownership all remain fail-closed. #400 duplicate-work protection intact.
- #420's feature branch was only read (git ls-remote); not modified/deleted.
Validation:
venv/bin/python -m pytest tests/ -q -> 1611 passed, 6 skipped
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>