Containers: bake the patched forgejo-mcp into the image (merge_pull_request silent-success bug regressed in container mode) #32
Labels
No labels
area:agents
area:dashboard
area:database
area:design
area:design-review
area:flows
area:infra
area:meta
area:security
area:sessions
area:webhook
area:workdir
security
type:bug
type:chore
type:meta
type:user-story
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/claude-hooks#32
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
User story
As an operator running workers in container mode, I want the image
to ship a
forgejo-mcpbinary that surfaces real merge failuresinstead of silently returning
"Pull request merged successfully"onnon-200 responses — so boss's
merge_pull_requestcalls fail loudlywhen the server rejects, and the follow-up can kick in.
Background
The upstream
goern/forgejo-mcpv2.17.0 (what the Dockerfile currentlypulls) has a bug in
MergePullRequestFn: it discards theboolsuccess flag from the SDK and checks only
err, which is nil for anyHTTP response the SDK managed to parse (including 4xx / 5xx rejects).
Patched locally in-session (track record:
update_issueassignee andmerge_pull_requestboth fixed, built at/tmp/forgejo-mcp, installedto
~/.local/bin/forgejo-mcp).When workers ran on the host, PATH put
~/.local/binbefore/usr/binand the patched binary shadowed the pacman install. In container mode
there is no such shadowing: the image bakes
/usr/local/bin/forgejo-mcpat build time (
curlfrom the upstream release), and that is the onlybinary the in-container Claude CLI sees.
Observed 2026-04-17 on PR #31 (the #29 follow-up): boss claimed
"squash-merged successfully, branch dev/29 deleted" in the dashboard
event stream, but the PR was still open and the branch still present.
Forgejo was in fact returning HTTP 500 on the squash call (separate
issue — see Related below) and the buggy MCP swallowed it.
Recovered by
docker cp'ing the patched binary into each runningcontainer and completing the merge manually via REST. That recovery is
ephemeral — survives until the next
just containers-rebuild.Acceptance criteria
Dockerfile
forgejo-mcpbuild step must produce a binary that returnsan error when the server rejects a merge, not
"Pull request merged successfully".first release that carries it.
the patch (diff, not a blob) to this repo under
patches/andapply it during the Dockerfile build, then
go build.update_issue's droppedassignee— the upstreamcode comments out the assignee write ("assignee is not supported
in the current SDK" — the comment is wrong, the SDK has the
field). Both fixes in one binary.
Verification
docker run --rm claude-hooks:<tag> forgejo-mcp --versionreports a version string that makes it clear this is the patched
build (e.g. a dev suffix, or a fork's version).
a PR with a required-check still pending) and confirm the MCP
returns an error string, not the success string.
dockerfilejob inqa.ymlcontinues to pass thestatic audit (no new credential-leak patterns).
Docs
README.md(or wherever the container storylives) explaining why the image carries a patched binary, with a
pointer to the upstream issue / PR.
Out of scope
was closed via a
merge-style commit as a workaround. Separate bug,separate story if it's worth chasing (possibly commit signing
related — the PR page flagged "This instance has no key to sign
this commit with").
merge.mdskill's fallback strategy when squash 500s. If theserver-side squash issue is persistent,
merge.mdcould trysquashfirst, then fall back tomergewith a note. Separatestory.
References
writing, patched locally at
/tmp/forgejo-mcp)operation/pull/pull.go:MergePullRequestFn(check the
boolreturn fromMergePullRequest),operation/issue/issue.go:UpdateIssueFn(wire theassigneearginto
opt.Assignees)Dependencies
close
main