Filed unassigned from the #9863 dev seat (session session_01DdCnBGcHeufjrq7drTD3wt, PR #10450). Measured, not reasoned: the gate passed green over the read, and the declaration in that PR was added by hand.
The measurement
packages/cli/src/commands/serve-audit-registration.contract.test.ts (new in PR #10450) contains:
import{maskComments}from'../../../../scripts/js-comment-mask.mjs';That specifier climbs out of packages/cli and lands in scripts/. It is a real input: the test's verdict is a function of that module's masking behaviour. With no roster entry for it, pnpm check:cross-package-test-inputs reported:
OK: 12 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.
The consequence is the exact failure the gate exists to close, quoted from its own text: "a test whose real inputs are wider than its package is invisible to BOTH the affected-subset filter and the turbo cache, so it can go red on main while every PR reports green." An edit to js-comment-mask.mjs would not have re-run cli's suite.
Why the collector misses it
The gate is a deliberate source scan and publishes the spellings it recognises — all of them are path-shaped file reads seeded from import.meta.url/__dirname:
const P = resolve(HERE, '<rel>');
const P = fileURLToPath(new URL('<rel>', import.meta.url));
readFileSync(resolve(HERE, '<rel>'))
…
An ES module specifier is none of those. It is a bare string in import position that the module resolver — not node:path — turns into a file. So the escape is invisible, and per the gate's own framing that means no declaration, silently.
Not the same as the neighbours
Worth noting that check-test-source-alias.mjs already resolves test import specifiers for its own purpose, so the parsing work may not need to be written from scratch.
Suggested shape, and the trap in it
Extend the collector to recognise static import/export … from specifiers (and await import('…')) that begin with ../ and resolve outside the package root, judged on the shallowest point reached, exactly as the path forms already are. Add a --self-test case per spelling in the same edit — the gate's own instruction, and the reason its recognised list is published rather than private.
⚠️ The trap: a bare package specifier (@objectstack/verify) must NOT be collected. Those are declared dependencies resolved through node_modules, which the gate already excludes by design — "an installed dependency is not a repo source input, and no turbo glob can name it." Only relative specifiers that escape the package are in scope. Getting that boundary wrong would put every package's test suite on every workspace sibling.
Interim state
PR #10450 declares scripts/js-comment-mask.mjs for @objectstack/cli in CROSS_PACKAGE_TEST_INPUTS and adds the matching $TURBO_ROOT$ input to @objectstack/cli#test, with a comment recording that the gate did not demand it. That closes the one instance; it does not close the class, and any future test importing across the boundary is silent again.
Filed unassigned from the #9863 dev seat (session
session_01DdCnBGcHeufjrq7drTD3wt, PR #10450). Measured, not reasoned: the gate passed green over the read, and the declaration in that PR was added by hand.The measurement
packages/cli/src/commands/serve-audit-registration.contract.test.ts(new in PR #10450) contains:That specifier climbs out of
packages/cliand lands inscripts/. It is a real input: the test's verdict is a function of that module's masking behaviour. With no roster entry for it,pnpm check:cross-package-test-inputsreported:The consequence is the exact failure the gate exists to close, quoted from its own text: "a test whose real inputs are wider than its package is invisible to BOTH the affected-subset filter and the turbo cache, so it can go red on
mainwhile every PR reports green." An edit tojs-comment-mask.mjswould not have re-run cli's suite.Why the collector misses it
The gate is a deliberate source scan and publishes the spellings it recognises — all of them are path-shaped file reads seeded from
import.meta.url/__dirname:An ES module specifier is none of those. It is a bare string in
importposition that the module resolver — notnode:path— turns into a file. So the escape is invisible, and per the gate's own framing that means no declaration, silently.Not the same as the neighbours
new URL(…, import.meta.url)seed, so an undeclared cross-package read passes green #8698 taught it thenew URL(…, import.meta.url)seed. Likewise.dispatch-gatescannot namecheck:test-source-aliasbecause the trigger is a new cross-package import inside a test. That is the same underlying shape one layer up (which gate to run), while this is about which inputs get declared once the gate does run.Worth noting that
check-test-source-alias.mjsalready resolves test import specifiers for its own purpose, so the parsing work may not need to be written from scratch.Suggested shape, and the trap in it
Extend the collector to recognise static
import/export … fromspecifiers (andawait import('…')) that begin with../and resolve outside the package root, judged on the shallowest point reached, exactly as the path forms already are. Add a--self-testcase per spelling in the same edit — the gate's own instruction, and the reason its recognised list is published rather than private.@objectstack/verify) must NOT be collected. Those are declared dependencies resolved throughnode_modules, which the gate already excludes by design — "an installed dependency is not a repo source input, and no turbo glob can name it." Only relative specifiers that escape the package are in scope. Getting that boundary wrong would put every package's test suite on every workspace sibling.Interim state
PR #10450 declares
scripts/js-comment-mask.mjsfor@objectstack/cliinCROSS_PACKAGE_TEST_INPUTSand adds the matching$TURBO_ROOT$input to@objectstack/cli#test, with a comment recording that the gate did not demand it. That closes the one instance; it does not close the class, and any future test importing across the boundary is silent again.