fix(finish): surface real merge errors verbatim, classify non-retryable, re-check landed state after a 5xx #463

Merged
hexajon merged 1 commit from issue-456-merge-error-surface into main 2026-08-27 05:52:56 +00:00
Owner

Summary

madt_finish and madt_prs action=merge relabeled every merge failure as a generic "transient flake" and auto-retried 4 times, even for permanent errors, and a 5xx that had actually landed server-side was reported as failed - a retry then opened a redundant PR on a branch already in the base.

Three fixes:

  1. Verbatim surfacing: a merge failure now wraps the real Gitea body ("merge failed: %w"), no relabel.
  2. Non-retryable classification: a new gitea.IsMergeNonRetryable predicate flags the permanent 405 bodies (already-included / nothing-to-merge, no-signing-key, out-of-date-with-base). MergeWithRetry breaks immediately on them - no retry, no branch update - so the caller sees the real cause. A plain 4xx also fails fast.
  3. Post-5xx landed check: after a server error, the PR merged-state is re-read before any retry. merged=true reports success and opens no duplicate PR; not-landed-and-mergeable retries on the existing 5s/10s/15s backoff; otherwise the 5xx surfaces verbatim. The landed check gates every retry.

The behind-base update path and the try-again-later backoff are unchanged. Adds tests for each non-retryable body (one attempt, verbatim), the 5xx-already-merged path (success, no duplicate, MergePR called once), and the 5xx-transient-still-mergeable path. No auto-merge-on-green (#438) behavior included.

Refs #456

## Summary madt_finish and madt_prs action=merge relabeled every merge failure as a generic "transient flake" and auto-retried 4 times, even for permanent errors, and a 5xx that had actually landed server-side was reported as failed - a retry then opened a redundant PR on a branch already in the base. Three fixes: 1. Verbatim surfacing: a merge failure now wraps the real Gitea body ("merge failed: %w"), no relabel. 2. Non-retryable classification: a new gitea.IsMergeNonRetryable predicate flags the permanent 405 bodies (already-included / nothing-to-merge, no-signing-key, out-of-date-with-base). MergeWithRetry breaks immediately on them - no retry, no branch update - so the caller sees the real cause. A plain 4xx also fails fast. 3. Post-5xx landed check: after a server error, the PR merged-state is re-read before any retry. merged=true reports success and opens no duplicate PR; not-landed-and-mergeable retries on the existing 5s/10s/15s backoff; otherwise the 5xx surfaces verbatim. The landed check gates every retry. The behind-base update path and the try-again-later backoff are unchanged. Adds tests for each non-retryable body (one attempt, verbatim), the 5xx-already-merged path (success, no duplicate, MergePR called once), and the 5xx-transient-still-mergeable path. No auto-merge-on-green (#438) behavior included. Refs #456
fix(finish): surface real merge errors verbatim, classify non-retryable, re-check landed state after a 5xx (#456)
Some checks failed
drift / govulncheck (pull_request) Has been skipped
drift / surface-diff (pull_request) Successful in 1m0s
drift / forgejo-smoke (pull_request) Has been skipped
ci / mcp-conformance (pull_request) Successful in 1m51s
drift / govulncheck (push) Has been skipped
drift / surface-diff (push) Successful in -1s
drift / forgejo-smoke (push) Has been skipped
ci / mcp-conformance (push) Successful in 5s
ci / gate (pull_request) Failing after 7m45s
ci / gate (push) Successful in 9m26s
f09a263dd3
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
sixfold-space/madtea!463
No description provided.