Skip to content

Commit d8e7844

Browse files
committed
docs(ci): state the gate-invocation idiom once, and scope the #9465 fence correctly
lint.yml asserted at nineteen separate sites that root `package.json` is declared territory of the @changesets/cli v3 migration lane, and that this is why most gate steps are invoked as `node scripts/...` rather than through a `pnpm check:*` alias. The second half is a real and legitimate idiom; the first half is false. The lane's declared territory in that file is scoped by its own parenthetical, verbatim from the epic's body: root `package.json` (the `@changesets/cli` range and the `version` script) That parenthetical is scoping, not illustrative: one dependency range and one script key. It does not cover the file, and it does not cover a new `check:*` key -- so an alias would have been allowed all along. The direct form is still correct here, on its own merits (local precedent; dispatch-gates.mjs derives gate families from either spelling; three gates pin the direct wiring in their own self-tests), which is why no step is rewired. The remedy is one canonical statement placed above `jobs:`, where both jobs can point at it, with all nineteen sites reduced to a pointer. Nineteen corrected copies would re-arm the mechanism this corrects: the over-broad reading spreads by copying, measured at 15 sites when the finding was filed and 19 three days later, four of those added afterwards. Prose-only: every changed line is a comment, and all 183 steps survive identical on name/run/uses.
1 parent 50369dc commit d8e7844

1 file changed

Lines changed: 95 additions & 76 deletions

File tree

.github/workflows/lint.yml

Lines changed: 95 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,54 @@ concurrency:
1919
group: lint-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
2020
cancel-in-progress: true
2121

22+
# ─────────────────────────────────────────────────────────────────────────────
23+
# ⭐ GATE INVOCATION IDIOM — stated ONCE, here. The steps below point at it.
24+
#
25+
# Most gate steps in this file run their script directly:
26+
#
27+
# run: node scripts/check-<thing>.mjs
28+
#
29+
# rather than through a `pnpm check:*` alias. That is a deliberate in-repo
30+
# idiom with its own justification, not a workaround:
31+
#
32+
# * Many gate steps in this file already use it — it is the local precedent.
33+
# * `scripts/pm/dispatch-gates.mjs` derives gate families from EITHER
34+
# spelling, so the direct form loses no discovery.
35+
# * It adds no key to the root manifest.
36+
# * It is pinned rather than incidental: check-aggregator-roster,
37+
# check-ci-filter-parity and check-doc-frontmatter each assert in their own
38+
# self-test that `node <self>` appears in THIS file, so silently rewiring
39+
# one of them to an alias reddens its gate.
40+
#
41+
# ⚠️ It is NOT because root `package.json` is off limits. This file used to say
42+
# that, in one shape or another, at nineteen separate sites, and it is FALSE.
43+
# The declared territory of the #9465 @changesets/cli v3 lane inside that file
44+
# is scoped by the lane's own parenthetical, verbatim from the issue body:
45+
#
46+
# root `package.json` (the `@changesets/cli` range and the `version` script)
47+
#
48+
# That parenthetical is SCOPING, not illustrative: one dependency range and one
49+
# script key. It does not cover the file, and it does not cover a new `check:*`
50+
# key — so adding `check:<thing>` to the root manifest would have been allowed
51+
# all along. The steps below take the direct form on the merits listed above;
52+
# they are not fenced out of the alternative.
53+
#
54+
# Stated once here, and POINTED AT rather than restated, because the over-broad
55+
# reading propagates by copying — measured, not supposed. A dev agent wrote the
56+
# same sentence into a new comment and a new script's docblock and said plainly:
57+
# "I copied the phrasing from neighbouring comments instead of checking it."
58+
# The count was 15 sites when #10894 was filed on 2026-08-21 and 19 by
59+
# 2026-08-24, four of them added AFTER the card was filed and two of those
60+
# within ninety minutes of it being picked up. Nineteen CORRECTED copies would
61+
# re-arm the identical mechanism: a claim that reads as verified because it
62+
# reads exactly like the verified ones beside it. So there is one statement of
63+
# this idiom — this one — and every step refers to it.
64+
#
65+
# Changing this note? It is prose, and no gate can red on a wrong sentence
66+
# here; that is precisely how nineteen wrong ones accumulated. Check any claim
67+
# about the #9465 fence against #9465's own body before you write it down.
68+
# ─────────────────────────────────────────────────────────────────────────────
69+
2270
jobs:
2371

2472
lint:
@@ -176,11 +224,8 @@ jobs:
176224
#
177225
# What runs here is the self-tests, not the modules' callers: the callers
178226
# are the two gate steps below, which already run on every PR.
179-
# Invoked as `node` rather than through `pnpm check:*`: those aliases
180-
# belong in root package.json, declared territory of the @changesets/cli
181-
# v3 migration lane (#9465) while it runs — same shape as the
182-
# `node scripts/…` steps later in this lane. dispatch-gates.mjs derives
183-
# gate families from either spelling.
227+
# Invoked as `node` rather than through `pnpm check:*`: see the GATE
228+
# INVOCATION IDIOM note at the top of this file.
184229
# `ts-parse` spawns ~20 real node children (~10 s measured, and the spawns
185230
# are the point — they pin that a caller's try/catch cannot swallow the
186231
# refusal); the other two are in-process fixtures, ~0.5 s combined.
@@ -297,9 +342,9 @@ jobs:
297342
# contract (scripts/check-required-contexts.mjs) that only a
298343
# maintainer-present Settings change can enrol, and an unenrolled job is
299344
# an advisory gate — #5617 verbatim. This job is already required.
300-
# Invoked as `node` rather than `pnpm check:*` for the same reason as the
301-
# other `node scripts/…` steps in this lane: that alias is a line in root
302-
# package.json, inside the @changesets/cli v3 fence (#9465).
345+
# Invoked as `node` rather than `pnpm check:*`, same as the other
346+
# `node scripts/…` steps in this lane: see the GATE INVOCATION IDIOM note
347+
# at the top of this file.
303348
# Runs its own comparator self-test first — 12 cases proving the
304349
# comparison can still REPORT, because "0 files disagree" and "the
305350
# comparison is broken" otherwise print the same line.
@@ -547,11 +592,9 @@ jobs:
547592
# from this step reddens instead of going quiet. Temp-dir fixtures, no
548593
# network, ~1.7 s.
549594
#
550-
# Invoked as `node` rather than through a `pnpm check:*` alias: that alias
551-
# belongs in root package.json, which is declared territory of the
552-
# @changesets/cli v3 migration lane (#9465) while it runs. Same shape as
553-
# check-links.yml's ADR-link step, and dispatch-gates.mjs derives gate
554-
# families from either spelling.
595+
# Invoked as `node` rather than through a `pnpm check:*` alias: see the
596+
# GATE INVOCATION IDIOM note at the top of this file. Same shape as
597+
# check-links.yml's ADR-link step.
555598
- name: Release-rehearsal clone preflight self-test
556599
run: node scripts/pm/release-rehearsal-clone.mjs --self-test
557600

@@ -586,9 +629,8 @@ jobs:
586629
# until now NOTHING in this repo ran, and an unrun self-test is a phantom
587630
# check — the AGENTS.md rule about `@ts-expect-error` in an uncompiled
588631
# file, wearing a different hat. Invoked as `node`/`bash` rather than
589-
# through `pnpm check:*`: that alias belongs in root package.json, which
590-
# is declared territory of the @changesets/cli v3 migration lane (#9465)
591-
# while it runs. Temp-dir fixtures, no network, ~6 s in total.
632+
# through `pnpm check:*`: see the GATE INVOCATION IDIOM note at the top of
633+
# this file. Temp-dir fixtures, no network, ~6 s in total.
592634
#
593635
# (`check-governed-merges.mjs`'s own cases run in the
594636
# `pnpm check:pm-governed-merges` step above, which is already its
@@ -685,19 +727,9 @@ jobs:
685727
# commands executing when the first fails, 3 of 3 when none does. A harness
686728
# that cannot reproduce the defect cannot certify the fix.
687729
#
688-
# Invoked as `node scripts/…` rather than through a `pnpm check:*` alias,
689-
# on the precedent this job already sets: several gate steps here are
690-
# invoked directly, and dispatch-gates.mjs derives gate families from
691-
# either spelling, so the direct form loses no discovery and adds no key
692-
# to the root manifest.
693-
#
694-
# ⚠️ NOT because root package.json is off limits. The #9465 changeset lane
695-
# fences that file's `@changesets/cli` range and its `version` script — the
696-
# parenthetical in the issue body is scoping, not illustrative — and not
697-
# the file, so a `check:step-collectors` key would have been allowed. Said
698-
# plainly because the over-broad reading is easy to acquire and then
699-
# propagates as a constraint nobody actually has, which is this step's own
700-
# defect class wearing a different hat: a claim that reads as verified.
730+
# Invoked as `node scripts/…` rather than through a `pnpm check:*` alias:
731+
# see the GATE INVOCATION IDIOM note at the top of this file, which is
732+
# where the correction this comment used to carry now lives in full.
701733
# Temp-dir fixtures, no network, ~1 s.
702734
- name: Step-collector gate (self-tests that mask each other)
703735
run: |
@@ -918,10 +950,9 @@ jobs:
918950
# naming what could not be read (#4690 / #7484).
919951
#
920952
# Invoked as `node` rather than through a `pnpm check:*` alias, same as
921-
# the aggregator-roster step below: that alias belongs in root
922-
# package.json, declared territory of the @changesets/cli v3 migration
923-
# lane (#9465) while it runs. Dependency-free filesystem read, sub-second
924-
# over the whole corpus.
953+
# the aggregator-roster step below: see the GATE INVOCATION IDIOM note at
954+
# the top of this file. Dependency-free filesystem read, sub-second over
955+
# the whole corpus.
925956
- name: Docs frontmatter parses
926957
run: |
927958
node scripts/check-doc-frontmatter.mjs --self-test
@@ -1009,8 +1040,8 @@ jobs:
10091040
# failing step here is read by everyone — and enforcement later needs
10101041
# only the flag flip, not a Settings change.
10111042
# Invoked as `node` rather than a `pnpm check:*` alias, same as the
1012-
# frontmatter step above: the alias is a line in root package.json,
1013-
# inside the @changesets/cli v3 fence (#9465) while that lane runs.
1043+
# frontmatter step above: see the GATE INVOCATION IDIOM note at the top of
1044+
# this file.
10141045
- name: Route spellings taught in prose match the ledgers (advisory)
10151046
run: |
10161047
node scripts/check-doc-route-spelling.mjs --self-test
@@ -1074,10 +1105,9 @@ jobs:
10741105
# re-narrowing the scope REFUSES instead of reporting a clean corpus.
10751106
#
10761107
# Invoked as `node` rather than through a `pnpm check:*` alias, same as the
1077-
# doc-frontmatter and aggregator-roster steps: that alias belongs in root
1078-
# package.json, declared territory of the @changesets/cli v3 migration lane
1079-
# (#9465) while it runs. Dependency-free filesystem read, sub-second over
1080-
# the whole corpus.
1108+
# doc-frontmatter and aggregator-roster steps: see the GATE INVOCATION
1109+
# IDIOM note at the top of this file. Dependency-free filesystem read,
1110+
# sub-second over the whole corpus.
10811111
#
10821112
# Its --self-test runs FIRST and is where the rule is observed FAILING:
10831113
# this PR swept the live corpus, so a passing run over real data cannot
@@ -1361,10 +1391,8 @@ jobs:
13611391
# them — the #9604 precedent, re-measured.
13621392
#
13631393
# Invoked as `node scripts/…` rather than through a `pnpm check:*` alias,
1364-
# same as the other direct steps in this lane: that alias is a line in root
1365-
# package.json, inside the @changesets/cli v3 fence (#9465), and the direct
1366-
# form loses no discovery — dispatch-gates.mjs derives gate families from
1367-
# either spelling. Precedent: PR #10698.
1394+
# same as the other direct steps in this lane: see the GATE INVOCATION
1395+
# IDIOM note at the top of this file. Precedent: PR #10698.
13681396
#
13691397
# Runs its own --self-test first, and that leg is load-bearing: this gate
13701398
# computes its own population, so a block parser that stopped matching
@@ -1812,7 +1840,8 @@ jobs:
18121840
# instrument is fatal here in every mode. `--strict` promotes findings to
18131841
# exit 1 for a caller that owns the remedy; nothing on a PR passes it.
18141842
#
1815-
# No `pnpm check:*` alias: root package.json is inside the #9465 fence.
1843+
# No `pnpm check:*` alias: see the GATE INVOCATION IDIOM note at the top
1844+
# of this file.
18161845
- name: Release section-coverage guard
18171846
run: |
18181847
node scripts/check-release-section-coverage.mjs --self-test
@@ -1935,8 +1964,8 @@ jobs:
19351964
# entry without one.
19361965
#
19371966
# Invoked as `node` rather than through a `pnpm check:*` alias, same as the
1938-
# other `node scripts/...` steps in this lane: that alias is a line in root
1939-
# package.json, inside the @changesets/cli v3 fence (#9465).
1967+
# other `node scripts/...` steps in this lane: see the GATE INVOCATION
1968+
# IDIOM note at the top of this file.
19401969
#
19411970
# Its --self-test runs first and is not ceremony. The gate is GREEN OVER AN
19421971
# EMPTY EXECUTABLE POPULATION -- every mention of the verb on this tree is
@@ -1996,11 +2025,9 @@ jobs:
19962025
# as a fresh one, and this comment is what the next reader checks the
19972026
# harness against. Prose classes survive an M16; a count does not.
19982027
#
1999-
# Invoked as `node` rather than through a `pnpm check:*` alias: that alias
2000-
# belongs in root package.json, declared territory of the @changesets/cli
2001-
# v3 migration lane (#9465) while it runs. Same shape as the
2002-
# release-rehearsal step above; dispatch-gates.mjs derives gate families
2003-
# from either spelling. No network, no build; ~0.2 s.
2028+
# Invoked as `node` rather than through a `pnpm check:*` alias: see the
2029+
# GATE INVOCATION IDIOM note at the top of this file. Same shape as the
2030+
# release-rehearsal step above. No network, no build; ~0.2 s.
20042031
- name: Cross-repo closer outcome contract
20052032
run: |
20062033
node scripts/check-cross-repo-closer-outcome.mjs --self-test
@@ -2025,10 +2052,8 @@ jobs:
20252052
# is exactly the change that would miss one.
20262053
#
20272054
# Invoked as `node` rather than a `pnpm check:*` alias for the same reason
2028-
# as the step above: that alias belongs in root package.json, declared
2029-
# territory of the @changesets/cli v3 migration lane (#9465) while it
2030-
# runs. dispatch-gates.mjs derives gate families from either spelling.
2031-
# No network, no build; ~1 s, most of it the tracked-file sweep.
2055+
# as the step above: see the GATE INVOCATION IDIOM note at the top of this
2056+
# file. No network, no build; ~1 s, most of it the tracked-file sweep.
20322057
- name: Closing-keyword parser parity
20332058
run: |
20342059
node scripts/check-closing-keyword-parity.mjs --self-test
@@ -2067,8 +2092,8 @@ jobs:
20672092
# matching nothing.
20682093
#
20692094
# Invoked as `node` rather than through a `pnpm check:*` alias, same
2070-
# reason as the step above: root package.json is declared territory of
2071-
# the @changesets/cli v3 migration lane (#9465) while it runs.
2095+
# reason as the step above: see the GATE INVOCATION IDIOM note at the top
2096+
# of this file.
20722097
- name: Merge-queue triage outcome contract
20732098
run: |
20742099
node scripts/check-merge-queue-triage-outcome.mjs --self-test
@@ -2114,9 +2139,8 @@ jobs:
21142139
# state is a refusal rather than a quiet pass — no aggregator found, a
21152140
# workflow that will not parse, or a roster that resolves to zero members
21162141
# all exit 1 naming what could not be read (#4690).
2117-
# Invoked as `node` rather than through a `pnpm check:*` alias: that
2118-
# alias belongs in root package.json, which is declared territory of the
2119-
# @changesets/cli v3 migration lane (#9465) while it runs. Reads two YAML
2142+
# Invoked as `node` rather than through a `pnpm check:*` alias: see the
2143+
# GATE INVOCATION IDIOM note at the top of this file. Reads two YAML
21202144
# files; sub-second.
21212145
- name: Aggregator roster gate
21222146
run: |
@@ -2225,10 +2249,9 @@ jobs:
22252249
# either filter has left the OR, and reports a `crosspkg` entry that
22262250
# covers no declaration any more.
22272251
# Invoked as `node` rather than through a `pnpm check:*` alias for the
2228-
# same reason as the steps below: that alias belongs in root package.json,
2229-
# declared territory of the @changesets/cli v3 lane (#9465) while it runs.
2230-
# dispatch-gates.mjs derives gate families from either spelling. Two YAML
2231-
# parses and a string comparison; milliseconds.
2252+
# same reason as the steps below: see the GATE INVOCATION IDIOM note at
2253+
# the top of this file. Two YAML parses and a string comparison;
2254+
# milliseconds.
22322255
- name: CI filter parity (cross-package Layer C)
22332256
run: |
22342257
node scripts/check-ci-filter-parity.mjs --self-test
@@ -2247,10 +2270,8 @@ jobs:
22472270
# and the reader-side refusal that now catches that needs a live pin of its
22482271
# own or it rots the same way.
22492272
# Invoked as `node` rather than through a `pnpm check:*` alias for the same
2250-
# reason as the release-rehearsal self-test above: that alias belongs in
2251-
# root package.json, declared territory of the @changesets/cli v3 lane
2252-
# (#9465) while it runs. dispatch-gates.mjs derives gate families from
2253-
# either spelling. Pure functions, no IO, milliseconds.
2273+
# reason as the release-rehearsal self-test above: see the GATE INVOCATION
2274+
# IDIOM note at the top of this file. Pure functions, no IO, milliseconds.
22542275
- name: Shard partitioner self-test
22552276
run: node scripts/partition-test-shards.mjs --self-test
22562277

@@ -2270,10 +2291,8 @@ jobs:
22702291
# corpus (every `scripts/**/*.d.mts`), so a third mirror added tomorrow is
22712292
# covered by existing rather than by anyone remembering to enrol it.
22722293
# Invoked as `node` rather than through a `pnpm check:*` alias for the
2273-
# same reason as the two steps above: that alias belongs in root
2274-
# package.json, declared territory of the @changesets/cli v3 lane (#9465)
2275-
# while it runs. dispatch-gates.mjs derives gate families from either
2276-
# spelling. Imports two small modules; milliseconds.
2294+
# same reason as the two steps above: see the GATE INVOCATION IDIOM note
2295+
# at the top of this file. Imports two small modules; milliseconds.
22772296
- name: Hand-written declaration mirrors
22782297
run: |
22792298
node scripts/check-declaration-mirrors.mjs --self-test
@@ -2493,8 +2512,8 @@ jobs:
24932512
# pointing at #10371 which owns their repair; the list refuses new members
24942513
# outright, so nothing can join it to get CI green.
24952514
# Invoked as `node` rather than through a `pnpm check:*` alias, same as
2496-
# the other `node scripts/…` steps in this lane: that alias is a line in
2497-
# root package.json, inside the @changesets/cli v3 fence (#9465).
2515+
# the other `node scripts/…` steps in this lane: see the GATE INVOCATION
2516+
# IDIOM note at the top of this file.
24982517
# Runs its own --self-test first, and that leg is not decoration: this
24992518
# gate computes its own population, so a scan that stopped matching would
25002519
# print a confident green over a tree it never read (#4690's family). The
@@ -3215,8 +3234,8 @@ jobs:
32153234
# SUCCESS as well as failure, which is the visibility half of #10473 — every
32163235
# PR's log carries each file's distance from its ceiling.
32173236
#
3218-
# Invoked as `node`, NOT through an npm script: root package.json is fenced
3219-
# (#9465), and this gate needs no wiring there — same shape as the two
3237+
# Invoked as `node`, NOT through an npm script — see the GATE INVOCATION
3238+
# IDIOM note at the top of this file; same shape as the two
32203239
# `node scripts/…` steps earlier in this lane. Static text only, no build, so
32213240
# it belongs in this pre-build group. No paths filter, for the standard reason:
32223241
# a filter on skills/** would go dormant on the PR that edits the ceilings.

0 commit comments

Comments
 (0)