fix(webhook): dispatch address-review skill when re-firing assignee on a PR with outstanding REQUEST_CHANGES #270
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#270
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?
Bug
When an issue with an open linked PR gets re-dispatched via
issues.assigned(operator bounce, dependency propagator, label re-add),handleIssueAssignedroutes the task through theimplementskill — even when the PR carries an outstandingREQUEST_CHANGESreview that's the actual blocker.The implement skill runs "build the feature", sees PR already exists + CI green + tests pass, concludes
work complete — QA green across the board, and exits without reading the review comment. Head sha doesn't change. Reviewer doesn't re-review (no new commit to look at). The issue sits inIN_REVIEWforever while dev keeps getting pinged and saying "done already".Repro — 2026-04-21 / 2026-04-22, issue #262 / PR #266
issues.assigned→ implement skill → opens PR #266 at sha21ace1df.c07b5d7d.REQUEST_CHANGESwith 3 findings (missing last-modified timestamp, etc.).pull_request_review.submittedwithstate=changes_requested→ address-review skill → taskbcedc120. Did not settle before operator intervention.dependent_unblockedre-fireshandleIssueAssignedon #262 → implement skill → task01964658, 7 turns, $0.18. Reads issue body, lists PRs, notices PR #266 exists, runs QA, concludes "work complete".issues.assigned→ implement skill → taskd98aa129, 15 turns, $0.35. Same pattern: reads issue, runs QA, declares victory.Net effect: PR head sha stays at
c07b5d7d(the sha the reviewer already rejected). REQUEST_CHANGES stands. Dev keeps getting pinged, keeps saying "done".Acceptance criteria
Routing fix
apps/server/src/webhook-handlers.ts::dispatchIssueForAgent(or the equivalent assignment-webhook entry point) looks up the issue's linked PR before picking a skill. If:openREQUEST_CHANGESREQUEST_CHANGESreview was submitted againstthen the dispatcher picks the
address-reviewskill and setstask.branchto the PR's head ref (so the worker checks outdev/262, notdev/Nderived from issue number).Same check applies to the propagator's
dependent_unblockedre-fire path — it currently callshandleIssueAssignedunconditionally.Skill side — defensive
skills/implement.mdgets a guard at the top: "Before executing, callmcp__forgejo__list_repo_pull_requestsscoped to this issue. If a PR exists with an outstandingREQUEST_CHANGESreview, STOP and post a comment🛑 implement skill misrouted — this issue has PR #N awaiting review changes; operator should re-dispatch address-review. Do not run QA and claim completion."Verification
apps/server/src/webhook-handlers.test.ts— synthesize an issue + linked PR with aREQUEST_CHANGESreview; assertdispatchIssueForAgentpicksaddress-review.open+ no review →implement; PRopen+APPROVED→implement(idempotent); PRopen+REQUEST_CHANGESon same sha →address-review; PRopen+REQUEST_CHANGESon stale sha (author already pushed a response) →implement.dependent_unblockedon an issue with outstanding REQUEST_CHANGES PR; assertaddress-reviewdispatched, notimplement.Out of scope
issues.assigned+handleIssueAssigned; only the skill pick inside that handler changes.address-review.mditself — the skill works when invoked; the bug is that it isn't.References
apps/server/src/webhook-handlers.ts::handleIssueAssigned+dispatchIssueForAgent.apps/server/src/deps.ts::propagateDependencyClosure— theassigned + blockers clearbranch re-fireshandleIssueAssigned.skills/implement.mdvs.skills/address-review.md.c07b5d7d.stale_idle_assignedrule — would flag this, but doesn't diagnose the cause.