bug(gates): ac-closure-check does not see PR comments, inline review comments or the PR title, and Forgejo closes from all three #1506
Labels
No labels
bump
major
bump
minor
bump
patch
kind
bug
kind
chore
kind
docs
kind
epic
kind
feature
loop/paused
mirror-drift
priority
critical
priority
high
priority
low
priority
medium
size
L
size
M
size
S
size
XL
status
blocked
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
frankenbit/release-toolkit#1506
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?
rt ac-closure-checkgrades closing keywords in two places: the PR body and the commit messages. Forgejo also closes issues from PR comments, inline review comments, and the PR title. The gate reads none of those three. So a keyword typed into a comment closes its target on merge, unfinished acceptance criteria and all, and the check stays green.Measured
The incident: PR #1489 and issue #1485, 2026-10-02. The PR body said
Refs #1485. The commit message had no keyword. Comment 113711 saidclose #1485 by hand, as plain text in the comment itself. Here is #1485's timeline (GET /repos/frankenbit/release-toolkit/issues/1485/timeline), read back:The last
pull_requestevent (the body edit, 11:07:02) came before both comments. So even a gate that read comments onpull_requestevents would have graded this PR without seeing comment 113711.Re-verified on throwaway repos.
claude/ac-xref-probe-a-20261002and-b-are private, carry no workflows (.forgejo,.giteaand.github/workflowsall returned 404 before use), and havehas_actions=false(read back). Forgejo 15.0.7+gitea-1.22.0. Each target issue had one- [ ]box. The PR was rebase-merged by its author, who has write access. Every row below is the target's timeline, read back before and after the merge:closes #1(plain)comment_ref,closesCloses #4comment_ref,closesCloses #5Closes #6nullrowRefs #7neutered, thennonecloses claude/ac-xref-probe-a-20261002#8comment_ref,closesprobe: fix #9pull_ref,closescloses https://.../issues/10comment_ref,noneCloses claude/ac-xref-probe-a-20261002#11pull_ref,closescloses claude/ac-xref-probe-b-20261002#1comment_ref,closeson repo BForgejo source at the deployed tag (
v15.0.7).models/issues/issue_xref.go,Comment.AddCrossReferences: onlyCommentTypeCodeandCommentTypeCommentcreate cross-references. The comment text goes through the Markdown parser only, which strips code spans.ResolveCrossReferencesselects every row whoseref_issueis the PR and whoseref_actionisclosesorreopens. Rows are ordered byid, and the latest row per target wins.handleCloseCrossReferencesinservices/pull/merge.goapplies the result at merge.DeleteCommentandUpdateCommentboth neuter the comment's old references.services/convert/issue_comment.go,ToTimelineComment: returnsnil(a JSONnull) when any load fails, including when the referencing comment no longer exists.Two things this contradicts in the gate as it stands:
Live replay of the proposed reader against the merged #1489: the candidate
#1485has an effectiveclosesrow fromcomment_ref113711. It is a target, and it is undeclared. Cost on the three most-commented recent PRs (#1410, #1416, #1432): 22, 27 and 34 API calls, each under 1 s.Why it matters
The gate exists so that merging never closes an issue whose acceptance criteria are still open. A comment is the easiest place for a closing keyword to land by accident. Reviewers write "this fixes #N" in inline comments, and mergers write "close #N by hand". Neither is visible to the gate. #1485's boxes happened to be ticked. An issue with open boxes would have been closed the same way, behind a green check.
Acceptance criteria
idamongcloses/reopens) iscloses. The body and commit regex arms are unchanged, and the result is the union of all arms. — DONE @ #1560 (6a36090, reviewed as6a36090).readForgeArm,gates.ForgeCloseRef(TestForgeCloseRef); live replay of #1489 refuses #1485 via comment 113711.closesrow for it, and the gate defers to that. — DONE @ #1560 (6a36090, reviewed as6a36090).TestRunACClosureCheck_1506_InlineCodeInACommentIsNotATarget, with the keyword regex asserted to match as a precondition.Refs, plain keyword in a comment, ACs ticked. It fails as undeclared, and with an unticked box it also fails as unfinished. The refusal names the comment and the remedy: edit or delete it (measured to neuter), or wrap the keyword in inline code, or declare it inIntended-targets:. — DONE @ #1560 (6a36090, reviewed as6a36090).…_CommentReferenceIsATarget, plus toolkit-self fixture control PR 9:main's rt exits 0 on it and this rt exits 1.nulltimeline rows are skipped and counted in the output. They are not a decode error and not could-not-grade, because the one measured producer is a deleted comment, whose reference Forgejo neuters. — DONE @ #1560 (6a36090, reviewed as6a36090).…_NullRowsAreDisclosedNotFatalandTestListIssueTimeline_NullPageEndsTheWalkAndNullRowsAreCounted. The timeline's empty page is alsonull(measured), which the reader handles.page: measured, page 1, 2 and 3 return the same rows. A non-array response fails loud. — DONE @ #1560 (6a36090, reviewed as6a36090). One request per review, in the same shape #1496 gaveListIssueComments;TestListPRReviewComments_UnpaginatedRouteIsReadOnceAndTeamRequestsAreFine.6a36090, reviewed as6a36090). Review bodies were added to the disclosure in6a36090, after review 7272; pinned byTestRunACClosureCheck_ScopeDisclosed.owner/repo#Nas a non-closing form. — DONE @ #1560 (6a36090, reviewed as6a36090).…_RemedyTextIsNotItselfAClosingReference.6a36090, reviewed as6a36090). 12 of 12 mutations were killed, including removing the forge arm (#1489 replay red). The reviewer killed 6 more of their own..forgejosource and the regenerated.giteatwin) describe the new scope. No trigger change. The PR body records the trigger decision as a decision tree. — DONE @ #1560 (6a36090, reviewed as6a36090) for the trigger half: no trigger change, and the decision tree is in the PR body. The workflow-header half is RETIRED:0de3876removedreusable-ac-closure-check.ymland its twin before this landed. The scope text now lives indocs/integration.md'sac_closurebullet, andreusable-pr-gates.yml's one-line description stays true.changelog.dfragment. — DONE @ #1560 (6a36090, reviewed as6a36090).changelog.d/1506-ac-closure-forge-xrefs.fixed.md.ac-closure-checkgrades. If it is onmainwhen this lands, flip its "Graded" column to yes for each surface this fix reads (PR title, PR comments, inline review comments), and keep review bodies at no. — DONE @ #1560 (6a36090, reviewed as6a36090). #1498 was onmain, so the Graded column now says yes for the title, PR comments and inline review comments, and no for review bodies.Out of scope
pull_requestevent, which is the exact #1489 timing. Perservices/actions/commit_status.goatv15.0.7, a run triggered byissue_commentcreates no commit status, so its verdict cannot reach the PR's check. This is a source read, not a live probe: no runner serves the throwaway owner. Options and their trade-offs belong in the PR's decision tree and a follow-up.ListIssueCommentsre-reading the unpaginated/issues/{n}/commentsuntilMaxPages. This is a separate defect, found while probing.Added one acceptance criterion to this issue's body, at the request of review 7175 on #1498.
#1498 adds AGENTS.md section 15a, whose table says which close surfaces
ac-closure-checkgrades. It cites this issue's throwaway-repo rows for the PR title, inline review comments, review bodies and qualified references. When this fix lands, that table's "Graded" column has to flip for the surfaces the gate starts reading, and the new last box tracks that. No other text in the issue changed.Landed through #1560:
6a360903dc559026d5aa9a28282dfc513778c71f, fast-forward-merged by @alex on 2026-10-06. The landed commit is the reviewed head:merge_commit_shaequals the PR head andmain's tip.6a36090. Reviewer verdicts are reviews 7272 and 7273 (the latter at6a36090).mainhad 2 green and 11 still running. They were not awaited.0de3876removed that workflow.ListIssueCommentspaging defect was fixed by #1495/#1496.Closing by hand, because the PR used
Refs.