The wrapper keeps writing after the pull request head moves or the pull request is merged or closed #54
Loading…
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?
The wrapper checks the pull request head once before it writes, ignores whether the pull request is still open, and does not look at the state at all on event runs. It also reads the pull request diff after its first write. Comments can land on a pull request that was merged or closed, and on a head the review was not made for.
What happens
headMovedcompares only the head. It reads the pull request and returns onheadShaalone (src/wrapper/orchestrator.ts:70-79). The forge client already parsesstateandmerged(src/forge/types.ts:35-45), andassertDispatchablealready refuses a closed or merged pull request (src/wrapper/event-context.ts:74-88), but only at the start of a dispatch run. A dispatched pull request that is merged afterwards is still reconciled. Reproduced ass8-merged-mid-run: the pull request was merged right after the one validation read, and the run made two writes while the forge reported it merged.pull_request_targetbranch reads the payload and never looks atstateormerged(src/wrapper/event-context.ts:103-118). The wrapper's own workflow triggers onedited(.forgejo/workflows/review.yml:57-58), and the upstream Forgejo source raiseseditedfor a title or body change whatever the pull request's state. Reproduced ass8-event-merged: aneditedevent for a pull request that the payload and the forge both report closed and merged got a review and two forge writes.src/wrapper/orchestrator.ts:122-138), and a second precedes a re-triage (src/wrapper/orchestrator.ts:105-107). After that come service reads that the wait budgets at several minutes, and forge writes with no timeout. Only the inline review carriescommit_id(src/forge/client.ts:147-153). The summary edit, the thread replies and the resolutions carry no commit (src/forge/client.ts:154-160;s5b-inventorylists every write and which ones carry it). Reproduced ass5b-toctou: with the head moved right after the check was answered, a stand-in forge received four writes while its head differed from the asked one: the summary comment, the inline review, a reply and a resolution. This one is a limit rather than a bug. The forge offers no conditional write, so the window can be narrowed and not closed.src/reconcile/inline.ts:53). The read takes only the pull request number, so it returns whatever diff the pull request has at that moment (src/forge/client.ts:80-83). The file text is read at the asked head (src/reconcile/inline.ts:65), and the review is posted withcommit_idset to the asked head (src/reconcile/inline.ts:83). If the head moved in between, the comment positions come from the new head's diff and the commit from the old one. The positions and the commit then describe different heads, and a comment can sit on a line that means something else in the old commit. Reproduced asv-s5b-diff-order: the requests ran as head read (head1111…), summary write, diff read, review post withcommit_id1111…, while the forge head had been2222…since right after the first read. The stand-in serves one fixed diff, so this shows the order and thecommit_id, not a shifted line.What it should do instead
Make one check that a run must pass before writing: the head equals the asked head,
stateisopenandmergedisfalse. Run it on event runs as well as dispatch runs, and run it again before each of the three reconcile steps (summary, inline review, dispositions) and before the disposition loop, not once. Stop with the existinghead-movedoutcome, or a sibling for a closed pull request.Read the diff before the first write, so the diff, the file text and
commit_idcome from the same head, and check the head again immediately before the inline review is posted.None of this closes the window in item 3. The workflow's
cancel-in-progressgroup (.forgejo/workflows/review.yml:67-68) is what ends a superseded run today.Why it matters
A merged or closed pull request gets a new summary comment and inline threads that nobody will act on, and the run asks the service for a review of it first. A head that has moved gets thread replies and resolutions from a review of the old head, and inline comments whose positions and commit disagree. None of this has a security effect: every write is still a comment or a conversation resolution.
🤖 Generated with Claude Code