[merger][bug] No sanctioned lease path after reviewer lease expiry; adopt tool returns internal_error #718

Open
opened 2026-07-16 02:10:49 -05:00 by jcwalker3 · 1 comment
Owner

Problem

An approved and otherwise merge-eligible PR becomes impossible to merge when its reviewer lease has expired.

Reproduced on PR #688 at head:

1c37e620142ec4c9d5271ee84a0abc2d8decdd99

Valid approval:

  • Review 442
  • Current-head approval
  • Not stale, dismissed, or quarantined
  • No blocking change request
  • PR mergeable
  • Merger identity differs from author
  • Runtime at current-master parity

The native merge gate refused the merge because no in-session lease proof existed. The previous reviewer lease 11762 had expired.

The sanctioned merger-native recovery tool:

gitea_adopt_merger_pr_lease

returned:

  • internal_error
  • retryable: false
  • transport_survives: true

Lease diagnosis reported:

  • classification: no_lease
  • active lease: none
  • next action: acquire

However, the only acquisition tool offered by the merge error is gitea_acquire_reviewer_pr_lease, which is a reviewer-role mutation and cannot be used through the immutable merger namespace. The auto-mode classifier correctly blocked that cross-role workaround.

This creates a fail-closed deadlock:

  1. merge requires lease proof;
  2. reviewer lease has expired;
  3. merger adoption fails internally;
  4. merger has no sanctioned lease-acquisition path;
  5. reviewer acquisition through the merger namespace violates role separation.

No merge or workaround was performed.

Expected behavior

A merger operating through prgs-merger must have a sanctioned native path to obtain valid merge lease proof when:

  • the PR is open;
  • the head is pinned;
  • approval is valid at the current head;
  • no active reviewer lease exists;
  • the isolated merger worktree matches the pinned head;
  • all merge gates otherwise pass.

The implementation may safely extend adoption semantics or add a dedicated merger lease-acquisition operation, but it must not require a reviewer-role mutation.

Acceptance criteria

  1. A merger-native operation handles the no_lease and expired-lease cases without profile switching.
  2. The resulting proof is bound to the exact host, repository, PR, head SHA, merger identity, session, and isolated worktree.
  3. gitea_merge_pr recognizes the sanctioned proof.
  4. Active foreign leases remain fail-closed and cannot be stolen.
  5. Dirty, mismatched, missing, or unpinned worktrees remain blocked.
  6. Reviewer decision locks are not erased or weakened.
  7. Failures return structured actionable errors instead of generic internal_error.
  8. Tests cover:
    • no existing lease;
    • expired reviewer lease;
    • active foreign lease;
    • head mismatch;
    • dirty worktree;
    • successful merger proof followed by merge eligibility.
  9. Regression coverage proves the merger cannot call reviewer-only acquisition as a workaround.
  10. Documentation and tool guidance name the exact sanctioned next action.

Impact

PR #688 is approved and mergeable but cannot land. Its live terminal review lock then blocks the prepared formal verdict on PR #616, creating a queue-wide dependency stall.

Related evidence

## Problem An approved and otherwise merge-eligible PR becomes impossible to merge when its reviewer lease has expired. Reproduced on PR #688 at head: `1c37e620142ec4c9d5271ee84a0abc2d8decdd99` Valid approval: * Review 442 * Current-head approval * Not stale, dismissed, or quarantined * No blocking change request * PR mergeable * Merger identity differs from author * Runtime at current-master parity The native merge gate refused the merge because no in-session lease proof existed. The previous reviewer lease 11762 had expired. The sanctioned merger-native recovery tool: `gitea_adopt_merger_pr_lease` returned: * `internal_error` * `retryable: false` * `transport_survives: true` Lease diagnosis reported: * classification: `no_lease` * active lease: none * next action: acquire However, the only acquisition tool offered by the merge error is `gitea_acquire_reviewer_pr_lease`, which is a reviewer-role mutation and cannot be used through the immutable merger namespace. The auto-mode classifier correctly blocked that cross-role workaround. This creates a fail-closed deadlock: 1. merge requires lease proof; 2. reviewer lease has expired; 3. merger adoption fails internally; 4. merger has no sanctioned lease-acquisition path; 5. reviewer acquisition through the merger namespace violates role separation. No merge or workaround was performed. ## Expected behavior A merger operating through `prgs-merger` must have a sanctioned native path to obtain valid merge lease proof when: * the PR is open; * the head is pinned; * approval is valid at the current head; * no active reviewer lease exists; * the isolated merger worktree matches the pinned head; * all merge gates otherwise pass. The implementation may safely extend adoption semantics or add a dedicated merger lease-acquisition operation, but it must not require a reviewer-role mutation. ## Acceptance criteria 1. A merger-native operation handles the `no_lease` and expired-lease cases without profile switching. 2. The resulting proof is bound to the exact host, repository, PR, head SHA, merger identity, session, and isolated worktree. 3. `gitea_merge_pr` recognizes the sanctioned proof. 4. Active foreign leases remain fail-closed and cannot be stolen. 5. Dirty, mismatched, missing, or unpinned worktrees remain blocked. 6. Reviewer decision locks are not erased or weakened. 7. Failures return structured actionable errors instead of generic `internal_error`. 8. Tests cover: * no existing lease; * expired reviewer lease; * active foreign lease; * head mismatch; * dirty worktree; * successful merger proof followed by merge eligibility. 9. Regression coverage proves the merger cannot call reviewer-only acquisition as a workaround. 10. Documentation and tool guidance name the exact sanctioned next action. ## Impact PR #688 is approved and mergeable but cannot land. Its live terminal review lock then blocks the prepared formal verdict on PR #616, creating a queue-wide dependency stall. ## Related evidence * PR #688 * Issue #687 * Review 442 * Expired lease comment 11762 * PR #616 lease 11767 * Existing decision-lock and lease-recovery issues, including #332 and #594 where applicable * Prior `gitea_adopt_merger_pr_lease` defects #547 and #548 (closed) describe a different failure mode (duplicate `verify_preflight_purity` clearing capability state, surfacing as a pre-flight order violation), not `internal_error`. * A second tool, `gitea_dry_run_pr_review`, returned the same `internal_error` / `retryable:false` / `transport_survives:true` shape. Recorded as possibly related shared-path evidence only; no common root cause is claimed without proof.
Author
Owner

Canonical Issue State

STATE: ready-for-author
WHO_IS_NEXT: author
NEXT_ACTION: Implement structured MCP error mapping for preflight task mismatch on #718
NEXT_PROMPT:

You are the author for issue #718 (corrected scope).
Do not re-open the disproven merger-lease deadlock claim.

Verified: gitea_resolve_task_capability(task="adopt_merger_pr_lease") through
prgs-merger returns allowed_in_current_session=true. No merger-role deadlock
has been demonstrated. The earlier gitea_adopt_merger_pr_lease internal_error
came from resolving merge_pr instead of adopt_merger_pr_lease. The correct
resolve→adopt sequence has not been fully executed yet. Do not claim
gitea_dry_run_pr_review shares this root cause.

Implement and test:
1) structured error mapping for preflight task mismatch (not generic internal_error);
2) identify resolved task and required task without secrets;
3) safe next action telling the caller to resolve the required capability;
4) transport_survives remains truthful; no mutation on the mismatch path;
5) regression: resolve merge_pr then call merger adoption → structured mismatch;
6) regression: correct resolve→adopt reaches normal adoption gates;
7) do not weaken role, lease, parity, native-transport, or anti-stomp protections;
8) do not add a new merger acquisition tool.

Target title: [errors][bug] Preflight task mismatch escapes as generic internal_error

WHAT_HAPPENED: Diagnostic proved adopt_merger_pr_lease capability resolves as allowed on prgs-merger; #718 narrowed from lease-deadlock to structured-error defect. Canonical correction posted; native gitea_edit_issue is not exposed so title/body fields remain historical until an edit tool lands or operator edits.
WHY: A mismatched capability proof raises a useful internal RuntimeError, but the MCP boundary converts it into a generic internal_error with no actionable instruction. Callers cannot see that they must resolve the correct task.
RELATED_PRS: PR #688 (historical reproduction context only; not the remaining defect); prior closed adopt defects #547 #548 for different failure modes
BLOCKERS: none
VALIDATION: gitea-merger resolve adopt_merger_pr_lease → allowed_in_current_session true; no adopt/merge/review performed in this correction session
LAST_UPDATED_BY: prgs-author / jcwalker3


Withdrawn claims

  • Fail-closed merger lease deadlock (merge requires lease; adopt fails; merger has no acquisition path).
  • Requirement for a new merger lease-acquisition tool beyond existing gitea_adopt_merger_pr_lease.
  • Shared root cause with gitea_dry_run_pr_review (unproven).

Corrected title (target)

[errors][bug] Preflight task mismatch escapes as generic internal_error

Label intent after this correction

Retain type:bug, mcp, reliability, safety, workflow-hardening; add preflight; remove leases, recovery, terminal-lock (lease-deadlock framing only); keep status:ready.

## Canonical Issue State STATE: ready-for-author WHO_IS_NEXT: author NEXT_ACTION: Implement structured MCP error mapping for preflight task mismatch on #718 NEXT_PROMPT: ```text You are the author for issue #718 (corrected scope). Do not re-open the disproven merger-lease deadlock claim. Verified: gitea_resolve_task_capability(task="adopt_merger_pr_lease") through prgs-merger returns allowed_in_current_session=true. No merger-role deadlock has been demonstrated. The earlier gitea_adopt_merger_pr_lease internal_error came from resolving merge_pr instead of adopt_merger_pr_lease. The correct resolve→adopt sequence has not been fully executed yet. Do not claim gitea_dry_run_pr_review shares this root cause. Implement and test: 1) structured error mapping for preflight task mismatch (not generic internal_error); 2) identify resolved task and required task without secrets; 3) safe next action telling the caller to resolve the required capability; 4) transport_survives remains truthful; no mutation on the mismatch path; 5) regression: resolve merge_pr then call merger adoption → structured mismatch; 6) regression: correct resolve→adopt reaches normal adoption gates; 7) do not weaken role, lease, parity, native-transport, or anti-stomp protections; 8) do not add a new merger acquisition tool. Target title: [errors][bug] Preflight task mismatch escapes as generic internal_error ``` WHAT_HAPPENED: Diagnostic proved adopt_merger_pr_lease capability resolves as allowed on prgs-merger; #718 narrowed from lease-deadlock to structured-error defect. Canonical correction posted; native gitea_edit_issue is not exposed so title/body fields remain historical until an edit tool lands or operator edits. WHY: A mismatched capability proof raises a useful internal RuntimeError, but the MCP boundary converts it into a generic internal_error with no actionable instruction. Callers cannot see that they must resolve the correct task. RELATED_PRS: PR #688 (historical reproduction context only; not the remaining defect); prior closed adopt defects #547 #548 for different failure modes BLOCKERS: none VALIDATION: gitea-merger resolve adopt_merger_pr_lease → allowed_in_current_session true; no adopt/merge/review performed in this correction session LAST_UPDATED_BY: prgs-author / jcwalker3 --- ## Withdrawn claims - Fail-closed merger lease deadlock (merge requires lease; adopt fails; merger has no acquisition path). - Requirement for a new merger lease-acquisition tool beyond existing `gitea_adopt_merger_pr_lease`. - Shared root cause with `gitea_dry_run_pr_review` (unproven). ## Corrected title (target) `[errors][bug] Preflight task mismatch escapes as generic internal_error` ## Label intent after this correction Retain `type:bug`, `mcp`, `reliability`, `safety`, `workflow-hardening`; add `preflight`; remove `leases`, `recovery`, `terminal-lock` (lease-deadlock framing only); keep `status:ready`.
jcwalker3 added preflight and removed leasesrecoveryterminal-lock labels 2026-07-16 02:38:52 -05:00
jcwalker3 added status:pr-open and removed status:ready labels 2026-07-17 00:44:27 -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#718