Allow dead-session lock recovery when the issue already has its owning open PR #755

Closed
opened 2026-07-18 20:31:36 -05:00 by jcwalker3 · 0 comments
Owner

Problem

PR #754 / issue #753 added a server-sanctioned dead-session author-lock recovery assessment. The live assessor correctly returns RECOVERY_SANCTIONED for issue #749 and PR #750, but the production gitea_lock_issue path rejects the request before it can persist the recovered lock.

This leaves PR #750 blocked from its required update_branch_by_merge operation even though every recovery ownership proof agrees.

Live reproduction

Repository: Scaled-Tech-Consulting/Gitea-Tools

Affected state:

Despite that proof, gitea_lock_issue fails with:

open PR #750 already covers issue #749 (fail closed)

The sanctioned gitea_update_pr_branch_by_merge path then fails because no live recovered author lock was persisted.

Root cause

Blocker 1: owning PR is treated as duplicate work

gitea_lock_issue calculates the dead-session recovery decision before invoking the duplicate-work gate.

The duplicate gate then blocks unconditionally when any linked open PR exists. It does not distinguish:

  • the exact PR already owned by the recovering issue/branch/worktree; from
  • a competing PR representing duplicate work.

The computed recovery_sanctioned result is therefore discarded.

Relevant production areas include:

  • gitea_mcp_server.py — recovery assessment followed by _assess_issue_duplicate_gate
  • issue_work_duplicate_gate.py — unconditional linked-open-PR blocker
  • issue_lock_recovery.py — successful server-side recovery assessment

Verify current line numbers during implementation rather than treating reported locations as immutable.

Blocker 2: downstream ownership remains unproven

gitea_update_pr_branch_by_merge requires a live author lock. Because the duplicate gate prevents recovery from being persisted, the durable dead-session lock remains non-live and ownership proof fails.

The primary fix should make the sanctioned recovery persist a renewed live lock. Change downstream ownership logic only if still necessary after that end-to-end path works.

Required behavior

A linked open PR must not be considered duplicate work when it is conclusively the exact PR already owned by the recovering lock.

The exception must apply only when server-derived evidence proves all of the following:

  • recovery outcome is RECOVERY_SANCTIONED;
  • issue number matches;
  • locked branch matches the linked PR head branch;
  • registered worktree matches;
  • worktree is clean;
  • local head equals remote branch head;
  • PR head equals that same head;
  • author identity/profile matches;
  • prior PID is dead;
  • no live competing lock or lease exists;
  • no competing branch or additional linked PR exists.

Any mismatch must retain the existing fail-closed duplicate-work behavior.

Acceptance criteria

  1. Thread the server-derived recovery decision and owning-PR evidence into the duplicate-work assessment.

  2. Exempt only the exact self-owned PR from the linked-open-PR duplicate blocker.

  3. Do not exempt:

    • a different PR;
    • multiple linked open PRs;
    • a different branch;
    • a different head;
    • a different worktree;
    • a foreign author/profile;
    • dirty or divergent work;
    • a live prior PID;
    • a competing live lock or lease.
  4. Preserve duplicate blocking for every normal new-issue and competing-work path.

  5. Persist the recovered lock with a renewed live lease and truthful recovery provenance.

  6. Prove the resulting live lock satisfies gitea_update_pr_branch_by_merge.

  7. Add end-to-end MCP regression coverage that invokes gitea_lock_issue with:

    • a linked owning open PR and sanctioned recovery → succeeds;
    • a competing linked PR → fails;
    • multiple linked PRs → fails;
    • matching PR but mismatched branch/head/worktree → fails;
    • live prior PID → fails;
    • dead PID plus exact ownership proof → recovered lock permits update-by-merge.
  8. Do not rely only on unit tests of assess_dead_session_lock_recovery.

  9. Do not add caller-controlled recovery or duplicate-gate bypass inputs.

  10. Preserve all existing new-claim base-equivalence and worktree-cleanliness protections.

Relationship to existing work

  • Follow-up to #753 / PR #754.
  • Blocks recovery of #749 / PR #750.
  • Not a duplicate of #753: #753's assessor correctly sanctions recovery; this issue covers the production duplicate gate that prevents that sanctioned decision from being applied end to end.
## Problem PR #754 / issue #753 added a server-sanctioned dead-session author-lock recovery assessment. The live assessor correctly returns `RECOVERY_SANCTIONED` for issue #749 and PR #750, but the production `gitea_lock_issue` path rejects the request before it can persist the recovered lock. This leaves PR #750 blocked from its required `update_branch_by_merge` operation even though every recovery ownership proof agrees. ## Live reproduction Repository: `Scaled-Tech-Consulting/Gitea-Tools` Affected state: * Issue: #749 * Owning PR: #750 * PR branch: `fix/issue-749-create-issue-bootstrap` * PR head: `e349839fd739e10af2df9c0f429ac487e8ca8b9d` * Registered worktree: `branches/issue-749-create-issue-bootstrap` * Worktree: clean * Local head = remote head = PR head * Recorded session PID: dead * No live competing author lock or lease * No competing branch * Live recovery assessment: `RECOVERY_SANCTIONED` Despite that proof, `gitea_lock_issue` fails with: `open PR #750 already covers issue #749 (fail closed)` The sanctioned `gitea_update_pr_branch_by_merge` path then fails because no live recovered author lock was persisted. ## Root cause ### Blocker 1: owning PR is treated as duplicate work `gitea_lock_issue` calculates the dead-session recovery decision before invoking the duplicate-work gate. The duplicate gate then blocks unconditionally when any linked open PR exists. It does not distinguish: * the exact PR already owned by the recovering issue/branch/worktree; from * a competing PR representing duplicate work. The computed `recovery_sanctioned` result is therefore discarded. Relevant production areas include: * `gitea_mcp_server.py` — recovery assessment followed by `_assess_issue_duplicate_gate` * `issue_work_duplicate_gate.py` — unconditional linked-open-PR blocker * `issue_lock_recovery.py` — successful server-side recovery assessment Verify current line numbers during implementation rather than treating reported locations as immutable. ### Blocker 2: downstream ownership remains unproven `gitea_update_pr_branch_by_merge` requires a live author lock. Because the duplicate gate prevents recovery from being persisted, the durable dead-session lock remains non-live and ownership proof fails. The primary fix should make the sanctioned recovery persist a renewed live lock. Change downstream ownership logic only if still necessary after that end-to-end path works. ## Required behavior A linked open PR must not be considered duplicate work when it is conclusively the exact PR already owned by the recovering lock. The exception must apply only when server-derived evidence proves all of the following: * recovery outcome is `RECOVERY_SANCTIONED`; * issue number matches; * locked branch matches the linked PR head branch; * registered worktree matches; * worktree is clean; * local head equals remote branch head; * PR head equals that same head; * author identity/profile matches; * prior PID is dead; * no live competing lock or lease exists; * no competing branch or additional linked PR exists. Any mismatch must retain the existing fail-closed duplicate-work behavior. ## Acceptance criteria 1. Thread the server-derived recovery decision and owning-PR evidence into the duplicate-work assessment. 2. Exempt only the exact self-owned PR from the linked-open-PR duplicate blocker. 3. Do not exempt: * a different PR; * multiple linked open PRs; * a different branch; * a different head; * a different worktree; * a foreign author/profile; * dirty or divergent work; * a live prior PID; * a competing live lock or lease. 4. Preserve duplicate blocking for every normal new-issue and competing-work path. 5. Persist the recovered lock with a renewed live lease and truthful recovery provenance. 6. Prove the resulting live lock satisfies `gitea_update_pr_branch_by_merge`. 7. Add end-to-end MCP regression coverage that invokes `gitea_lock_issue` with: * a linked owning open PR and sanctioned recovery → succeeds; * a competing linked PR → fails; * multiple linked PRs → fails; * matching PR but mismatched branch/head/worktree → fails; * live prior PID → fails; * dead PID plus exact ownership proof → recovered lock permits update-by-merge. 8. Do not rely only on unit tests of `assess_dead_session_lock_recovery`. 9. Do not add caller-controlled recovery or duplicate-gate bypass inputs. 10. Preserve all existing new-claim base-equivalence and worktree-cleanliness protections. ## Relationship to existing work * Follow-up to #753 / PR #754. * Blocks recovery of #749 / PR #750. * Not a duplicate of #753: #753's assessor correctly sanctions recovery; this issue covers the production duplicate gate that prevents that sanctioned decision from being applied end to end.
jcwalker3 added status:pr-open and removed status:ready labels 2026-07-18 20:51:24 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Scaled-Tech-Consulting/Gitea-Tools#755