Skip to content

Commit 2fd3118

Browse files
committed
fix(ci): run the self-tests the two scripts/ routing gates delegate to, and the sink gate that never ran
`check:entry-guard` and `check:parse-guard` are spelling gates whose headers each answer "why is spelling enough?" by delegating to a module's own `--self-test` — `invoked-as.mjs` and `ts-parse.mjs`. Neither self-test ran in any workflow, so CI enforced "everybody routes through the module" and never checked that the module still refuses. `js-comment-mask.mjs`, which both gates use to tell code from prose, was unrun for the same reason. `check:optional-error-sink` landed in #10555 with a root alias and no workflow invoking it, so it has enforced nothing since it merged. Wired as `lint.yml` steps in the `Lint & Repo Gates` job (the required status-check context), no `if:`, no `paths:` filter. No new root `package.json` alias — that file is #9465 fence territory. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DdCnBGcHeufjrq7drTD3wt
1 parent 47aff09 commit 2fd3118

1 file changed

Lines changed: 61 additions & 0 deletions

File tree

.github/workflows/lint.yml

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,44 @@ jobs:
152152
- name: Raw control-byte guard
153153
run: pnpm check:nul-bytes
154154

155+
# The three shared modules the two `scripts/**` routing gates below
156+
# DELEGATE their design arguments to (#10608). Both of those gates are
157+
# SPELLING gates by deliberate design — `check:entry-guard` enforces that
158+
# only `scripts/invoked-as.mjs` may read `process.argv[1]`, and
159+
# `check:parse-guard` that every TypeScript parse goes through
160+
# `scripts/ts-parse.mjs` — and each answers "why is spelling enough?" the
161+
# same way: the BEHAVIOUR is pinned once, at the module, by that module's
162+
# own `--self-test`. Neither self-test ran anywhere. So CI enforced
163+
# *"everybody routes through the module"* and never once checked that the
164+
# module still refuses — a regression inside `ts-parse.mjs` (a
165+
# `process.exit` downgraded to a `throw`, a dropped `parseDiagnostics`
166+
# read) was caught by nothing, and its symptom is the same green line this
167+
# whole family exists to distrust (#10574 / #10573 / #4690).
168+
#
169+
# `js-comment-mask.mjs` is here because BOTH gates ask it to tell code
170+
# from prose before they judge anything, and its header records that it
171+
# exists because two private `stripComments` families drifted apart in
172+
# opposite directions. Its two failure directions are not symmetric: a
173+
# mask that stops masking makes both gates fabricate findings out of their
174+
# own documentation — loud — while a mask that starts OVER-masking makes
175+
# both go quietly green over text they never read.
176+
#
177+
# What runs here is the self-tests, not the modules' callers: the callers
178+
# 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.
184+
# `ts-parse` spawns ~20 real node children (~10 s measured, and the spawns
185+
# are the point — they pin that a caller's try/catch cannot swallow the
186+
# refusal); the other two are in-process fixtures, ~0.5 s combined.
187+
- name: scripts/ shared-module self-tests (parse · entry predicate · comment mask)
188+
run: |
189+
node scripts/ts-parse.mjs --self-test
190+
node scripts/invoked-as.mjs --self-test
191+
node scripts/js-comment-mask.mjs --self-test
192+
155193
# Every `scripts/**` entry guard goes through ONE predicate (#10086).
156194
# The hand-typed forms of "did node run me, or did someone import me?"
157195
# had drifted into ELEVEN spellings across 33 files, and NINE were wrong
@@ -932,6 +970,29 @@ jobs:
932970
- name: Dispatcher error-code vocabulary guard
933971
run: pnpm check:dispatcher-error-vocabulary
934972

973+
# Optional-`error` sink contract (#9754, wired by #10608). A sink TYPE that
974+
# declares `error` as OPTIONAL must also declare `warn` as NON-optional, so
975+
# every value of that type has somewhere to put a durability report —
976+
# silence stops being representable at AUTHORING time instead of being
977+
# caught one gate-run later. `AuthEventAuditLogger` declared `error?` and
978+
# `debug?` and no `warn` at all: the call site could not have been written
979+
# correctly against the contract it was given. Producer-side half of
980+
# AGENTS.md → Prime Directive #12; the call-site rule
981+
# (`check:durability-log-level`) is separate and unchanged.
982+
#
983+
# WHY THIS STEP EXISTS AT ALL: the gate landed with PR #10555 carrying its
984+
# root alias, and NO workflow ever invoked it — so from the hour it merged
985+
# it enforced nothing, while reading in `package.json` exactly like the
986+
# gates that do. That is #10574's defect one file over, and it is the only
987+
# thing this change repairs; the gate itself is untouched.
988+
# Runs its own --self-test FIRST (13 cases, both directions, both
989+
# narrowings pinned as counts), then the scan — 36 sink types, 15
990+
# baselined shrink-only. AST over packages/**, no spawns; ~3.5 s.
991+
# No `paths:` filter, for the standard reason: a filter on `packages/**`
992+
# would go dormant on the PR that edits the baseline.
993+
- name: Optional-`error` sink contract
994+
run: pnpm check:optional-error-sink
995+
935996
# Namespace-wildcard fall-through guard (#4116). A handler mounted on
936997
# `<prefix>/*` claims the whole namespace, and Hono's first-registered
937998
# handler that answers wins — so a TERMINAL wildcard makes every other

0 commit comments

Comments
 (0)