Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion .github/workflows/cross-repo-issue-closer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,35 @@ jobs:
// GitHub's own keyword set, restricted to the qualified
// `owner/repo#N` form — the bare `#N` form already works natively
// and must not be touched here.
//
// The `:?` is load-bearing, and MEASURED rather than inherited from
// the sibling file's comment (#9755). GitHub's own parser accepts
// the colon, so `Fixes: <ref>` is a real closing declaration and not
// a typo: PR #10241 merged 2026-08-20T15:10:06Z carrying the
// sentence `Filed, not fixed: #10240`, and GitHub closed #10240 as
// `completed` at 15:10:08Z — two seconds later, with that issue's
// own `closed_by_pull_requests` naming #10241 and nothing else. The
// negation in the prose changed nothing, which is the same property
// partof-closing-keyword-guard.yml is built on.
//
// Without the `:?` this job read `Fixes: objectstack-ai/objectui#456`
// as `No cross-repository closing keywords in this PR body.` and
// took exit path 1 — the quiet green that is indistinguishable from
// the ~2300 runs with genuinely nothing to do. A foreign issue is
// then lost with no notice on the PR and no annotation anywhere,
// which is the exact silent-path shape this file's card family
// (#9373 · #9424 · #9575 · #9595 · #9643 · #9711) has been closing
// one exit at a time.
//
// duplicate-fix-guard.yml spells this same keyword-and-separator
// prefix, and `scripts/check-closing-keyword-parity.mjs` goes red if
// the two ever stop agreeing. NEITHER workflow checks this repo out
// — this one deliberately (`pull_request_target`), the guard because
// it has no checkout step at all — so they cannot import a shared
// module. That gate is what stands in for the import.
const KEYWORDS = 'close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved';
const pattern = new RegExp(
`\\b(?:${KEYWORDS})\\s+([\\w.-]+)\\/([\\w.-]+)#(\\d+)\\b`,
`\\b(?:${KEYWORDS}):?\\s+([\\w.-]+)\\/([\\w.-]+)#(\\d+)\\b`,
'gi',
);

Expand Down
28 changes: 28 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1110,6 +1110,34 @@ jobs:
node scripts/check-cross-repo-closer-outcome.mjs --self-test
node scripts/check-cross-repo-closer-outcome.mjs

# Closing-keyword parser parity (#9755). THREE files parse GitHub's
# closing-keyword grammar out of user-authored markdown — the cross-repo
# closer, the duplicate-fix guard, and H7's `closingKeywordRe()` behind
# partof-closing-keyword-guard.yml — and they had drifted: the guard
# accepted `Fixes: <ref>` and the closer silently did not, so a merged PR
# written with the colon took the closer's quiet green exit and lost its
# foreign issue.
#
# A shared module is the right shape and is unavailable: neither workflow
# checks the repo out (the closer deliberately, on `pull_request_target`;
# the guard because its whole job is one github-script step), so
# `require()` resolves to nothing in either. This gate is what stands in
# for the import — it extracts all three parsers from the shipped bytes
# and asserts they agree BEHAVIOURALLY on the keyword set and the
# separator, while still refusing what each is scoped to refuse. It also
# sweeps all tracked files for a FOURTH parser, because "fix these two"
# is exactly the change that would miss one.
#
# Invoked as `node` rather than a `pnpm check:*` alias for the same reason
# as the step above: that alias belongs in root package.json, declared
# territory of the @changesets/cli v3 migration lane (#9465) while it
# runs. dispatch-gates.mjs derives gate families from either spelling.
# No network, no build; ~1 s, most of it the tracked-file sweep.
- name: Closing-keyword parser parity
run: |
node scripts/check-closing-keyword-parity.mjs --self-test
node scripts/check-closing-keyword-parity.mjs

# Merge-queue triage outcome contract (#10128, over the #4859 bot).
# merge-queue-triage.yml carries ~450 lines of inline github-script that
# only ever runs on a RED merge_group build — a condition nobody can
Expand Down
Loading
Loading