From 3e78376c45490385d3ac381b996eba9bb5ef3d10 Mon Sep 17 00:00:00 2001 From: operator Date: Thu, 20 Aug 2026 00:13:03 +0900 Subject: [PATCH] `gate` pinned its own name and left the other half of the pair unpinned Branch protection requires two contexts, `gate` and `lint`, and a required context is a check-run NAME. GitHub takes that name from an explicit `name:` when a job has one and from the job id otherwise. `gate` pins its name. `lint` did not, and the asymmetry was mine. A peer reading this reported the hole as renaming the job id, which is caught: renaming `lint` to `linting` fails two tests, one here and one in `action-lint.test.ts`, because both assert `jobs.lint` exists. Checked by making the rename rather than by reading the assertions. The hole is the other direction. ADDING `name: pr-lint` while leaving the job id `lint` breaks the required context exactly as thoroughly -- the check-run becomes `pr-lint`, the context named `lint` never reports, and every pull request blocks with no failing check to explain it -- and the whole suite passed with that edit in place. A job called `lint` still sits in the file, which is what both existing assertions look at. This is the shape that keeps turning up: the name written in the required list and the place that emits it are different places, and only one of them was guarded. Record-Id: r-lintcontextname Provenance: authored Certainty: firm Blast: local Undo: easy Ruled-out: asserting the name equals "lint" | the job has no `name:` today and should not gain one, so the property is its absence; asserting equality would invite someone to add `name: lint`, which is redundant and one edit from wrong Ruled-out: leaving it because renaming the id is already caught | that catches one of the two ways the context can stop reporting, and the uncaught one leaves a file that still reads correctly Limit: this pins where the name comes from, not that the check ever runs. A workflow-level failure before job dispatch produces no check run at all, and neither this nor the trigger assertion sees that Verified: the hole was reproduced before it was closed -- `name: pr-lint` added to the job left all 35 assertions in both files passing. The peer's reported version was tested too and does not reproduce: renaming the job id fails two assertions. One negative control on the fix, restored with the workflow byte-identical CommitLore-Version: 2.0.0 --- test/ci-gate.test.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/ci-gate.test.ts b/test/ci-gate.test.ts index 2fba6298..840c172c 100644 --- a/test/ci-gate.test.ts +++ b/test/ci-gate.test.ts @@ -91,6 +91,17 @@ describe('the CI fan-in gate', () => { // version bump renames it. const demo = workflow('demo-lint.yml'); expect(demo.jobs.lint, 'demo-lint.yml has no lint job').toBeDefined(); + // The required context is the check-run NAME, which GitHub takes from an + // explicit `name:` when there is one and from the job id otherwise. Renaming + // the id is caught here and in action-lint.test.ts; ADDING a `name:` was + // not, and it breaks the context exactly as thoroughly -- `lint` would stop + // reporting while a job called `lint` still sits in the file. `gate` pins + // its name above; this is the same pin, which was missing on the other half + // of the required pair. + expect( + demo.jobs.lint?.name, + 'lint must not carry an explicit name: the required context is derived from the job id', + ).toBeUndefined(); for (const [file, id] of [['ci.yml', 'gate'], ['demo-lint.yml', 'lint']] as const) { const body = readFileSync(join(REPO_ROOT, '.github/workflows', file), 'utf8'); const job = body.slice(body.indexOf(` ${id}:`));