Found while working #9348 (measuring whether create-objectstack#test is scheduled when scripts/sync-template-versions.mjs changes). Out of that card's scope — it fixes one script by adding a lint.yml self-test; this is the general shape. Filed unassigned.
What was measured
scripts/check-cross-package-test-inputs.mjs exists so that a test whose real inputs escape its package is still scheduled when those inputs change (#7802). Layer A of that mechanism is the --union-into call in ci.yml's Compute this shard's package set step.
Three of the twelve declarations in CROSS_PACKAGE_TEST_INPUTS point under scripts/:
@objectstack/spec declares scripts/**
@objectstack/cli declares scripts/check-nul-bytes.mjs
create-objectstack declares scripts/sync-template-versions.mjs
The union step works. Measured on a worktree at 9ff11921a, with a diff confined to scripts/sync-template-versions.mjs:
$ node scripts/check-cross-package-test-inputs.mjs --union-into turbo-ls.json --changed changed.txt
Cross-package scans pulled into this run because the diff touched their declared inputs:
+ @objectstack/spec (declared glob matched scripts/sync-template-versions.mjs)
+ create-objectstack (declared glob matched scripts/sync-template-versions.mjs)
But that step is a step inside ci.yml's test job, and the test job is:
if: ${{ !cancelled() && needs.filter.outputs.core != 'false' }}
The core filter is packages/**, examples/**, apps/!(docs)/**, package.json, pnpm-lock.yaml, tsconfig.json, .github/workflows/ci.yml. No entry matches any path under scripts/. Measured with picomatch 4.0.5, the matcher dorny/paths-filter@v4 uses:
scripts/sync-template-versions.mjs core=false docs=false console=false
packages/create-objectstack/src/x.ts core=true docs=false console=false (control)
package.json core=true docs=false console=false (control)
scripts/build-console.sh core=false docs=false console=true (control)
So on a PR whose diff is confined to scripts/, Test Core is skipped in full, the union never executes, and the very declarations written to make those scans reachable are unreachable at PR time.
Two adjacent layers were measured and neither substitutes:
turbo ls --affected returns zero packages for that diff (control: a package-local edit returns 1, so the probe is live). A $TURBO_ROOT$ entry in a task's inputs moves the task hash — measured, c71674900a9d7591 to 5d4c8751951575c1 for create-objectstack#test — which is Layer B, the thing that stops a cached green. It is not what selects packages.
merge_group builds set every filter output to 'true' and partition the full package list, so the queue does run these suites. That is the repo's existing safety net, and its cost is on the record twice in this gate's own ledger: "Undeclared, cli was outside the affected set, so PR CI was green and the merge queue was the first signal — it dequeued the PR and took two unrelated PRs down as batch collateral."
Why this is worth recording rather than patching in place
Adding scripts/** to core would put the full 3-shard Test Core matrix, Build Core and Dogfood on every scripts-only PR — the affected-subset optimisation those shards exist for, given back. Adding individual script paths reproduces the hand-kept-list failure mode this gate's own header calls out ("a list you must remember to update is exactly the failure mode that produced #7802"). Neither is obviously right, which is why this is a finding and not a patch.
Same family as #9710 (packages/spec/** is not in ci.yml's console filter), from the other direction.
Shape of a fix (not a ruling)
Options, roughly in increasing cost:
- Derive the
core filter's scripts/ entries from CROSS_PACKAGE_TEST_INPUTS at gate time — a --verify clause requiring every declared glob under scripts/ to be named in ci.yml's core filter, the same way --verify already requires a matching turbo.json input. Keeps the radius narrow and cannot be forgotten.
- Add a fourth filter output (
scripts) that ORs into the test job's condition only.
- Accept the queue as the signal for scripts-only PRs and say so where a reader will look.
Refs: #9348 (measured there), #7802 (the defect the mechanism exists for), #9710 (sibling filter gap).
Generated by Claude Code
Found while working #9348 (measuring whether
create-objectstack#testis scheduled whenscripts/sync-template-versions.mjschanges). Out of that card's scope — it fixes one script by adding a lint.yml self-test; this is the general shape. Filed unassigned.What was measured
scripts/check-cross-package-test-inputs.mjsexists so that a test whose real inputs escape its package is still scheduled when those inputs change (#7802). Layer A of that mechanism is the--union-intocall in ci.yml'sCompute this shard's package setstep.Three of the twelve declarations in
CROSS_PACKAGE_TEST_INPUTSpoint underscripts/:@objectstack/specdeclaresscripts/**@objectstack/clideclaresscripts/check-nul-bytes.mjscreate-objectstackdeclaresscripts/sync-template-versions.mjsThe union step works. Measured on a worktree at
9ff11921a, with a diff confined toscripts/sync-template-versions.mjs:But that step is a step inside ci.yml's
testjob, and thetestjob is:The
corefilter ispackages/**,examples/**,apps/!(docs)/**,package.json,pnpm-lock.yaml,tsconfig.json,.github/workflows/ci.yml. No entry matches any path underscripts/. Measured with picomatch 4.0.5, the matcherdorny/paths-filter@v4uses:So on a PR whose diff is confined to
scripts/, Test Core is skipped in full, the union never executes, and the very declarations written to make those scans reachable are unreachable at PR time.Two adjacent layers were measured and neither substitutes:
turbo ls --affectedreturns zero packages for that diff (control: a package-local edit returns 1, so the probe is live). A$TURBO_ROOT$entry in a task'sinputsmoves the task hash — measured,c71674900a9d7591to5d4c8751951575c1forcreate-objectstack#test— which is Layer B, the thing that stops a cached green. It is not what selects packages.merge_groupbuilds set every filter output to'true'and partition the full package list, so the queue does run these suites. That is the repo's existing safety net, and its cost is on the record twice in this gate's own ledger: "Undeclared, cli was outside the affected set, so PR CI was green and the merge queue was the first signal — it dequeued the PR and took two unrelated PRs down as batch collateral."Why this is worth recording rather than patching in place
Adding
scripts/**tocorewould put the full 3-shard Test Core matrix, Build Core and Dogfood on every scripts-only PR — the affected-subset optimisation those shards exist for, given back. Adding individual script paths reproduces the hand-kept-list failure mode this gate's own header calls out ("a list you must remember to update is exactly the failure mode that produced #7802"). Neither is obviously right, which is why this is a finding and not a patch.Same family as #9710 (
packages/spec/**is not in ci.yml'sconsolefilter), from the other direction.Shape of a fix (not a ruling)
Options, roughly in increasing cost:
corefilter'sscripts/entries fromCROSS_PACKAGE_TEST_INPUTSat gate time — a--verifyclause requiring every declared glob underscripts/to be named in ci.yml'scorefilter, the same way--verifyalready requires a matchingturbo.jsoninput. Keeps the radius narrow and cannot be forgotten.scripts) that ORs into thetestjob's condition only.Refs: #9348 (measured there), #7802 (the defect the mechanism exists for), #9710 (sibling filter gap).
Generated by Claude Code