Measured while implementing #10163. No gate is red today, and the failure is silent by construction. Filed unassigned.
Measured on ceb33a9f12
verify() has two staleness limbs and both are package-scoped:
- a package with an escaping test and no entry → red (
:950-958); - an entry whose package has no escaping test any more → red as stale (
:960-967).
There is no limb asking whether each declared glob is still held by something. The roster check at :973-988 runs the other direction: it fails when a test names a path no glob covers. A glob that covers nothing any more is never reported.
That gap was invisible while most declaring packages had exactly one escaping test. @objectstack/plugin-auth now has two, so it is reachable there today:
Ablation. Reseed packages/plugins/plugin-auth/src/managed-extension-fields.test.ts from process.cwd() (the walk the detector deliberately does not resolve) and re-run the gate:
$ node scripts/check-cross-package-test-inputs.mjs --list-escapes | grep -A3 plugin-auth
@objectstack/plugin-auth (packages/plugins/plugin-auth)
packages/plugins/plugin-auth/src/rate-limit-storage-isolation.test.ts # the object walker is gone
$ node scripts/check-cross-package-test-inputs.mjs ; echo "EXIT=$?"
OK: 12 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.
EXIT=0
packages/**/*.object.ts stays declared in CROSS_PACKAGE_TEST_INPUTS and in turbo.json, and nothing holds it. Restored; the tree is unchanged.
Positive control, same ablation on packages/platform-objects/src/managed-api-method-affordance-sweep.test.ts — that package's only escaping test — exits 1: "@objectstack/platform-objects declares a cross-package input radius, but no test in it reads outside the package any more." So the limb works exactly as far as its package scope reaches, and no further.
Why this matters more than a dangling glob
An unheld glob costs nothing directly — it over-invalidates a cache. The cost is that the declaration stops being checked against the code, which is the property the gate's own header calls load-bearing: "a narrow glob is only safe while the gate can check it against the paths the tests really read". This is #9763's "prose was holding the radius" one level up: after the ablation, packages/**/*.object.ts is held by a comment in a test file, and a later edit that removed the glob as "unused" would go green while putting that sweep back in #7802's blind spot.
The prose in managed-extension-fields.test.ts used to promise the stale-declaration failure as the guard for exactly this. #10163 corrects the promise; it cannot supply the missing limb.
Direction (not a decision)
- A — extend
verify() with a per-glob holder check: a declared glob that matches nothing in its package's roster fails as unheld. Symmetric with the roster check already there, and it is the same data (info.literals / info.dirEntries) read the other way. Needs care with globs deliberately declared for a path only named in prose (serve.ts, check-nul-bytes.mjs, realtime-protocol.mdx today) — those are held by the flat literal collector, so the check must read the same roster the coverage limb reads, not the fs. - B — leave it, and rely on the per-glob comments naming their justifying test.
A is the one that keeps a narrow radius honest, which is the reason the glob list is allowed to be narrow at all.
⚠️Collision: the file is edited by open PR #10450, so this wants to land after it, not as a rider.
Refs
#10163 (where it was measured) · #10161 (gave plugin-auth its second escaping test, which is what made the gap reachable) · #9763 · #7802 · PR #10450 (holds the file)
Measured while implementing #10163. No gate is red today, and the failure is silent by construction. Filed unassigned.
Measured on
ceb33a9f12verify()has two staleness limbs and both are package-scoped::950-958);:960-967).There is no limb asking whether each declared glob is still held by something. The roster check at
:973-988runs the other direction: it fails when a test names a path no glob covers. A glob that covers nothing any more is never reported.That gap was invisible while most declaring packages had exactly one escaping test.
@objectstack/plugin-authnow has two, so it is reachable there today:Ablation. Reseed
packages/plugins/plugin-auth/src/managed-extension-fields.test.tsfromprocess.cwd()(the walk the detector deliberately does not resolve) and re-run the gate:packages/**/*.object.tsstays declared inCROSS_PACKAGE_TEST_INPUTSand inturbo.json, and nothing holds it. Restored; the tree is unchanged.Positive control, same ablation on
packages/platform-objects/src/managed-api-method-affordance-sweep.test.ts— that package's only escaping test — exits 1: "@objectstack/platform-objects declares a cross-package input radius, but no test in it reads outside the package any more." So the limb works exactly as far as its package scope reaches, and no further.Why this matters more than a dangling glob
An unheld glob costs nothing directly — it over-invalidates a cache. The cost is that the declaration stops being checked against the code, which is the property the gate's own header calls load-bearing: "a narrow glob is only safe while the gate can check it against the paths the tests really read". This is #9763's "prose was holding the radius" one level up: after the ablation,
packages/**/*.object.tsis held by a comment in a test file, and a later edit that removed the glob as "unused" would go green while putting that sweep back in #7802's blind spot.The prose in
managed-extension-fields.test.tsused to promise the stale-declaration failure as the guard for exactly this. #10163 corrects the promise; it cannot supply the missing limb.Direction (not a decision)
verify()with a per-glob holder check: a declared glob that matches nothing in its package's roster fails as unheld. Symmetric with the roster check already there, and it is the same data (info.literals/info.dirEntries) read the other way. Needs care with globs deliberately declared for a path only named in prose (serve.ts,check-nul-bytes.mjs,realtime-protocol.mdxtoday) — those are held by the flat literal collector, so the check must read the same roster the coverage limb reads, not the fs.A is the one that keeps a narrow radius honest, which is the reason the glob list is allowed to be narrow at all.
Refs
#10163 (where it was measured) · #10161 (gave plugin-auth its second escaping test, which is what made the gap reachable) · #9763 · #7802 · PR #10450 (holds the file)