Filed unassigned by the domain:devx os-dev seat while landing #9755. Recording, not claiming — it surfaced as the evidence for that card and is a different defect class, so it was left out of that PR.
⚠️There is a live consequence to triage first: issue #10240 is closed completed and is not fixed. It needs a human decision about reopening; this seat did not touch it (a state it did not set is another actor's, per the standing rule).
Dedup-searched over the open issues (partof closing keyword, auto-close, silently closed card, negation): no card covers this.
What happened
PR #10241 merged 2026-08-20T15:10:06Z. Its body contains no Part of anywhere, and contains this sentence:
Filed, not fixed: #10240 — the same leak through the **delete** verb.
Issue #10240 closed as completed at 15:10:08Z — two seconds later — and its closing-link summary names #10241 and nothing else. The author wrote the sentence to record that the card was deliberately left open, and the sentence closed it.
#10240 is a genuine, unfixed defect (attachment tombstoning no-ops on a predicate delete). It is now marked completed, and a closed card reads as finished — the exact failure mode .github/workflows/partof-closing-keyword-guard.yml was built to stop.
Why the existing guard did not fire
That workflow's header states the rule in fully general terms:
GitHub's closing-keyword parser matches the keyword plus the number and ignores the surrounding prose entirely — negations and modals included — so the sentence an author writes to PREVENT an auto-close is exactly what performs it on merge.
That is a precise description of what happened here. But the predicate behind it, h7PartOfWithClosingKeyword in scripts/pm/check-half-states.mjs, is bound per issue number to a Part of #N declaration: it fires only when the same #N carries both Part of and a closing keyword. #10241 declared Part of for nothing, so H7 was silent by construction.
So the guard covers one spelling of "this PR does not close that card" (Part of #N) while its rationale covers the whole class. Filed, not fixed: #N, does not close #N, #N remains open and out of scope: closes #N are all in the class and none are in the trigger.
Worth noting the guard's own advice inherits the gap: AGENTS.md and the os-dev contract tell authors to write #N is not addressed here / out of scope: #N / #N remains open — the first and third are safe (no keyword adjacent to the number), but the general instruction "keep closing keywords away from other card numbers" is exactly what a busy author gets wrong, and nothing mechanical catches it outside the Part of shape.
Suggested shape — and the reason it needs judgement, not a quick widening
The naive widening (flag any closing keyword bound to a #N the PR does not intend to close) is not available: a PR cannot declare which cards it intends to close except by using the keyword, so the guard would have no way to separate the intent from the accident.
Two candidates worth costing against each other:
- Negation-window detection. Flag a closing keyword whose match is preceded within the same sentence by a negation or filing marker (
not, no longer, filed, out of scope, rather than, instead of). Cheap, and the false-positive direction is a red on a PR that has to reword one sentence. Needs a corpus measurement of how often that fires on merged bodies before it can block. - Declared-closes manifest. The PR states its closing targets in one machine-read place and the guard fails when the body's keyword set and that set disagree —
declared = enforced, which is the shape this repo prefers, at the cost of a new thing every PR must carry.
Either way the specimen above is the regression fixture: it is real, it is dated, and it is the first known instance of this class actually costing a card.
Ref: #9755 · #10240 · #10241 · #8293
Filed unassigned by the
domain:devxos-dev seat while landing #9755. Recording, not claiming — it surfaced as the evidence for that card and is a different defect class, so it was left out of that PR.completedand is not fixed. It needs a human decision about reopening; this seat did not touch it (a state it did not set is another actor's, per the standing rule).Dedup-searched over the open issues (
partof closing keyword,auto-close,silently closed card,negation): no card covers this.What happened
PR #10241 merged 2026-08-20T15:10:06Z. Its body contains no
Part ofanywhere, and contains this sentence:Issue #10240 closed as
completedat 15:10:08Z — two seconds later — and its closing-link summary names #10241 and nothing else. The author wrote the sentence to record that the card was deliberately left open, and the sentence closed it.#10240 is a genuine, unfixed defect (attachment tombstoning no-ops on a predicate delete). It is now marked completed, and a closed card reads as finished — the exact failure mode
.github/workflows/partof-closing-keyword-guard.ymlwas built to stop.Why the existing guard did not fire
That workflow's header states the rule in fully general terms:
That is a precise description of what happened here. But the predicate behind it,
h7PartOfWithClosingKeywordinscripts/pm/check-half-states.mjs, is bound per issue number to aPart of #Ndeclaration: it fires only when the same#Ncarries bothPart ofand a closing keyword. #10241 declaredPart offor nothing, so H7 was silent by construction.So the guard covers one spelling of "this PR does not close that card" (
Part of #N) while its rationale covers the whole class.Filed, not fixed: #N,does not close #N,#N remains openandout of scope: closes #Nare all in the class and none are in the trigger.Worth noting the guard's own advice inherits the gap: AGENTS.md and the os-dev contract tell authors to write
#N is not addressed here/out of scope: #N/#N remains open— the first and third are safe (no keyword adjacent to the number), but the general instruction "keep closing keywords away from other card numbers" is exactly what a busy author gets wrong, and nothing mechanical catches it outside thePart ofshape.Suggested shape — and the reason it needs judgement, not a quick widening
The naive widening (flag any closing keyword bound to a
#Nthe PR does not intend to close) is not available: a PR cannot declare which cards it intends to close except by using the keyword, so the guard would have no way to separate the intent from the accident.Two candidates worth costing against each other:
not,no longer,filed,out of scope,rather than,instead of). Cheap, and the false-positive direction is a red on a PR that has to reword one sentence. Needs a corpus measurement of how often that fires on merged bodies before it can block.declared = enforced, which is the shape this repo prefers, at the cost of a new thing every PR must carry.Either way the specimen above is the regression fixture: it is real, it is dated, and it is the first known instance of this class actually costing a card.
Ref: #9755 · #10240 · #10241 · #8293