Skip to content

Commit 7ca8e1f

Browse files
os-zhuangclaude
andcommitted
fix(ci): schedule Test Core for the ten declared cross-package test inputs 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
1 parent 0bfbeb7 commit 7ca8e1f

2 files changed

Lines changed: 67 additions & 29 deletions

File tree

.github/workflows/ci.yml

Lines changed: 61 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ jobs:
3838
docs: ${{ steps.changes.outputs.docs || 'true' }}
3939
core: ${{ steps.changes.outputs.core || 'true' }}
4040
console: ${{ steps.changes.outputs.console || 'true' }}
41-
# Gates the `test` job ONLY, ORed with `core` — see the `scripts`
42-
# filter below and the `test` job's `if:` (#9829).
43-
scripts: ${{ steps.changes.outputs.scripts || 'true' }}
41+
# Gates the `test` job ONLY, ORed with `core` — see the `crosspkg`
42+
# filter below and the `test` job's `if:` (#9829, #10015).
43+
crosspkg: ${{ steps.changes.outputs.crosspkg || 'true' }}
4444
# ── THE FILTER CONTRACT, both halves (#4928) ──────────────────────────
4545
#
4646
# Half 1 is the `|| 'true'` above: when in doubt, RUN EVERYTHING. It
@@ -110,30 +110,66 @@ jobs:
110110
- 'scripts/console-spec-probes.mjs'
111111
- 'scripts/assert-console-spec-injection.mjs'
112112
- '.github/workflows/ci.yml'
113-
# Test inputs that live OUTSIDE every package (#9829). Packages whose
114-
# suites read across their own boundary declare that radius in
113+
# Test inputs that live OUTSIDE every package (#9829, #10015). Packages
114+
# whose suites read across their own boundary declare that radius in
115115
# scripts/check-cross-package-test-inputs.mjs, and turbo.json mirrors each
116-
# as a `$TURBO_ROOT$/…` input. SIX of those declarations are rooted at
117-
# `scripts/` — @objectstack/spec (`scripts/**`), @objectstack/cli,
118-
# @objectstack/metadata-protocol and create-objectstack (five single
119-
# files) — and the step that pulls those packages back into the affected
120-
# set, `--union-into` in the `test` job below, is a step INSIDE that job.
121-
# So on a `scripts/`-only diff `core` was false, the job never started,
122-
# the union never ran, and the declarations bought nothing at PR time:
123-
# the merge queue was the first signal, which is the #7802 failure this
124-
# whole mechanism exists to prevent. Measured with picomatch, the matcher
125-
# dorny/paths-filter uses: a diff confined to
126-
# `scripts/sync-template-versions.mjs` yields core=false.
116+
# as a `$TURBO_ROOT$/…` input. The step that pulls those packages back into
117+
# the affected set, `--union-into` in the `test` job below, is a step
118+
# INSIDE that job — so when NO filter matches the changed path the job
119+
# never starts, the union never runs, and the declaration buys nothing at
120+
# PR time: the merge queue is the first signal, which is the #7802 failure
121+
# this whole mechanism exists to prevent.
127122
#
128-
# Deliberately a SEPARATE output rather than a widening of `core`,
129-
# because it gates ONE job — the `test` job ORs it with `core`, while
130-
# Build Core, Dogfood and Temporal Conformance stay filtered exactly as
131-
# they are today. `scripts/**` rather than the six declared paths: the
132-
# narrower filter does not exist while @objectstack/spec declares
133-
# `scripts/**`, and a hand-kept list of script paths is the failure mode
134-
# #7802 recorded ("a list you must remember to update").
135-
scripts:
123+
# Measured by instantiating every one of turbo.json's `$TURBO_ROOT$`
124+
# declarations to a real tracked file and running it through these filters
125+
# with picomatch — the matcher dorny/paths-filter uses — under BOTH 2.3.1
126+
# (what the action's own lockfile resolves and ncc-bundles) and 4.0.5 (what
127+
# this tree resolves); the two agree on every row. On `0bfbeb70c`, 19 of 64
128+
# declarations were core=false. #9829 closed the nine rooted at `scripts/`;
129+
# the four roots added below are the remaining ten (#10015).
130+
#
131+
# Deliberately a SEPARATE output rather than a widening of `core`, because
132+
# it gates ONE job — the `test` job ORs it with `core`, while Build Core,
133+
# Dogfood and Temporal Conformance stay filtered exactly as they are today.
134+
# That separation is what makes `content/**` affordable at all. Widening
135+
# `core` would put the whole core pipeline on every docs PR; here a
136+
# docs-only diff starts Test Core, whose package set is `turbo ls
137+
# --affected` (empty for such a diff) UNIONED with the packages whose
138+
# declarations matched — so it runs the declaring package, not the
139+
# workspace. Measured through the real `--union-into` on all three
140+
# content-only commits in the window below: `create-objectstack` alone,
141+
# all three times, against a positive control where a
142+
# `packages/lint/src/**` diff unions in @objectstack/spec and
143+
# @objectstack/core.
144+
#
145+
# WIDTH, per entry — each is exactly what some package declared, never a
146+
# root rounded up:
147+
# `scripts/**` and `content/**` are declared verbatim, by
148+
# @objectstack/spec and create-objectstack respectively, so the
149+
# narrower filter does not exist and a hand-kept list of the paths
150+
# inside them is the failure mode #7802 recorded ("a list you must
151+
# remember to update"). Eight further single-file `scripts/`
152+
# declarations and six narrower `content/docs/…` ones (spec, cli,
153+
# dogfood) sit inside the two.
154+
# The last three are a single file, a single file and a single subtree,
155+
# because that is what the declarations say — NOT `docs/**`,
156+
# `skills/**` or `.github/workflows/**`. Over the last 100 first-parent
157+
# commits of `main` at `0bfbeb70c`, those three roots would newly have
158+
# started Test Core on 5, 2 and 3 commits; the declared paths on ZERO.
159+
#
160+
# PRICE, re-measured over that same 100-commit window through this matcher:
161+
# Test Core scheduling goes 80/100 → 83/100. All three additions are
162+
# content-only docs commits, and each runs one package's suite.
163+
#
164+
# This list is a SECOND recognizer of those declarations and is kept in
165+
# step with them BY HAND: nothing fails today when a declaration lands in a
166+
# root no entry here covers. Closing that is #10379.
167+
crosspkg:
136168
- 'scripts/**'
169+
- 'content/**'
170+
- 'docs/audits/2026-07-unknown-key-strictness-ledger.md'
171+
- 'skills/objectstack-formula/**'
172+
- '.github/workflows/scaffold-e2e.yml'
137173
138174
test:
139175
# Sharded 3-way BY PACKAGE: a core-touching PR ran the affected suite
@@ -160,7 +196,7 @@ jobs:
160196
# in, never ANDed: two independent reasons to run this job, and it skips only
161197
# when BOTH filters explicitly said false, so the contract's "when in doubt,
162198
# RUN EVERYTHING" posture is unchanged (#9829).
163-
if: ${{ !cancelled() && (needs.filter.outputs.core != 'false' || needs.filter.outputs.scripts != 'false') }}
199+
if: ${{ !cancelled() && (needs.filter.outputs.core != 'false' || needs.filter.outputs.crosspkg != 'false') }}
164200
runs-on: ubuntu-latest
165201
# Backstop only — the stall guard on the test steps is the primary
166202
# detector for a #4250-style hang and fires well before this. 30 min is

scripts/sync-template-versions.mjs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -397,15 +397,17 @@ function main() {
397397
// and with it the vitest, on precisely the PR that changes the rewriter.
398398
//
399399
// `core` is still false for such a diff; `core` was never widened. What changed
400-
// is that the `test` job now ORs in a SECOND filter output — `scripts:` /
401-
// `'scripts/**'` — and skips only when BOTH say false (#9829).
400+
// is that the `test` job now ORs in a SECOND filter output — `crosspkg:`, which
401+
// carries `'scripts/**'` among its entries — and skips only when BOTH say false
402+
// (#9829; the output was named `scripts:` until #10015 generalised it to the
403+
// other four roots that declare cross-package test inputs).
402404
//
403405
// Re-measured against the merged workflow, with picomatch 2.3.1: that is the
404406
// version dorny/paths-filter@v4's own lockfile resolves and ncc-bundles, NOT the
405407
// 4.0.5 in this tree — the two agree on these globs, but the action is what
406408
// runs, so it is the one to quote. For a diff confined to this file:
407-
// `core=false`, `scripts=true`, so
408-
// `!cancelled() && (core != 'false' || scripts != 'false')` is TRUE. The job
409+
// `core=false`, `crosspkg=true`, so
410+
// `!cancelled() && (core != 'false' || crosspkg != 'false')` is TRUE. The job
409411
// runs.
410412
//
411413
// The job running is necessary, NOT sufficient — the shard tests a FILTERED

0 commit comments

Comments
 (0)