Triage
Confirmed. This is a bug in T3's Forgejo/Gitea provider, and there is no existing issue for it.
Forgejo does not apply the repository setting inside the merge call. MergePullRequestForm.DeleteBranchAfterMerge is a plain bool, so an omitted delete_branch_after_merge is false, and POST /repos/{owner}/{repo}/pulls/{index}/merge deletes the head branch only when that flag is true. The web UI is what honors the setting: the merge form is initialized from defaultDeleteBranchAfterMerge on the pull-request unit. Gitea’s merge API behaves the same way, and this provider serves both (fj and tea).
ForgejoPullRequestProvider.runAction sends only { Do: input.mergeMethod ?? "merge" } for a merge, and ForgejoRepository never decodes default_delete_branch_after_merge. A merge from T3 therefore always leaves the source branch, even when the repository default is on. GitHub is different: it applies delete_branch_on_merge on the server, so the GitHub provider does not need to send a flag.
Reading the target repository and passing delete_branch_after_merge is the right fix. getRepo already calls GET /repos/{owner}/{repo}; treat a missing field as false. Two constraints for the implementation:
- If the flag is true and deletion fails, Forgejo still completes the merge and then returns an error (403 when the head branch is protected, is the default branch, or the merger cannot write to the head repo, which includes many forks).
write()treats that as a failed action, so the UI can report a failed merge after the pull request has already landed. The web UI only offers the checkbox when the head branch is deletable. Sending the repository default unconditionally will hit that path. - Refusing to merge when that settings read fails avoids silently ignoring an enabled setting. That failure should stay distinct from a merge rejection, so a transient
GETdoes not look like the host refused the merge.