An issue whose closing PR was landed via gh pr merge --auto --squash could stay open indefinitely after the merge actually happened, with no error surfaced — easy to mistake for “nothing to close.”
close-issues-on-merge.yml gated the whole job on if: github.event.pull_request.merged == true, trusting the pull_request: types: [closed] webhook’s merged field at face value. gh pr merge --auto queues the squash-merge asynchronously — GitHub can deliver the closed webhook before the merge commit actually lands, with merged: false still in that snapshot. Measured 68s of lag between the closed event and the PR’s real mergedAt (krill-agents#55, observed via krill-oss#197’s copy of the same workflow). The job-level if: snapshot never re-checked, so the run silently no-oped on a PR that had, in fact, merged.
.github/workflows/close-issues-on-merge.yml: dropped the job-level if: github.event.pull_request.merged == true gate.actions/github-script step: if the webhook payload’s pull_request.merged is false, re-fetch the PR via github.rest.pulls.get up to 6 times, 15s apart, before concluding it closed without merging.krill-agents#55/#56 and mirrored into krill-oss — this repo’s copy (added via krill#827) had drifted behind that fix.server/src/jvmTest/kotlin/krill/zone/server/CloseIssuesOnMergeWorkflowTest.kt as a text-content regression guard on the workflow YAML (the workflow itself has no test harness) — mirrors the DocsSeoTest pattern for config/markup file assertions.merged/mergeable/similar derived-state field for an async operation (gh pr merge --auto, branch protection auto-merge) should treat that field as a snapshot, not ground truth — re-fetch before trusting a “false” reading if the action that would flip it true is known to be in flight.krill, krill-oss, and krill-agents (as this one is, per its own header comment), a fix landing in one copy needs an explicit tracker in the other two — this drift shipped silently until a QA filing (krill#853) caught it independently rather than via cross-repo propagation.