Filed unassigned from the #10610 dev seat (session session_01DdCnBGcHeufjrq7drTD3wt, PR #10628). This is the sweep that #10610's "Not claimed" section explicitly left undone — "I have not swept the other scripts/** gates for the same missing guard. If several are unguarded the shape of the fix changes."
Several are. 39.
The measurement
On origin/main at 5e50e2e3ba, files under scripts/ that export at least one binding and have noisEntrypoint(import.meta.url) guard — comments and string literals masked with check-entry-guard.mjs's own codeOnly(), so a prose mention of the idiom is not miscounted as a guard:
scripts/ files scanned : 123
with a real (code, not comment) entry guard : 41 (42 after PR #10628)
exporting bindings AND unguarded : 40 (39 after PR #10628)
The 39 that remain:
ablation-dist-preflight.mjs check-release-page-status.mjs
adr-anchors.mjs check-required-contexts.mjs
check-changeset-no-major.mjs check-route-envelope.mjs
check-dispatcher-error-vocabulary check-runtime-services-index.mjs
check-driver-memory-census.mjs check-shard-attestation.mjs
check-empty-changeset.mjs check-spec-parsed-alias.mjs
check-engine-split-ratio.mjs check-startup-registry-verdict.mjs
check-error-code-casing.mjs check-tenant-chokepoint.mjs
check-error-status-conformance.mjs check-test-completeness.mjs
check-examples-live-imports.mjs check-workflow-status-functions.mjs
check-filter-alias-parity.mjs checklist-select.mjs
check-nul-bytes.mjs cli-build-prerequisite.mjs
check-org-identifier.mjs console-spec-probes.mjs
check-query-options-erasure-ratchet.mjs docs-audit/check-audit-scope.mjs
check-quick-reference-counts.mjs eslint-fatal-guard.mjs
check-ratchet-remedy-authority.mjs eslint-stack-headroom.mjs
i18n-bundle-surface.mjs
measure-test-shard-timings.mjs
pm/check-governed-prose.mjs
pm/check-label-desc-cap.mjs
pm/check-skill-id-lint.mjs
pm/check-skill-line-ratchet.mjs
regen-artifacts.mjs
Reproduce:
import{readdirSync,readFileSync,statSync}from'node:fs';import{join}from'node:path';import{codeOnly}from'./scripts/check-entry-guard.mjs';// walk scripts/, skipping node_modules and dotfiles, for .mjs/.js/.cjsconstguarded=/isEntrypoint\s*\(\s*import\.meta\.url\s*\)/.test(codeOnly(src));constexports=/^export\s/m.test(codeOnly(src));Why this is a class, not 39 tickets
It has now been fixed twice, one file at a time:
Both were found by someone tripping over one instance. Nothing gates the next one, and there are 39 more sitting there. check-entry-guard.mjs exists precisely because a one-time sweep is worth little on its own — its own header says so about the spelling problem — but it currently enforces only that a guard, if present, is spelled the one right way. A file with no guard at all is accepted; that case is an explicit self-test assertion:
t('a file with no guard at all is accepted',n("console.log('hello');\n")===0);which is correct for a script that exports nothing, and is exactly the hole for one that does.
Suggested shape (not claimed)
Extend check-entry-guard.mjs with a second finding kind: a scripts/** file that both exports a binding and performs top-level side-effectful dispatch must carry the guard. That closes the class the way the spelling half was closed, instead of a 40th ticket later.
The hard part is deciding "top-level side-effectful dispatch" precisely enough to avoid false positives — a module of pure export functions with no top-level statements is fine unguarded, and several of the 39 may be exactly that. Worth measuring before choosing between:
- A. Require the guard whenever the file exports AND has any top-level call expression. Strongest; needs a real parse, and
check-entry-guard.mjs is deliberately a masked-source scanner rather than a parser. - B. Require it only when the file exports AND reads
process.argv. Cheap, no parser, catches the CLI-dispatch shape all three known instances had — including this one. Misses a module whose top-level side effect is not argv-driven. - C. Behavioural: import each candidate in a child and assert silence + exit 0. Directly measures the property.
check-entry-guard.mjs's header already rejected running scripts wholesale, but importing them is a much weaker action than invoking them — though not a safe one for the modules whose top level has real side effects, which is the same objection in a smaller form.
B looks like the best value for the risk; A is the one that actually closes the class. Someone should measure how many of the 39 have top-level statements at all before picking — that number decides whether this is a 5-file fix or a 39-file one.
Not claimed
Generated by Claude Code
Filed unassigned from the #10610 dev seat (session
session_01DdCnBGcHeufjrq7drTD3wt, PR #10628). This is the sweep that #10610's "Not claimed" section explicitly left undone — "I have not swept the otherscripts/**gates for the same missing guard. If several are unguarded the shape of the fix changes."Several are. 39.
The measurement
On
origin/mainat5e50e2e3ba, files underscripts/that export at least one binding and have noisEntrypoint(import.meta.url)guard — comments and string literals masked withcheck-entry-guard.mjs's owncodeOnly(), so a prose mention of the idiom is not miscounted as a guard:The 39 that remain:
Reproduce:
Why this is a class, not 39 tickets
It has now been fixed twice, one file at a time:
scripts/pm/dispatch-gates.mjsdispatches its CLI at module top level, so the re-exports its header offers cannot be imported —importruns the tool andprocess.exit(2)s the importer #9757 —scripts/pm/dispatch-gates.mjsdispatched at top level, soimportran the tool andprocess.exit(2)'d the importer. Closed.check-cross-package-test-inputs.mjs, where it had already cost something real:check-examples-live-imports.mjshand-copiedglobToRegExprather than import it, and recorded the load-time gate as its reason. PR fix(scripts): guard check-cross-package-test-inputs dispatch, and stop mirroring globToRegExp #10628 adds the guard and collects the duplicate.Both were found by someone tripping over one instance. Nothing gates the next one, and there are 39 more sitting there.
check-entry-guard.mjsexists precisely because a one-time sweep is worth little on its own — its own header says so about the spelling problem — but it currently enforces only that a guard, if present, is spelled the one right way. A file with no guard at all is accepted; that case is an explicit self-test assertion:which is correct for a script that exports nothing, and is exactly the hole for one that does.
Suggested shape (not claimed)
Extend
check-entry-guard.mjswith a second finding kind: ascripts/**file that both exports a binding and performs top-level side-effectful dispatch must carry the guard. That closes the class the way the spelling half was closed, instead of a 40th ticket later.The hard part is deciding "top-level side-effectful dispatch" precisely enough to avoid false positives — a module of pure
export functions with no top-level statements is fine unguarded, and several of the 39 may be exactly that. Worth measuring before choosing between:check-entry-guard.mjsis deliberately a masked-source scanner rather than a parser.process.argv. Cheap, no parser, catches the CLI-dispatch shape all three known instances had — including this one. Misses a module whose top-level side effect is not argv-driven.check-entry-guard.mjs's header already rejected running scripts wholesale, but importing them is a much weaker action than invoking them — though not a safe one for the modules whose top level has real side effects, which is the same objection in a smaller form.B looks like the best value for the risk; A is the one that actually closes the class. Someone should measure how many of the 39 have top-level statements at all before picking — that number decides whether this is a 5-file fix or a 39-file one.
Not claimed
Generated by Claude Code