Skip to content

fix(ci): schedule Test Core for the ten declared cross-package test inputs in content/, docs/, skills/ and .github/workflows/ - #10388

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-10015-ci-core-filter-cross-package-inputs
Aug 20, 2026
Merged

fix(ci): schedule Test Core for the ten declared cross-package test inputs in content/, docs/, skills/ and .github/workflows/#10388
os-zhuang merged 1 commit into
mainfrom
claude/issue-10015-ci-core-filter-cross-package-inputs

Conversation

@claude

@claude claude Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Fixes #10015.

turbo.json declares, per task, the paths outside a package that the package's tests read — 64
$TURBO_ROOT$/… entries today, each mirroring a CROSS_PACKAGE_TEST_INPUTS declaration. Ten of
them could not schedule Test Core at PR time, so the --union-into step those declarations depend
on — a step inside the test job — never ran, and the merge queue was the first signal (#7802).

This generalises the separate filter output #9829 / #10014 landed for scripts/ to the four other
roots: scriptscrosspkg, plus content/**, the strictness-ledger audit file,
skills/objectstack-formula/** and .github/workflows/scaffold-e2e.yml.

All measurements below were taken at head 7ca8e1f2e against base 0bfbeb70c (origin/main
merged in at that sha; no commit between the card's base and this one touches ci.yml, turbo.json,
sync-template-versions.mjs or check-cross-package-test-inputs.mjs).

Premise: re-derived, not inherited — and the card's ten are exactly right

Every one of the 64 $TURBO_ROOT$ declarations instantiated to a real tracked file and run
through ci.yml's filters with picomatch(pattern, {dot: true}) — the matcher
dorny/paths-filter@v4 builds its rules with (src/filter.ts, createRuleItem, default
predicate-quantifier: some) — under both 2.3.1 (what the action's package.json pins and
ncc-bundles) and 4.0.5 (what this tree's lockfile resolves).

On 0bfbeb70c, 19 of 64 declarations are core=false; 9 of those are the scripts/ rows
already covered by #10014's output, leaving 10 unschedulable — the card's ten, task for task and
path for path:

UNSCHEDULABLE declared test inputs (no test-gating filter matches): 10 of 64
  by top-level root: content=7  docs=1  skills=1  .github=1

The two picomatch majors agree on all 64 rows × 4 filters (IDENTICAL). That comparator has a
positive control: flipping a single verdict cell in one copy makes the same comparator print
DIVERGENT.

Two re-derivation deltas from the card, neither of which moves the ten:

card (filed 2026-08-19) here (0bfbeb70c)
$TURBO_ROOT$ declarations 58 64
core=false 16 19 (all 3 new ones under scripts/, already covered by #10014)
unschedulable after #10014 10 10 — unchanged

The card's line numbers for the last three rows shifted (161→166, 195→200, 197→203) because
turbo.json gained declarations in between; the task and path are identical.

The fix schedules what it claims — all ten flip false → true

Same instantiate-to-a-real-tracked-file method, base tree vs this head:

turbo.json line task declared input instantiated tracked file test-gated BEFORE AFTER
35 @objectstack/spec#test content/docs/references/** content/docs/references/ai/agent.mdx false true
42 @objectstack/spec#test content/docs/api/error-catalog.mdx same false true
43 @objectstack/spec#test docs/audits/2026-07-unknown-key-strictness-ledger.md same false true
72 @objectstack/cli#test content/docs/deployment/cli.mdx same false true
73 @objectstack/cli#test content/docs/deployment/index.mdx same false true
74 @objectstack/cli#test content/docs/permissions/authentication.mdx same false true
154 @objectstack/dogfood#test content/docs/protocol/kernel/realtime-protocol.mdx same false true
166 @objectstack/formula#test skills/objectstack-formula/** skills/objectstack-formula/SKILL.md false true
200 create-objectstack#test content/** content/blog/context-window-is-the-constraint.mdx false true
203 create-objectstack#test .github/workflows/scaffold-e2e.yml same false true

flipped false -> true: 10 · unchanged: 54 · flipped true -> false: 0, and on this head the
harness prints UNSCHEDULABLE … 0 of 64 under both picomatch majors. That zero's positive
control is the identical harness on the base tree printing 10 of 64.

It does not over-schedule — swept over every tracked file

base this head
tracked files the test gate matches 5269 5707
files that LOST scheduling 0
files that GAINED scheduling 438

Every gained file traced to one of the four patterns I added — content/** 434, the ledger file 1,
skills/objectstack-formula/** 2, scaffold-e2e.yml 1 — UNATTRIBUTED: 0. Both zero-hits are
controlled: swapping the two trees makes the same census report LOST: 438, and withholding
content/** from the attribution list makes the same probe report UNATTRIBUTED: 434. Both
picomatch majors produce a byte-identical census.

Scheduling volume, per root, re-measured

Replaying the last 100 first-parent commits of main at 0bfbeb70c through the same matcher:

Test Core scheduled BEFORE (core || scripts):  80/100
Test Core scheduled AFTER  (core || crosspkg): 83/100
NEWLY scheduled: 3   — all three via content/**, all three content-only docs commits
candidate commits touching it of those, newly scheduling Test Core
scripts/** (already landed) 46 0
content/** 13 3
docs/audits/2026-07-…-ledger.md 0 0
skills/objectstack-formula/** 0 0
.github/workflows/scaffold-e2e.yml 0 0

and the roots not taken, priced on the same window: docs/** 5, skills/** 2,
.github/workflows/** 3. Nine of the ten declarations are a single file or a single subtree, so
naming the declared path costs zero here where the root would have cost ten between them.

Why content/** is affordable, and why it is not narrowable

It is not narrowable: create-objectstack#test declares content/** verbatim
(src/template-consistency.test.ts reads doc frontmatter to decide which templates are internal), so
no narrower entry covers that row — the same argument #10014 made for scripts/**.

It is affordable because this is a separate output ORed into the test job only, never a
widening of core: Build Core, Dogfood and Temporal Conformance stay filtered exactly as they are.
A docs-only diff starts Test Core, whose package set is turbo ls --affected (empty for such a
diff) unioned with the packages whose declarations matched. Measured through the real
--union-into on all three newly-scheduled commits:

0bfbeb70c  ->  1 package: create-objectstack
fdde7b202  ->  1 package: create-objectstack
de28d38c7  ->  1 package: create-objectstack
positive control, a packages/lint/src/** diff -> 2: @objectstack/spec, @objectstack/core

One package's suite, and it is the package that declared the input — i.e. exactly the coverage the
declaration was bought for, not a docs PR running the core suite.

Second file: prose my own rename falsified

scripts/sync-template-versions.mjs's "GAP 1 — SCHEDULING, NOW CLOSED" block names the output
literally (`scripts:` / `'scripts/**'`, and `core=false`, `scripts=true`). Renaming the
output makes those two sentences false, so they are corrected in the same commit. Substance
unchanged — scripts/** is still an entry and the OR is still there; only the output's name moved.

Known residual, filed rather than fixed here

The five entries are a second recognizer of the declarations, kept in step by hand: nothing
fails today when a declaration lands in a root no entry covers. Closing that is a mechanism
decision (derive the entries at CI time — unblocked now that #9826 has merged — versus a static
parity gate), which is why it is #10379 and not a rider on this PR. The workflow comment says so at
the point of use.

Gates

Re-derived from the real diff with node scripts/pm/dispatch-gates.mjs (no paths passed — it takes
the change set from the merge base itself). That run named two families the path-hypothetical run
did not
, both pulled in by the sync-template-versions.mjs edit. All named families run at
7ca8e1f2e, each verdict quoted from the gate's own output line:

check:node-version                 check-node-version: OK (29 setup-node step(s) across 26 workflow(s), all on Node 22).
check:workflow-status-functions    ✓ --self-test: 34 assertions over temp fixture roots (real scan() path)
                                   OK (scanned 26 workflow file(s), 45 job(s), 23 job-level if: expression(s);
                                   10 read needs.*.outputs.*, all naming a status function).
check:required-contexts            ✓ --self-test: 124 assertions … + the live required-set diff …
                                   ci.yml:test-gate → 'Test Core'   (+ dogfood-gate, build-core, temporal-conformance)
check:shard-attestation            ✓ --self-test: 92 assertions (dominance experiment + both #6082 counter-examples …)
                                   ✓ 2 aggregate gate(s) count 3 declared leg(s) across 3 attesting job(s).
check:cross-package-test-inputs    All 60 self-test cases passed.
                                   OK: 12 package(s) read outside themselves, all declared, and turbo.json
                                   hashes every declared glob.
check:template-version-sync        ✓ --self-test: 40 assertions over temp fixtures, running the real CLI.
check:nul-bytes                    ✓ --self-test: 75 assertions over a temp git repo (real scan() path)
                                   OK (scanned 6115 text file(s) … no raw ASCII control bytes).

check:filter-alias-parity was named in the dispatch prompt but not by the derivation, and the
derivation is right: it guards ObjectQL's where/filter/filters/$filter wire-alias slots
(#8002), nothing to do with dorny/paths-filter. Not run.

These are all pure source-text scanners — none builds or reads dist/ — so per the resource rules
they were run outside os-verify-lock.sh, deliberately and declared here. Nothing in this diff
compiles.

Clause ② — declared, and not reached. The path limb is packages/spec/src/**; this diff is
.github/workflows/ci.yml and scripts/sync-template-versions.mjs. On content: no contract
accept/reject behaviour changes and no public surface widens — the change is CI scheduling only.

No changeset (skip-changeset): .github/workflows/** and a root scripts/*.mjs comment
publish nothing. Judged by publish surface, not habit — nothing here reaches a packages/* that
ships.


Generated by Claude Code

…nputs in content/, docs/, skills/ and .github/workflows/

Generalises the separate filter output #9829 landed for `scripts/` to the four
other roots that `CROSS_PACKAGE_TEST_INPUTS` declares: the output is renamed
`scripts` -> `crosspkg` and gains `content/**`, the strictness-ledger audit file,
`skills/objectstack-formula/**` and `.github/workflows/scaffold-e2e.yml`.

Still a SEPARATE output ORed into the `test` job only, never a widening of
`core`, which is what makes `content/**` affordable: a docs-only diff starts
Test Core, whose package set is the (empty) affected set unioned with the
declaring packages -- measured as `create-objectstack` alone on all three
content-only commits in the sampled window.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DdCnBGcHeufjrq7drTD3wt
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 20, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review August 20, 2026 17:40
@os-zhuang
os-zhuang enabled auto-merge August 20, 2026 17:40
@github-actions github-actions Bot added the ci/cd label Aug 20, 2026
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 20, 2026
Merged via the queue into main with commit fda3d67 Aug 20, 2026
27 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-10015-ci-core-filter-cross-package-inputs branch August 20, 2026 18:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ci.yml's core filter misses ten more declared cross-package test inputs — content/ (7), docs/, skills/, .github/workflows/

1 participant