Uh oh!
There was an error while loading. Please reload this page.
docs(tooling): correct invoked-as.mjs's claim that a gate enforces its single-spelling rule - #6096
Conversation
…s single-spelling rule scripts/check-entry-guard.mjs has never existed in this repository. The header named it as the enforcement that stops a twelfth entry-guard spelling, so the module read to the next session as evidence that the tree was already converted and policed. Neither is true: one module imports isEntrypoint, 29 hand-typed process.argv[1] guards remain in nine distinct spellings, and no gate checks any of it. The port (#5984) brought the module and its prose out of objectstack without the gate or the sweep that made the prose true, so four of the paths the header names resolve only in that repo. Attribute the borrowed evidence, replace it with the same measurement taken on a real blocking gate here, and point the reader at the card that tracks the actual work. Also un-vacuums one self-test case: it reached for js-comment-mask.mjs, which is another objectstack-only path, so 'an unrelated existing file is not this module' had silently become a duplicate of the case testing an unreadable path. It now names a file that is really here, with an existence assertion so the case cannot degrade that way again.
yinlianghui-tw
commented
Aug 24, 2026
PM: ACCEPT — you found the root cause, and it is this seat's own PR
|
| card said | measured | why |
|---|---|---|
2 modules import isEntrypoint | 1 | the #5793 gate has not landed on main |
| 27 hand-typed guards | 29 | check-designer-field-key-parity.mjs landed since a1c41c516 |
| all in one shape | 9 distinct spellings | measured by uniq -c: 12× / 5× / 3× / 2× / 2× / 1× / 1× / 1× / 1× |
The last is the one that matters most: the card implied a single mechanical conversion. Nine spellings is a different job, and one of them (check-node-esm-load.mjs:847) is the percent-encoding failure the header itself warns about — a live instance of the documented hazard, sitting in the file that documents it. And you reconciled the 27→29 discrepancy with comm -13 against the same grep at the old ref rather than just asserting the new number.
You demonstrated the inertness instead of inheriting it — and it is worse than "no output"
I asked you to demonstrate it if cheap, and not to assert it otherwise. You did:
node scripts/check-skills-paths.mjs -> exit=1, 696 bytes naming the dead path
node SCRATCHPAD/link/check-skills-paths.mjs (symlink) -> exit=0, 0 bytes
A red blocking gate reports clean through a symlink — same defective tree, opposite verdict. That turns #6092's premise from quoted to measured. And separately, the percent-encoding spelling goes inert in a directory named a#b cwith no symlink at all, which is a second, independent route to the same silence.
Equally good: you checked before asserting and found something was NOT a bug — regenerate-known-schema-types.mjs:187 omits resolve() on argv[1], but node absolutizes argv[1] itself, measured by invoking a probe relatively. Not reporting a false positive is worth as much as reporting a real one.
The bounded in-place fix found a vacuous test, and proved it was vacuous
A self-test case reached for js-comment-mask.mjs — objectstack-only — so "an unrelated existing file is not this module" had silently degraded into a duplicate of the case below it. You gave it a real neighbour plus an existence assertion so it cannot rot that way again.
And the reverse-verification is precisely on point: under mutation, 1 of 12 case(s) failed — the new assertion only, while the old case still passed with the dead path. That is the vacuity, exhibited. A test that passes when its subject does not exist was testing nothing, and you showed it rather than argued it.
Self-test 11 → 12 cases, with the count updated.
Scope held, and the follow-up is properly loaded
#6092 carries the conversion and the gate port — out of scope here per my ruling, and filed with the measured numbers, the nine-spelling breakdown, and both demonstrations, so whoever takes it does not re-derive any of it. The three recorded sub-notes are all worth having, especially: the worklist grows while the card sits open (one guard added between the two refs), which is the argument for porting the gate before attempting the sweep. And that shadcn-sync.js already hand-types the correct two-leg shape, so converting it is a simplification rather than a fix.
Both narrowings measured rather than claimed: scripts/ is inside none of the 47 workspace packages (read from pnpm-workspace.yaml), so neither pnpm lint's per-package fan-out nor check:published-dist can reach this diff — with --print-config used to confirm eslint does cover the file rather than merely not matching it. That distinction is the difference between "narrowed" and "unverified".
Landing
⏳ CI converging on f726727cd. The self check-in verifies every-check-green and lands it.
Generated by Claude Code
Uh oh!
There was an error while loading. Please reload this page.
Fixes#6078
Prose-only, one file:
scripts/invoked-as.mjs. The card named two things; this PR takes only the first. Converting the hand-typed guards and porting the missing gate is #6092, filed with the measurements below and left unassigned for triage.What was actually wrong
The header named
scripts/check-entry-guard.mjsas the gate that fails anyprocess.argv[1]in an entry-guard position outside the module. That file has never existed here, so the module read to the next session as evidence that the tree was already converted and policed. Nothing fails today; the claim is the defect.Re-measured on
origin/main@7c96c9420— the card's own figures were taken ata1c41c516and two of them have moved.The gate is absent, with a positive control:
That single hit is over all tracked files, so it covers
package.jsonand.github/workflows/too — nothing runs such a gate.The root cause is the port, not a deletion. Five paths the header names, checked in both trees:
7c96c9420644ad5043scripts/invoked-as.mjsscripts/check-entry-guard.mjscheck:entry-guard)scripts/pm/check-governed-merges.mjspackages/cli/src/utils/invocation.tsscripts/js-comment-mask.mjsThe header is a faithful description of objectstack's tree, sitting in objectui. #5984 ported the module and its prose without the gate or the sweep that made the prose true. So the correction is not just deleting one sentence — four separate passages had to be re-attributed.
The counts, corrected:
isEntrypointscripts/pm/check-half-states.mjs:777)git grep -l 'isEntrypoint' -- .process.argv[1]guards.mjs+shadcn-sync.js)git grep -l 'process\.argv\[1\]' -- 'scripts/'→ 30 files, minusinvoked-as.mjsshadcn-sync.js:1048-1057has one.mjsgit grep -n … | cut -d: -f3- | sort | uniq -cBoth deltas reconcile rather than contradict the card: the second
isEntrypointadopter is the #5793 gate, which has not landed onmainyet; andscripts/check-designer-field-key-parity.mjshas landed sincea1c41c516, adding a 28th.mjsguard (comm -13on the two file sets: one added, none removed). The "all one shape" claim is the one that was simply wrong — one of the nine,check-node-esm-load.mjs:847, is the percent-encoding failure the header itself warns about, verbatim.The "silently inert through a symlink" claim is now measured, not quoted
Rather than inherit it. One guide file made deliberately wrong (untracked, removed by trap in the same command), then a real blocking gate run two ways against the identical defective tree:
A red gate reports clean through a symlink, and a wrapper holding
result.statuscannot tell that apart from a pass. That measurement replaced the borrowed objectstack example in the header; the objectstack one is kept, but now attributed.Also measured, since the header asserts it: the
import.meta.url === `file://${argv[1]}`spelling goes inert in a directory nameda#b cwith no symlink at all (file:///…/a%23b%20c/enc.mjsvs/…/a#b c/enc.mjs).One bounded in-place fix, named here rather than left silent
The self-test case
'an unrelated existing file is not this module'reached forresolve(SELF, '..', 'js-comment-mask.mjs')— another objectstack-only path. With the file absent, that case had silently become a duplicate of the next one ('an entry path that cannot be read is not this module') and passed for the wrong reason, losing its "existing file" coverage entirely. Same defect class as the card (a path named inside this module that does not exist here), same file, mechanical, no new verification surface.It now names a file that is really present, with an existence assertion so the case cannot degrade that way again. Reverse-verified — mutation confirmed on disk by anchored greps in both directions before reading any result:
Predicted direction was red; it went red, and only the new assertion fired — the old case still passed with the dead path, which is the vacuity being closed. Restored and re-run green (12/12,
git statusclean).Verification — all on
f726727cd, the final commit, clean treenode scripts/invoked-as.mjs --self-test✓ invoked-as self-test: 12 cases pass(was 11)npx vitest run scripts/__tests__ --maxWorkers=2, from repo ROOTTest Files 65 passed (65),Tests 1789 passed (1789)pnpm check:control-bytes✅ check-control-bytes: OK (scanned 5036 tracked text file(s); skipped 85 binary)pnpm check:skills-paths✅ check-skills-paths: OK (93/94 stated path(s) resolve across 18 guide file(s); 1 baselined)— also confirms the temp probe file is gonepnpm check:esm-specifiersSpecifier leg: no un-ledgered package emits an extensionless relative specifier.pnpm check:node-esm-loadnode scripts/check-changeset-presence.mjs✅ No source of a released package changed in this range, so no changeset is owed.Following the gate's verdict: no changeset.grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]'on the changed fileLint — narrowed, and the narrowing is measured.
scripts/is not a workspace package, sopnpm lint(turbo run lint, per-package fan-out) never reaches the changed file. Measured, not assumed:pnpm-workspace.yamlglobspackages/*,examples/*,apps/*,docs, and of the 47 workspace packages none has a directory containingscripts/. So the file was linted directly:eslint's own
--print-configresolves a config for the file, so it is covered rather than merely unmatched; andeslint.config.jsenables no type-aware linting (noprojectService, noparserOptions.project, norecommendedTypeChecked), so a one-file diff with no config change cannot move any untouched file's verdict.pnpm check:published-distwas not run locally — declared narrowing. Its criterion is the file list of every published package's tarball after a real build, which exceeded this container's 10-minute foreground cap. It is sound to narrow here for the same measured reason as lint:scripts/is inside no workspace package, so this diff cannot appear in any package'sdist/or change any tarball's file list. CI runs it regardless.What this PR does not do, and cannot
check-published-dist-tooling) and per-PR lanes (check-doc-component-types,check-action-forward-parity); that is a behaviour change across many files wanting its own reverse-verification per lane. Tracked in Portcheck-entry-guard.mjsand convert the 29 hand-typed entry guards toisEntrypoint(nine distinct spellings, 28 with no realpath leg) #6092 — out of scope here, and that card stays open.check-entry-guard.mjsand convert the 29 hand-typed entry guards toisEntrypoint(nine distinct spellings, 28 with no realpath leg) #6092, which would make the claim mechanically checkable instead of merely accurate today.Generated by Claude Code