Split out of #6078 by the dev seat correcting that card's header claim. #6078 fixes the claim only — the false "a gate enforces this" sentence in scripts/invoked-as.mjs. This card is the other half: the work the claim was describing, which never happened here.
What was measured
All figures on origin/main @ 7c96c9420, in /home/user/objectui-6078.
scripts/invoked-as.mjs was ported from objectstack (#5984) without the gate or the sweep that made its header true. Five paths that header names, checked in both trees:
| path | objectui 7c96c9420 | objectstack 644ad5043 |
|---|
scripts/invoked-as.mjs | EXISTS | EXISTS |
scripts/check-entry-guard.mjs | MISSING | EXISTS (787 lines, wired check:entry-guard) |
scripts/pm/check-governed-merges.mjs | MISSING | EXISTS |
packages/cli/src/utils/invocation.ts | MISSING | EXISTS |
scripts/js-comment-mask.mjs | MISSING | EXISTS |
So the header is a faithful description of objectstack's tree, sitting in objectui.
The adoption count.git grep -l 'isEntrypoint' -- . → exactly two files: scripts/invoked-as.mjs and one adopter, scripts/pm/check-half-states.mjs:777. #6078's body said two adopters; the second (the gate from #5793) has not landed on main.
The worklist.git grep -l 'process\.argv\[1\]' -- 'scripts/' → 30 files. Minus invoked-as.mjs itself → 29 hand-typed entry guards: 28 .mjs plus scripts/shadcn-sync.js.
#6078 measured 27 at a1c41c516; that reconciles — scripts/check-designer-field-key-parity.mjs has landed since, adding a 28th .mjs guard (comm -13 on the two file sets, one file added, none removed). The worklist grows while this sits open — which is the argument for doing the gate first.
The realpath leg. 28 of the 29 have none. The single exception is scripts/shadcn-sync.js:1048-1057, whose invokedAsCli() hand-types the correct two-leg shape.
The drift is real, not one spelling.#6078 said all of them use one shape; they do not. Nine textually distinct expressions across the 28 .mjs, by git grep -n 'process\.argv\[1\]' -- 'scripts/' | cut -d: -f3- | sort | uniq -c:
- 12×
argv[1] && resolve(argv[1]) === resolve(fileURLToPath(import.meta.url)) - 5×
argv[1] && resolve(argv[1]) === fileURLToPath(import.meta.url) - 3×
argv[1] && path.resolve(argv[1]) === fileURLToPath(import.meta.url) - 2×
argv[1] && pathToFileURL(argv[1]).href === import.meta.url - 2×
argv[1] && import.meta.url === pathToFileURL(argv[1]).href - 1×
resolve(argv[1] ?? "") === resolve(fileURLToPath(import.meta.url)) - 1×
argv[1] && fileURLToPath(import.meta.url) === argv[1] (regenerate-known-schema-types.mjs:187) - 1×
import.meta.url === pathToFileURL(argv[1] ?? '').href - 1×
import.meta.url === `file://${argv[1]}` (check-node-esm-load.mjs:847)
The last one is the percent-encoding failure invoked-as.mjs's own header documents, verbatim, still in the tree. Measured with that exact expression in a directory named a#b c:
INERT file:///…/a%23b%20c/enc.mjs vs /…/a#b c/enc.mjs
No symlink needed. regenerate-known-schema-types.mjs's missing resolve() is not a live bug — node absolutizes argv[1] itself (measured: invoking node argvprobe.mjs relatively prints an absolute path) — but it is a tenth thing a reader has to re-derive.
The hazard, measured on a real gate in THIS repo
#6078 quoted the header's "exit 0, no output" claim. It is measured now. One guide file made deliberately wrong (untracked, removed immediately), then the same blocking gate run two ways:
node scripts/check-skills-paths.mjs direct : exit=1, 696 bytes naming the dead path
node /…/link/check-skills-paths.mjs (symlink) symlink : exit=0, 0 bytes
Same tree, same defect, same gate. Through the symlink a red gate reports clean, and CI wrappers that hold result.status cannot tell that apart from a pass. check-skills-paths.mjs is one of the 28 no-realpath guards; check-published-dist-tooling.mjs (publish path), check-doc-component-types.mjs and check-action-forward-parity.mjs (per-PR lanes) carry the same shape.
Nothing is failing today — no current invocation goes through a symlink. This is a latent-hazard card, not an outage.
The work, in the order it should happen
- Port the gate. objectstack's
scripts/check-entry-guard.mjs is 787 lines and carries two rules — the spelling rule, and a second one about a file that exports but has no guard at all. It is not a copy-paste: it needs objectui's script inventory, its own baseline, and a wiring decision (objectstack runs it as check:entry-guard in package.json). Landing it first means it names its own worklist and stops a 30th spelling. - Convert the 29 call sites to
isEntrypoint(import.meta.url). Mechanical per site, but it touches blocking gates on the publish path and per-PR lanes, so it wants reverse-verification per lane, not a bulk sed. shadcn-sync.js is the one that already behaves correctly — converting it is a simplification, not a fix.
Splitting 1 and 2 into separate PRs is reasonable; doing 2 without 1 is the "one-time sweep that rots on merge day" the header itself argues against.
Refs: #6078 (the claim, corrected there) · #5984 (the port) · #5793 (the card whose seat found it).
Generated by Claude Code
Split out of #6078 by the dev seat correcting that card's header claim. #6078 fixes the claim only — the false "a gate enforces this" sentence in
scripts/invoked-as.mjs. This card is the other half: the work the claim was describing, which never happened here.What was measured
All figures on
origin/main@7c96c9420, in/home/user/objectui-6078.scripts/invoked-as.mjswas ported from objectstack (#5984) without the gate or the sweep that made its header true. Five paths that 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.mjsSo the header is a faithful description of objectstack's tree, sitting in objectui.
The adoption count.
git grep -l 'isEntrypoint' -- .→ exactly two files:scripts/invoked-as.mjsand one adopter,scripts/pm/check-half-states.mjs:777. #6078's body said two adopters; the second (the gate from #5793) has not landed onmain.The worklist.
git grep -l 'process\.argv\[1\]' -- 'scripts/'→ 30 files. Minusinvoked-as.mjsitself → 29 hand-typed entry guards: 28.mjsplusscripts/shadcn-sync.js.#6078 measured 27 at
a1c41c516; that reconciles —scripts/check-designer-field-key-parity.mjshas landed since, adding a 28th.mjsguard (comm -13on the two file sets, one file added, none removed). The worklist grows while this sits open — which is the argument for doing the gate first.The realpath leg. 28 of the 29 have none. The single exception is
scripts/shadcn-sync.js:1048-1057, whoseinvokedAsCli()hand-types the correct two-leg shape.The drift is real, not one spelling.#6078 said all of them use one shape; they do not. Nine textually distinct expressions across the 28
.mjs, bygit grep -n 'process\.argv\[1\]' -- 'scripts/' | cut -d: -f3- | sort | uniq -c:argv[1] && resolve(argv[1]) === resolve(fileURLToPath(import.meta.url))argv[1] && resolve(argv[1]) === fileURLToPath(import.meta.url)argv[1] && path.resolve(argv[1]) === fileURLToPath(import.meta.url)argv[1] && pathToFileURL(argv[1]).href === import.meta.urlargv[1] && import.meta.url === pathToFileURL(argv[1]).hrefresolve(argv[1] ?? "") === resolve(fileURLToPath(import.meta.url))argv[1] && fileURLToPath(import.meta.url) === argv[1](regenerate-known-schema-types.mjs:187)import.meta.url === pathToFileURL(argv[1] ?? '').hrefimport.meta.url === `file://${argv[1]}`(check-node-esm-load.mjs:847)The last one is the percent-encoding failure
invoked-as.mjs's own header documents, verbatim, still in the tree. Measured with that exact expression in a directory nameda#b c:No symlink needed.
regenerate-known-schema-types.mjs's missingresolve()is not a live bug — node absolutizesargv[1]itself (measured: invokingnode argvprobe.mjsrelatively prints an absolute path) — but it is a tenth thing a reader has to re-derive.The hazard, measured on a real gate in THIS repo
#6078 quoted the header's "exit 0, no output" claim. It is measured now. One guide file made deliberately wrong (untracked, removed immediately), then the same blocking gate run two ways:
Same tree, same defect, same gate. Through the symlink a red gate reports clean, and CI wrappers that hold
result.statuscannot tell that apart from a pass.check-skills-paths.mjsis one of the 28 no-realpath guards;check-published-dist-tooling.mjs(publish path),check-doc-component-types.mjsandcheck-action-forward-parity.mjs(per-PR lanes) carry the same shape.Nothing is failing today — no current invocation goes through a symlink. This is a latent-hazard card, not an outage.
The work, in the order it should happen
scripts/check-entry-guard.mjsis 787 lines and carries two rules — the spelling rule, and a second one about a file that exports but has no guard at all. It is not a copy-paste: it needs objectui's script inventory, its own baseline, and a wiring decision (objectstack runs it ascheck:entry-guardinpackage.json). Landing it first means it names its own worklist and stops a 30th spelling.isEntrypoint(import.meta.url). Mechanical per site, but it touches blocking gates on the publish path and per-PR lanes, so it wants reverse-verification per lane, not a bulk sed.shadcn-sync.jsis the one that already behaves correctly — converting it is a simplification, not a fix.Splitting 1 and 2 into separate PRs is reasonable; doing 2 without 1 is the "one-time sweep that rots on merge day" the header itself argues against.
Refs: #6078 (the claim, corrected there) · #5984 (the port) · #5793 (the card whose seat found it).
Generated by Claude Code