fix(mcp): let a sanctioned recovery keep its own owning PR (Closes #755) #756

Merged
sysadmin merged 1 commits from fix/issue-755-owning-pr-recovery into master 2026-07-18 21:08:35 -05:00
Owner

Closes #755.

Base: 08f67007c5984d239e1f52684e9e47543ff41785
Head: f858c1d1b20f403a80b57fe524a774687c38bc8b

Problem

#753 shipped the dead-session author-lock recovery assessor, but no sanctioned recovery could reach the lock write.

A dead-session lock is, by construction, a lock for work that already has an open PR. The #400 duplicate-work gate blocked unconditionally on any linked open PR, so gitea_lock_issue computed recovery_sanctioned and then discarded it one gate later:

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

Because the recovered lock was never persisted, it stayed non-live, so _prove_author_ownership_for_pr found no live author lock and gitea_update_pr_branch_by_merge also failed closed. Both blockers had one cause, so only the first is fixed here.

Live reproduction at the time of filing: issue #749, owning PR #750, branch fix/issue-749-create-issue-bootstrap, head e349839f, clean registered worktree, local == remote == PR head, dead recorded pid, no competing lock/lease/branch, assessor returning RECOVERY_SANCTIONED.

Design

issue_lock_recovery.owning_pr_recovery_evidence distils a granted assessment into the minimum evidence the duplicate gate needs: issue, branch, owning PR number, head.

It returns None unless the outcome is RECOVERY_SANCTIONED and the assessment's own evidence agrees that local head == remote head == PR head. Every field is copied from evidence the assessor built out of durable lock state plus live git/Gitea observation, so a partial or hand-built assessment cannot authorize anything. There is no new caller-facing input anywhere — no tool signature changed.

The duplicate gate re-checks that evidence against the live PR list it was handed, independently:

  • exactly one linked open PR;
  • its number matches the recovered owning PR;
  • its head branch matches the recovered branch;
  • its head SHA matches the recovered head;
  • the branch being locked matches the recovered branch;
  • the evidence is for this issue.

Only that exact self-owned PR is exempt. Any mismatch keeps the pre-existing fail-closed behavior and adds a diagnostic naming which element disagreed.

Untouched: the competing-branch arm, the claim-lease arms, and the PHASE_COMMIT/PHASE_PUSH/PHASE_CREATE_PR rechecks. With no evidence supplied, the gate behaves exactly as before — the default is None, so every existing caller is unchanged.

Ownership proof needed no change (AC7): once the recovered lock persists under the live session pid, _prove_author_ownership_for_pr matches it by its existing durable-lock path. AC5's renewed live lease and dead_session_recovery provenance are written by the existing #753 code, which this change simply lets execute.

Out of scope

The PHASE_COMMIT / PHASE_PUSH / PHASE_CREATE_PR duplicate rechecks still block on a linked open PR for every author, recovered or not. That is pre-existing #400 behavior outside lock recovery, and #755 does not cover it. Flagging it rather than widening this fix.

Files changed

File Change
issue_lock_recovery.py New owning_pr_recovery_evidence pure derivation
issue_work_duplicate_gate.py _pr_links_issue / _all_linked_open_prs / _assess_owning_pr_exemption; new keyword-only recovered_owning_pr
gitea_mcp_server.py Derive evidence after recovery_sanctioned; thread it through _assess_issue_duplicate_gate
tests/test_issue_755_owning_pr_recovery.py New end-to-end and rejection coverage

Resolver / mutation pairing

Step Task
Capability resolve lock_issue / push_branch / create_pr
Preflight task stamp push_branch (push), create_pr (PR)
Map permission gitea.branch.push + gitea.pr.create, author

Tests

tests/test_issue_755_owning_pr_recovery.py (new, 31 tests) drives the real mcp_server.gitea_lock_issue handler, not only the pure assessor (AC8):

  • sanctioned recovery with the exact owning PR relocks;
  • the recovered lock persists live with truthful dead_session_recovery provenance (prior pid, replacement pid, prior_pid_alive=false, PR number, branch, identity);
  • the recovered lock satisfies _prove_author_ownership_for_pr (AC6);
  • blocked: competing PR, multiple linked PRs, different head, different registered worktree, dirty worktree, worktree parked on another branch, local/remote head mismatch, live prior pid;
  • a fresh claim with no prior lock still requires base equivalence (AC10);
  • forged or partial evidence, evidence for another issue, and a real refused assessment all yield no exemption (AC9).

Load-bearing check: neutralizing owning_pr_recovery_evidence regresses all three success tests to the exact production error above.

Results:

  • Focused suites (755, 753, both duplicate gates, lock registration, provenance) — 102 passed
  • Lock / ownership / worktree / handoff suites — 172 passed, 16 subtests
  • Full suite tests/3529 passed, 6 skipped, 2 failed, 365 subtests

The 2 failures are pre-existing, reproduced on a pristine detached worktree at 08f67007c5984d239e1f52684e9e47543ff41785 (3498 passed, 6 skipped, 2 failed — identical set):

  • tests/test_issue_702_review_findings_f1_f6.py::TestF1RecoveryBeforeTerminalProbe::test_removed_worktree_recovers_before_probe
  • tests/test_reconciler_supersession_close.py::TestReconcilerSupersessionMcpTool::test_tool_posts_comment_and_closes_superseded_pr_issue

git diff --check — clean.

Safety

  • No config, launcher, credential, profile, or memory changes
  • PR #750 and PR #746 untouched; no recovery was performed against #749/#750 in this session
  • No caller-controlled bypass added; the exemption is derived server-side only
  • Worktree and branch preserved for review
  • No review or merge in this session
Closes #755. Base: `08f67007c5984d239e1f52684e9e47543ff41785` Head: `f858c1d1b20f403a80b57fe524a774687c38bc8b` ## Problem #753 shipped the dead-session author-lock recovery assessor, but no sanctioned recovery could reach the lock write. A dead-session lock is, by construction, a lock for work that already has an open PR. The #400 duplicate-work gate blocked unconditionally on any linked open PR, so `gitea_lock_issue` computed `recovery_sanctioned` and then discarded it one gate later: `open PR #750 already covers issue #749 (fail closed)` Because the recovered lock was never persisted, it stayed non-live, so `_prove_author_ownership_for_pr` found no live author lock and `gitea_update_pr_branch_by_merge` also failed closed. Both blockers had one cause, so only the first is fixed here. Live reproduction at the time of filing: issue #749, owning PR #750, branch `fix/issue-749-create-issue-bootstrap`, head `e349839f`, clean registered worktree, local == remote == PR head, dead recorded pid, no competing lock/lease/branch, assessor returning `RECOVERY_SANCTIONED`. ## Design `issue_lock_recovery.owning_pr_recovery_evidence` distils a granted assessment into the minimum evidence the duplicate gate needs: issue, branch, owning PR number, head. It returns `None` unless the outcome is `RECOVERY_SANCTIONED` **and** the assessment's own evidence agrees that local head == remote head == PR head. Every field is copied from evidence the assessor built out of durable lock state plus live git/Gitea observation, so a partial or hand-built assessment cannot authorize anything. There is no new caller-facing input anywhere — no tool signature changed. The duplicate gate re-checks that evidence against the live PR list it was handed, independently: * exactly one linked open PR; * its number matches the recovered owning PR; * its head branch matches the recovered branch; * its head SHA matches the recovered head; * the branch being locked matches the recovered branch; * the evidence is for this issue. Only that exact self-owned PR is exempt. Any mismatch keeps the pre-existing fail-closed behavior and adds a diagnostic naming which element disagreed. Untouched: the competing-branch arm, the claim-lease arms, and the `PHASE_COMMIT`/`PHASE_PUSH`/`PHASE_CREATE_PR` rechecks. With no evidence supplied, the gate behaves exactly as before — the default is `None`, so every existing caller is unchanged. Ownership proof needed no change (AC7): once the recovered lock persists under the live session pid, `_prove_author_ownership_for_pr` matches it by its existing durable-lock path. AC5's renewed live lease and `dead_session_recovery` provenance are written by the existing #753 code, which this change simply lets execute. ## Out of scope The `PHASE_COMMIT` / `PHASE_PUSH` / `PHASE_CREATE_PR` duplicate rechecks still block on a linked open PR for every author, recovered or not. That is pre-existing #400 behavior outside lock recovery, and #755 does not cover it. Flagging it rather than widening this fix. ## Files changed | File | Change | |------|--------| | `issue_lock_recovery.py` | New `owning_pr_recovery_evidence` pure derivation | | `issue_work_duplicate_gate.py` | `_pr_links_issue` / `_all_linked_open_prs` / `_assess_owning_pr_exemption`; new keyword-only `recovered_owning_pr` | | `gitea_mcp_server.py` | Derive evidence after `recovery_sanctioned`; thread it through `_assess_issue_duplicate_gate` | | `tests/test_issue_755_owning_pr_recovery.py` | New end-to-end and rejection coverage | ## Resolver / mutation pairing | Step | Task | |------|------| | Capability resolve | `lock_issue` / `push_branch` / `create_pr` | | Preflight task stamp | `push_branch` (push), `create_pr` (PR) | | Map permission | `gitea.branch.push` + `gitea.pr.create`, author | ## Tests `tests/test_issue_755_owning_pr_recovery.py` (new, 31 tests) drives the real `mcp_server.gitea_lock_issue` handler, not only the pure assessor (AC8): * sanctioned recovery with the exact owning PR relocks; * the recovered lock persists live with truthful `dead_session_recovery` provenance (prior pid, replacement pid, `prior_pid_alive=false`, PR number, branch, identity); * the recovered lock satisfies `_prove_author_ownership_for_pr` (AC6); * blocked: competing PR, multiple linked PRs, different head, different registered worktree, dirty worktree, worktree parked on another branch, local/remote head mismatch, live prior pid; * a fresh claim with no prior lock still requires base equivalence (AC10); * forged or partial evidence, evidence for another issue, and a real refused assessment all yield no exemption (AC9). Load-bearing check: neutralizing `owning_pr_recovery_evidence` regresses all three success tests to the exact production error above. Results: * Focused suites (755, 753, both duplicate gates, lock registration, provenance) — **102 passed** * Lock / ownership / worktree / handoff suites — **172 passed, 16 subtests** * Full suite `tests/` — **3529 passed, 6 skipped, 2 failed, 365 subtests** The 2 failures are **pre-existing**, reproduced on a pristine detached worktree at `08f67007c5984d239e1f52684e9e47543ff41785` (**3498 passed, 6 skipped, 2 failed** — identical set): * `tests/test_issue_702_review_findings_f1_f6.py::TestF1RecoveryBeforeTerminalProbe::test_removed_worktree_recovers_before_probe` * `tests/test_reconciler_supersession_close.py::TestReconcilerSupersessionMcpTool::test_tool_posts_comment_and_closes_superseded_pr_issue` `git diff --check` — clean. ## Safety * No config, launcher, credential, profile, or memory changes * PR #750 and PR #746 untouched; no recovery was performed against #749/#750 in this session * No caller-controlled bypass added; the exemption is derived server-side only * Worktree and branch preserved for review * No review or merge in this session
jcwalker3 added 1 commit 2026-07-18 20:51:24 -05:00
#753 added the dead-session author-lock recovery assessor, but no sanctioned
recovery could ever reach the lock write. A dead-session lock is by construction
a lock for work that already has an open PR, and the #400 duplicate-work gate
blocked unconditionally on any linked open PR. gitea_lock_issue computed
recovery_sanctioned, then discarded it one gate later:

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

Because the recovered lock was never persisted, it stayed non-live, so
_prove_author_ownership_for_pr found no live author lock and
gitea_update_pr_branch_by_merge failed closed too. Both blockers had a single
cause, so only the first one is fixed here.

issue_lock_recovery.owning_pr_recovery_evidence distils a granted assessment
into the minimum evidence the duplicate gate needs: issue, branch, owning PR
number, and head. It returns None unless the outcome is RECOVERY_SANCTIONED and
the assessment's own evidence agrees that local head == remote head == PR head,
so a partial or hand-built assessment cannot authorize anything.

The duplicate gate takes that evidence and re-checks every element against the
live PR list it was handed: exactly one linked open PR, matching number, head
branch, head SHA, and locked branch. Only that exact self-owned PR is exempt.
A different PR, several linked PRs, a different branch or head, or evidence for
another issue all keep failing closed, with a diagnostic naming which element
disagreed. The competing-branch, claim-lease and commit/push/create_pr arms are
untouched, and with no evidence the gate behaves exactly as before.

Ownership proof needed no change: once the recovered lock persists under the
live session pid, _prove_author_ownership_for_pr matches it as before.

Out of scope: the PHASE_COMMIT/PHASE_PUSH/PHASE_CREATE_PR rechecks still block
on a linked open PR for every author, recovered or not. That is pre-existing
#400 behavior, unrelated to lock recovery, and #755 does not cover it.

Tests
- tests/test_issue_755_owning_pr_recovery.py (new, 31 tests) drives the real
  mcp_server.gitea_lock_issue handler, not only the pure assessor: sanctioned
  recovery relocks, persists a live lease with truthful dead_session_recovery
  provenance, and satisfies _prove_author_ownership_for_pr; competing PR,
  multiple linked PRs, mismatched head/branch/worktree, dirty worktree, live
  prior pid, and a fresh claim with no prior lock all stay blocked.
- Neutralizing owning_pr_recovery_evidence regresses all three success tests to
  the exact production error above, so the coverage is load-bearing.
- Focused suites (755, 753, duplicate gates, lock registration, provenance) —
  102 passed.
- Lock/ownership/worktree/handoff suites — 172 passed, 16 subtests.
- Full suite tests/ — 3529 passed, 6 skipped, 2 failed, 365 subtests.
- The same 2 failures reproduce on a pristine detached worktree at
  08f67007c5 (3498 passed, 6 skipped, 2 failed):
  test_issue_702_review_findings_f1_f6 F1 recovery-before-probe and
  test_reconciler_supersession_close org/repo forwarding. Pre-existing.
- git diff --check clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_017BNsk3KUuFchaZyPJsCxjk
Author
Owner

Canonical Issue State

STATE: PR #756 open at head f858c1d1b20f403a80b57fe524a774687c38bc8b; author implementation pushed; no review verdict recorded on Gitea at any head
WHO_IS_NEXT: REVIEWER
NEXT_ACTION: Run a fresh independent reviewer session against PR #756 pinned to exact head f858c1d1b2, acquiring a reviewer PR lease before any review mutation
NEXT_PROMPT:

REVIEW PR #756 — ISSUE #755 DUPLICATE-GATE OWNING-PR RECOVERY

Role: prgs-reviewer (independent; the author session must not review)
Repository: Scaled-Tech-Consulting/Gitea-Tools
PR: #756
Exact head: f858c1d1b20f403a80b57fe524a774687c38bc8b
Base: master at 08f67007c5984d239e1f52684e9e47543ff41785

Verify before any review mutation:
- four namespaces in parity at 08f67007c5984d239e1f52684e9e47543ff41785
- reviewer identity/profile bound, author operations forbidden
- PR #756 still open at the exact head above
- acquire a reviewer PR lease pinned to that head

Assess against issue #755 AC1-AC10:
- AC1/AC2: server-derived recovery evidence reaches the duplicate-work gate and exempts only the exact self-owned PR
- AC3: a different PR, several linked PRs, a different branch/head/worktree, a foreign author or profile, dirty or divergent work, a live prior PID, and a competing live lock or lease all keep failing closed
- AC4: ordinary new-issue and competing-work duplicate blocking is unchanged
- AC5: the recovered lock persists with a renewed live lease and truthful recovery provenance
- AC6: that live lock satisfies the ownership prover used by gitea_update_pr_branch_by_merge
- AC7: downstream ownership logic was not altered
- AC8: coverage drives the real gitea_lock_issue handler, not only the pure assessor
- AC9: no caller-controlled recovery or duplicate-gate bypass input was added
- AC10: new-claim base-equivalence and worktree-cleanliness guards are intact

Also confirm the author's claim that the two full-suite failures are pre-existing by reproducing them on a pristine detached worktree at 08f67007c5984d239e1f52684e9e47543ff41785.

Do not run gitea_merge_pr. Do not mutate PR #750 or PR #746.

WHAT_HAPPENED: Acquired the author issue lock for #755 from worktree branches/issue-755-owning-pr-recovery; implemented owning_pr_recovery_evidence in issue_lock_recovery.py; taught issue_work_duplicate_gate.py to exempt only the exact self-owned open PR when that server-derived evidence matches the live PR list; threaded the evidence through _assess_issue_duplicate_gate in gitea_mcp_server.py; added tests/test_issue_755_owning_pr_recovery.py (31 tests) driving the real MCP handler; committed f858c1d1b2, pushed the branch, and opened PR #756. Duplicate-work outcome: duplicate work not prevented — the single linked open PR is this session's own PR #756 for issue #755.
WHY: #753 shipped the recovery assessor, but a dead-session lock always belongs to work that already has an open PR, and the #400 duplicate-work gate rejected any linked open PR unconditionally. gitea_lock_issue therefore computed recovery_sanctioned and discarded it one gate later with "open PR #750 already covers issue #749 (fail closed)", leaving the recovered lock unpersisted and non-live, which in turn made gitea_update_pr_branch_by_merge fail its ownership proof.
RELATED_PRS: #756 (this PR, open); #754 / issue #753 (parent implementation, landed on master at 08f67007c5); #750 / issue #749 (the live reproduction, open, untouched by this session); #746 (open, untouched by this session)
BLOCKERS: none for PR #756. Dependency note: dead-session recovery for issue #749 / PR #750 stays unavailable until PR #756 lands on master through sanctioned merge tooling and the MCP namespaces restart onto that master SHA.
VALIDATION: Focused suites (755, 753, both duplicate gates, lock registration, provenance) 102 passed; lock/ownership/worktree/handoff suites 172 passed with 16 subtests; full suite tests/ 3529 passed, 6 skipped, 2 failed, 365 subtests; pristine baseline at 08f67007c5 gave 3498 passed, 6 skipped, 2 failed with the identical failure set (test_issue_702_review_findings_f1_f6 F1 recovery-before-probe and test_reconciler_supersession_close org/repo forwarding), so both are pre-existing; neutralizing owning_pr_recovery_evidence regresses all three success tests to the exact production error, proving the coverage is load-bearing; git diff --check clean
LAST_UPDATED_BY: jcwalker3 / prgs-author (author session)

[THREAD STATE LEDGER] PR #756 — author implementation pushed, no review verdict exists

What is true now:

  • PR state: open
  • Current head SHA: f858c1d1b2
  • Base branch: master at 08f67007c5
  • Commits behind base: 0
  • Mergeable: true, no conflicts
  • Checks: checks_required=false, checks_status=not_required
  • Server-side decision state: no review verdict recorded on Gitea at any head
  • Local verdict/state: author implementation complete locally; no reviewer verdict prepared
  • Author lock: live for issue #755, held by the author session
  • Reviewer lease: none exists
  • Merger lease: none exists
  • Latest known validation: full suite 3529 passed, 6 skipped, 2 failed, 365 subtests; identical 2 failures reproduce on pristine master 08f67007c5984d239e1f52684e9e47543ff41785; git diff --check clean

What changed:

  • Server-derived dead-session recovery evidence is now threaded into the #400 duplicate-work gate, so a sanctioned recovery is no longer discarded by its own owning PR
  • Files changed: issue_lock_recovery.py, issue_work_duplicate_gate.py, gitea_mcp_server.py, tests/test_issue_755_owning_pr_recovery.py (new, 31 tests)

What is blocked:

  • Blocker classification: no blocker
  • Dependency note: issue #749 / PR #750 recovery stays unavailable until PR #756 lands on master through sanctioned merge tooling and the namespaces restart onto that master SHA; downstream dependency, not a blocker on PR #756

Who/what acts next:

  • Next actor: reviewer
  • Required action: fresh independent review of PR #756 pinned to exact head f858c1d1b2, acquiring a reviewer PR lease first
  • Do not do: post an approve or request-changes verdict from the author session; reuse any prior-head verdict; run gitea_merge_pr from this session; mutate PR #750 or PR #746; execute dead-session recovery against issue #749 from this branch
  • Resume from: PR #756 diff and this ledger
## Canonical Issue State STATE: PR #756 open at head f858c1d1b20f403a80b57fe524a774687c38bc8b; author implementation pushed; no review verdict recorded on Gitea at any head WHO_IS_NEXT: REVIEWER NEXT_ACTION: Run a fresh independent reviewer session against PR #756 pinned to exact head f858c1d1b20f403a80b57fe524a774687c38bc8b, acquiring a reviewer PR lease before any review mutation NEXT_PROMPT: ```text REVIEW PR #756 — ISSUE #755 DUPLICATE-GATE OWNING-PR RECOVERY Role: prgs-reviewer (independent; the author session must not review) Repository: Scaled-Tech-Consulting/Gitea-Tools PR: #756 Exact head: f858c1d1b20f403a80b57fe524a774687c38bc8b Base: master at 08f67007c5984d239e1f52684e9e47543ff41785 Verify before any review mutation: - four namespaces in parity at 08f67007c5984d239e1f52684e9e47543ff41785 - reviewer identity/profile bound, author operations forbidden - PR #756 still open at the exact head above - acquire a reviewer PR lease pinned to that head Assess against issue #755 AC1-AC10: - AC1/AC2: server-derived recovery evidence reaches the duplicate-work gate and exempts only the exact self-owned PR - AC3: a different PR, several linked PRs, a different branch/head/worktree, a foreign author or profile, dirty or divergent work, a live prior PID, and a competing live lock or lease all keep failing closed - AC4: ordinary new-issue and competing-work duplicate blocking is unchanged - AC5: the recovered lock persists with a renewed live lease and truthful recovery provenance - AC6: that live lock satisfies the ownership prover used by gitea_update_pr_branch_by_merge - AC7: downstream ownership logic was not altered - AC8: coverage drives the real gitea_lock_issue handler, not only the pure assessor - AC9: no caller-controlled recovery or duplicate-gate bypass input was added - AC10: new-claim base-equivalence and worktree-cleanliness guards are intact Also confirm the author's claim that the two full-suite failures are pre-existing by reproducing them on a pristine detached worktree at 08f67007c5984d239e1f52684e9e47543ff41785. Do not run gitea_merge_pr. Do not mutate PR #750 or PR #746. ``` WHAT_HAPPENED: Acquired the author issue lock for #755 from worktree branches/issue-755-owning-pr-recovery; implemented owning_pr_recovery_evidence in issue_lock_recovery.py; taught issue_work_duplicate_gate.py to exempt only the exact self-owned open PR when that server-derived evidence matches the live PR list; threaded the evidence through _assess_issue_duplicate_gate in gitea_mcp_server.py; added tests/test_issue_755_owning_pr_recovery.py (31 tests) driving the real MCP handler; committed f858c1d1b20f403a80b57fe524a774687c38bc8b, pushed the branch, and opened PR #756. Duplicate-work outcome: duplicate work not prevented — the single linked open PR is this session's own PR #756 for issue #755. WHY: #753 shipped the recovery assessor, but a dead-session lock always belongs to work that already has an open PR, and the #400 duplicate-work gate rejected any linked open PR unconditionally. gitea_lock_issue therefore computed recovery_sanctioned and discarded it one gate later with "open PR #750 already covers issue #749 (fail closed)", leaving the recovered lock unpersisted and non-live, which in turn made gitea_update_pr_branch_by_merge fail its ownership proof. RELATED_PRS: #756 (this PR, open); #754 / issue #753 (parent implementation, landed on master at 08f67007c5984d239e1f52684e9e47543ff41785); #750 / issue #749 (the live reproduction, open, untouched by this session); #746 (open, untouched by this session) BLOCKERS: none for PR #756. Dependency note: dead-session recovery for issue #749 / PR #750 stays unavailable until PR #756 lands on master through sanctioned merge tooling and the MCP namespaces restart onto that master SHA. VALIDATION: Focused suites (755, 753, both duplicate gates, lock registration, provenance) 102 passed; lock/ownership/worktree/handoff suites 172 passed with 16 subtests; full suite tests/ 3529 passed, 6 skipped, 2 failed, 365 subtests; pristine baseline at 08f67007c5984d239e1f52684e9e47543ff41785 gave 3498 passed, 6 skipped, 2 failed with the identical failure set (test_issue_702_review_findings_f1_f6 F1 recovery-before-probe and test_reconciler_supersession_close org/repo forwarding), so both are pre-existing; neutralizing owning_pr_recovery_evidence regresses all three success tests to the exact production error, proving the coverage is load-bearing; git diff --check clean LAST_UPDATED_BY: jcwalker3 / prgs-author (author session) [THREAD STATE LEDGER] PR #756 — author implementation pushed, no review verdict exists What is true now: - PR state: open - Current head SHA: f858c1d1b20f403a80b57fe524a774687c38bc8b - Base branch: master at 08f67007c5984d239e1f52684e9e47543ff41785 - Commits behind base: 0 - Mergeable: true, no conflicts - Checks: checks_required=false, checks_status=not_required - Server-side decision state: no review verdict recorded on Gitea at any head - Local verdict/state: author implementation complete locally; no reviewer verdict prepared - Author lock: live for issue #755, held by the author session - Reviewer lease: none exists - Merger lease: none exists - Latest known validation: full suite 3529 passed, 6 skipped, 2 failed, 365 subtests; identical 2 failures reproduce on pristine master 08f67007c5984d239e1f52684e9e47543ff41785; git diff --check clean What changed: - Server-derived dead-session recovery evidence is now threaded into the #400 duplicate-work gate, so a sanctioned recovery is no longer discarded by its own owning PR - Files changed: issue_lock_recovery.py, issue_work_duplicate_gate.py, gitea_mcp_server.py, tests/test_issue_755_owning_pr_recovery.py (new, 31 tests) What is blocked: - Blocker classification: no blocker - Dependency note: issue #749 / PR #750 recovery stays unavailable until PR #756 lands on master through sanctioned merge tooling and the namespaces restart onto that master SHA; downstream dependency, not a blocker on PR #756 Who/what acts next: - Next actor: reviewer - Required action: fresh independent review of PR #756 pinned to exact head f858c1d1b20f403a80b57fe524a774687c38bc8b, acquiring a reviewer PR lease first - Do not do: post an approve or request-changes verdict from the author session; reuse any prior-head verdict; run gitea_merge_pr from this session; mutate PR #750 or PR #746; execute dead-session recovery against issue #749 from this branch - Resume from: PR #756 diff and this ledger
Owner

repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #756
issue: #755
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 49447-f17743b05c18
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr-756
phase: claimed
candidate_head: f858c1d1b2
target_branch: master
target_branch_sha: 08f67007c5
last_activity: 2026-07-19T01:58:21Z
expires_at: 2026-07-19T02:08:21Z
blocker: none

<!-- mcp-review-lease:v1 --> repo: Scaled-Tech-Consulting/Gitea-Tools pr: #756 issue: #755 reviewer_identity: sysadmin profile: prgs-reviewer session_id: 49447-f17743b05c18 worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr-756 phase: claimed candidate_head: f858c1d1b20f403a80b57fe524a774687c38bc8b target_branch: master target_branch_sha: 08f67007c5984d239e1f52684e9e47543ff41785 last_activity: 2026-07-19T01:58:21Z expires_at: 2026-07-19T02:08:21Z blocker: none
sysadmin approved these changes 2026-07-18 21:01:04 -05:00
sysadmin left a comment
Owner

Canonical PR State

STATE: PR #756 open at head f858c1d1b20f403a80b57fe524a774687c38bc8b; independent Grok reviewer APPROVED at this exact head; no merge performed
WHO_IS_NEXT: MERGER
NEXT_ACTION: Start a fresh Grok merger session for PR #756 pinned to exact head f858c1d1b20f403a80b57fe524a774687c38bc8b; adopt or acquire merger lease and merge only if all merger gates pass
NEXT_PROMPT:

MERGE PR #756 — ISSUE #755 DUPLICATE-GATE OWNING-PR RECOVERY

Role: prgs-merger (fresh independent session; do not reuse this reviewer session)
Repository: Scaled-Tech-Consulting/Gitea-Tools on remote prgs
PR: #756
Issue: #755
Exact head: f858c1d1b20f403a80b57fe524a774687c38bc8b
Base: master at 08f67007c5984d239e1f52684e9e47543ff41785
Approval: formal APPROVE by sysadmin / prgs-reviewer at the exact head above

Preflight:
- prove prgs-merger identity/capability; author and reviewer mutations forbidden for this role
- runtime/master parity at 08f67007c5984d239e1f52684e9e47543ff41785 (or the post-review master if unchanged)
- refresh PR #756; stop unless open, head exact, base exact, mergeable, zero behind, approval still valid at this head
- acquire/adopt merger lease before any merge mutation

Do not re-review unless head moved. Do not touch PR #750 or PR #746.

WHAT_HAPPENED: Independent Grok reviewer session (sysadmin / prgs-reviewer) acquired reviewer lease comment 12718 against worktree branches/review-pr-756, inspected the full diff for #755, validated focused/affected suites (200 passed including 31 new #755 tests), and submitted formal APPROVE pinned to f858c1d1b2. No merge. PR #750/#746 untouched.
WHY: Server-derived owning-PR recovery evidence correctly exempts only the exact self-owned open PR from the #400 duplicate-work gate after RECOVERY_SANCTIONED; mutation path revalidates live PR number/branch/head; competing/mismatched/forged/partial paths fail closed; ordinary duplicate and base-equivalence behavior preserved; recovered lock proves ownership for update-by-merge.
ISSUE: #755
HEAD_SHA: f858c1d1b2
REVIEW_STATUS: APPROVED at exact head f858c1d1b2 by sysadmin (prgs-reviewer)
MERGE_READY: true
BLOCKERS: none for PR #756. Dependency note only: #749/#750 recovery remains unavailable until this PR lands on master and MCP namespaces restart onto that master SHA.
VALIDATION: test_issue_755_owning_pr_recovery 31 passed; expanded focused/affected suites (#753 recovery, both duplicate gates, lock adoption/store/worktree/provenance, lock MCP registration, PR ownership, worktree ownership, pr sync) 200 passed; git diff --check clean; py_compile clean; worktree clean at reviewed head; full suite not re-run (author pre-existing 2 failures on pristine master accepted under session rules)
NATIVE_REVIEW_PROOF: transport=native_mcp; entrypoint=mcp_server; token_fingerprint=0d54d79a6d57405b; pid=49447; mode=production
LAST_UPDATED_BY: sysadmin / prgs-reviewer (Grok independent review session)


Independent code review summary

Files: issue_lock_recovery.py, issue_work_duplicate_gate.py, gitea_mcp_server.py, tests/test_issue_755_owning_pr_recovery.py

AC1–AC10: all satisfied. Exemption is server-derived only (owning_pr_recovery_evidence); requires RECOVERY_SANCTIONED plus local==remote==PR head; gate rechecks exactly one linked open PR and matching number/branch/head/issue/locked-branch against the live PR list. Competing PR, multi-PR, wrong head/branch/worktree, dirty tree, live prior PID, foreign issue, forged/partial assessment evidence all fail closed. Default recovered_owning_pr=None preserves pre-#755 callers. Recovered lock is live with truthful dead_session_recovery provenance and satisfies _prove_author_ownership_for_pr. No #749/#750 special cases; no MCP tool signature bypass.

Race/trust boundary: evidence can age between assessment and mutation, but the gate re-lists and revalidates; head move or extra linked PR refuses exemption rather than adopting stale proof.

Verdict: APPROVE

## Canonical PR State STATE: PR #756 open at head f858c1d1b20f403a80b57fe524a774687c38bc8b; independent Grok reviewer APPROVED at this exact head; no merge performed WHO_IS_NEXT: MERGER NEXT_ACTION: Start a fresh Grok merger session for PR #756 pinned to exact head f858c1d1b20f403a80b57fe524a774687c38bc8b; adopt or acquire merger lease and merge only if all merger gates pass NEXT_PROMPT: ```text MERGE PR #756 — ISSUE #755 DUPLICATE-GATE OWNING-PR RECOVERY Role: prgs-merger (fresh independent session; do not reuse this reviewer session) Repository: Scaled-Tech-Consulting/Gitea-Tools on remote prgs PR: #756 Issue: #755 Exact head: f858c1d1b20f403a80b57fe524a774687c38bc8b Base: master at 08f67007c5984d239e1f52684e9e47543ff41785 Approval: formal APPROVE by sysadmin / prgs-reviewer at the exact head above Preflight: - prove prgs-merger identity/capability; author and reviewer mutations forbidden for this role - runtime/master parity at 08f67007c5984d239e1f52684e9e47543ff41785 (or the post-review master if unchanged) - refresh PR #756; stop unless open, head exact, base exact, mergeable, zero behind, approval still valid at this head - acquire/adopt merger lease before any merge mutation Do not re-review unless head moved. Do not touch PR #750 or PR #746. ``` WHAT_HAPPENED: Independent Grok reviewer session (sysadmin / prgs-reviewer) acquired reviewer lease comment 12718 against worktree branches/review-pr-756, inspected the full diff for #755, validated focused/affected suites (200 passed including 31 new #755 tests), and submitted formal APPROVE pinned to f858c1d1b20f403a80b57fe524a774687c38bc8b. No merge. PR #750/#746 untouched. WHY: Server-derived owning-PR recovery evidence correctly exempts only the exact self-owned open PR from the #400 duplicate-work gate after RECOVERY_SANCTIONED; mutation path revalidates live PR number/branch/head; competing/mismatched/forged/partial paths fail closed; ordinary duplicate and base-equivalence behavior preserved; recovered lock proves ownership for update-by-merge. ISSUE: #755 HEAD_SHA: f858c1d1b20f403a80b57fe524a774687c38bc8b REVIEW_STATUS: APPROVED at exact head f858c1d1b20f403a80b57fe524a774687c38bc8b by sysadmin (prgs-reviewer) MERGE_READY: true BLOCKERS: none for PR #756. Dependency note only: #749/#750 recovery remains unavailable until this PR lands on master and MCP namespaces restart onto that master SHA. VALIDATION: test_issue_755_owning_pr_recovery 31 passed; expanded focused/affected suites (#753 recovery, both duplicate gates, lock adoption/store/worktree/provenance, lock MCP registration, PR ownership, worktree ownership, pr sync) 200 passed; git diff --check clean; py_compile clean; worktree clean at reviewed head; full suite not re-run (author pre-existing 2 failures on pristine master accepted under session rules) NATIVE_REVIEW_PROOF: transport=native_mcp; entrypoint=mcp_server; token_fingerprint=0d54d79a6d57405b; pid=49447; mode=production LAST_UPDATED_BY: sysadmin / prgs-reviewer (Grok independent review session) --- ## Independent code review summary **Files:** issue_lock_recovery.py, issue_work_duplicate_gate.py, gitea_mcp_server.py, tests/test_issue_755_owning_pr_recovery.py **AC1–AC10:** all satisfied. Exemption is server-derived only (owning_pr_recovery_evidence); requires RECOVERY_SANCTIONED plus local==remote==PR head; gate rechecks exactly one linked open PR and matching number/branch/head/issue/locked-branch against the live PR list. Competing PR, multi-PR, wrong head/branch/worktree, dirty tree, live prior PID, foreign issue, forged/partial assessment evidence all fail closed. Default recovered_owning_pr=None preserves pre-#755 callers. Recovered lock is live with truthful dead_session_recovery provenance and satisfies _prove_author_ownership_for_pr. No #749/#750 special cases; no MCP tool signature bypass. **Race/trust boundary:** evidence can age between assessment and mutation, but the gate re-lists and revalidates; head move or extra linked PR refuses exemption rather than adopting stale proof. **Verdict: APPROVE**
Owner

repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #756
issue: #755
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 49447-f17743b05c18
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr-756
phase: released
candidate_head: f858c1d1b2
target_branch: master
target_branch_sha: 08f67007c5
last_activity: 2026-07-19T02:01:20Z
expires_at: 2026-07-19T02:11:20Z
blocker: manual-release

<!-- mcp-review-lease:v1 --> repo: Scaled-Tech-Consulting/Gitea-Tools pr: #756 issue: #755 reviewer_identity: sysadmin profile: prgs-reviewer session_id: 49447-f17743b05c18 worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr-756 phase: released candidate_head: f858c1d1b20f403a80b57fe524a774687c38bc8b target_branch: master target_branch_sha: 08f67007c5984d239e1f52684e9e47543ff41785 last_activity: 2026-07-19T02:01:20Z expires_at: 2026-07-19T02:11:20Z blocker: manual-release
Owner

repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #756
issue: #755
reviewer_identity: sysadmin
profile: prgs-merger
session_id: 53218-0e7a3db4efab
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/merge-pr-756
phase: claimed
candidate_head: f858c1d1b2
target_branch: master
target_branch_sha: 08f67007c5
last_activity: 2026-07-19T02:08:20Z
expires_at: 2026-07-19T02:18:20Z
blocker: none

<!-- mcp-review-lease:v1 --> repo: Scaled-Tech-Consulting/Gitea-Tools pr: #756 issue: #755 reviewer_identity: sysadmin profile: prgs-merger session_id: 53218-0e7a3db4efab worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/merge-pr-756 phase: claimed candidate_head: f858c1d1b20f403a80b57fe524a774687c38bc8b target_branch: master target_branch_sha: 08f67007c5984d239e1f52684e9e47543ff41785 last_activity: 2026-07-19T02:08:20Z expires_at: 2026-07-19T02:18:20Z blocker: none
sysadmin merged commit 043df0f7df into master 2026-07-18 21:08:35 -05:00
Owner

Stale #332 review-decision lock cleanup (#594)

Status: APPLIED

Manual deletion of session-state files is not the workflow.
This path only clears a lock when the referenced PR is merged/closed.

## Stale #332 review-decision lock cleanup (#594) Status: **APPLIED** - actor: `sysadmin` - profile: `prgs-merger` - timestamp: `2026-07-19T02:08:35.838401+00:00` - last terminal: `approve` on PR #756 - PR state: `closed` (merged=True) - merge_commit_sha: `043df0f7df220ad67ac1c3fefdcf3897dad9478b` - prior live_mutations_count: `1` - prior profile_identity: `prgs-reviewer` Manual deletion of session-state files is **not** the workflow. This path only clears a lock when the referenced PR is merged/closed.
Sign in to join this conversation.
No Reviewers
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

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