Filed unassigned by the domain:devx os-dev seat while landing #9643. Recording, not claiming — it came out of that card's H1/H4 audit of the per-target loop and is a different defect class (wrong target kind, not wrong target state), so it was left out of that PR rather than folded in.
Dedup-searched (workflow name, "pull request target", "closing keyword"): only #9575, #9595 and #9643 name this file, and none covers this.
The path
.github/workflows/cross-repo-issue-closer.yml, the per-target loop. The target regex accepts any qualified owner/repo#N:
constpattern=newRegExp(`\\b(?:${KEYWORDS})\\s+([\\w.-]+)\\/([\\w.-]+)#(\\d+)\\b`,'gi');N is just digits. Nothing downstream asks what kind of thing N is, and issues.get will not tell it unless asked: on GitHub every pull request is also an issue, so GET /repos/{owner}/{repo}/issues/{N} returns a pull request happily, with state, state_reason and all — plus a pull_request key that the loop never reads.
So a merged PR whose body says Fixes objectstack-ai/objectui#4500, where 4500 happens to be a pull request rather than an issue, makes this workflow comment on that foreign PR and then close it.
Why that is wrong rather than merely odd
GitHub's own closing-keyword parser — the thing this workflow exists to emulate across repository boundaries — does not close pull requests. Fixes #N against a PR number creates a reference and nothing else. This workflow's whole warrant is "do across repos what GitHub already does within one"; closing a foreign PR is a power the behaviour it emulates does not have, exercised with a credential scoped for issue hygiene.
The blast radius is the asymmetric part. A wrongly-closed issue is reopened by anyone who notices. A wrongly-closed PR loses its merge-queue membership and any armed auto-merge in the same step, and neither returns by itself — the same one-way property AGENTS.md §7 records for the draft flip.
Severity, stated honestly
Low, and the reason is measured rather than assumed: over the 1176 most recently merged PRs there are zero qualified foreign closing keywords of any kind (re-measured twice on 2026-08-18, two different windows, both zero), so this branch has never had a target at all. It is cheap to get wrong and cheap to get right.
The fix, if it is wanted
One condition in the loop, next to the state/state_reason conditions #9643 added:
if(issue.pull_request){/* announce and skip -- this is a PR, not an issue */}plus a scenario in scripts/check-cross-repo-closer-outcome.mjs (the doubles already model issues.get, so it is a fixture field and one assertion) and a matching mutation in --self-test. Whether the skip should be quiet, a core.warning, or red is the only judgement in it — a PR body naming a foreign PR with a closing keyword is more likely an authoring mistake than a deliberate instruction, which argues for at least an annotation.
Ref: #9643 · #9595 · #9575
Generated by Claude Code
Filed unassigned by the
domain:devxos-dev seat while landing #9643. Recording, not claiming — it came out of that card's H1/H4 audit of the per-target loop and is a different defect class (wrong target kind, not wrong target state), so it was left out of that PR rather than folded in.Dedup-searched (workflow name, "pull request target", "closing keyword"): only #9575, #9595 and #9643 name this file, and none covers this.
The path
.github/workflows/cross-repo-issue-closer.yml, the per-target loop. The target regex accepts any qualifiedowner/repo#N:Nis just digits. Nothing downstream asks what kind of thingNis, andissues.getwill not tell it unless asked: on GitHub every pull request is also an issue, soGET /repos/{owner}/{repo}/issues/{N}returns a pull request happily, withstate,state_reasonand all — plus apull_requestkey that the loop never reads.So a merged PR whose body says
Fixes objectstack-ai/objectui#4500, where 4500 happens to be a pull request rather than an issue, makes this workflow comment on that foreign PR and then close it.Why that is wrong rather than merely odd
GitHub's own closing-keyword parser — the thing this workflow exists to emulate across repository boundaries — does not close pull requests.
Fixes #Nagainst a PR number creates a reference and nothing else. This workflow's whole warrant is "do across repos what GitHub already does within one"; closing a foreign PR is a power the behaviour it emulates does not have, exercised with a credential scoped for issue hygiene.The blast radius is the asymmetric part. A wrongly-closed issue is reopened by anyone who notices. A wrongly-closed PR loses its merge-queue membership and any armed auto-merge in the same step, and neither returns by itself — the same one-way property AGENTS.md §7 records for the draft flip.
Severity, stated honestly
Low, and the reason is measured rather than assumed: over the 1176 most recently merged PRs there are zero qualified foreign closing keywords of any kind (re-measured twice on 2026-08-18, two different windows, both zero), so this branch has never had a target at all. It is cheap to get wrong and cheap to get right.
The fix, if it is wanted
One condition in the loop, next to the
state/state_reasonconditions #9643 added:plus a scenario in
scripts/check-cross-repo-closer-outcome.mjs(the doubles already modelissues.get, so it is a fixture field and one assertion) and a matching mutation in--self-test. Whether the skip should be quiet, acore.warning, or red is the only judgement in it — a PR body naming a foreign PR with a closing keyword is more likely an authoring mistake than a deliberate instruction, which argues for at least an annotation.Ref: #9643 · #9595 · #9575
Generated by Claude Code