diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 162b2c31d5..21ebfc9534 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,6 +38,9 @@ jobs: docs: ${{ steps.changes.outputs.docs || 'true' }} core: ${{ steps.changes.outputs.core || 'true' }} console: ${{ steps.changes.outputs.console || 'true' }} + # Gates the `test` job ONLY, ORed with `core` — see the `scripts` + # filter below and the `test` job's `if:` (#9829). + scripts: ${{ steps.changes.outputs.scripts || 'true' }} # ── THE FILTER CONTRACT, both halves (#4928) ────────────────────────── # # Half 1 is the `|| 'true'` above: when in doubt, RUN EVERYTHING. It @@ -107,6 +110,30 @@ jobs: - 'scripts/console-spec-probes.mjs' - 'scripts/assert-console-spec-injection.mjs' - '.github/workflows/ci.yml' + # Test inputs that live OUTSIDE every package (#9829). Packages whose + # suites read across their own boundary declare that radius in + # scripts/check-cross-package-test-inputs.mjs, and turbo.json mirrors each + # as a `$TURBO_ROOT$/…` input. SIX of those declarations are rooted at + # `scripts/` — @objectstack/spec (`scripts/**`), @objectstack/cli, + # @objectstack/metadata-protocol and create-objectstack (five single + # files) — and the step that pulls those packages back into the affected + # set, `--union-into` in the `test` job below, is a step INSIDE that job. + # So on a `scripts/`-only diff `core` was false, the job never started, + # the union never ran, and the declarations bought nothing at PR time: + # the merge queue was the first signal, which is the #7802 failure this + # whole mechanism exists to prevent. Measured with picomatch, the matcher + # dorny/paths-filter uses: a diff confined to + # `scripts/sync-template-versions.mjs` yields core=false. + # + # Deliberately a SEPARATE output rather than a widening of `core`, + # because it gates ONE job — the `test` job ORs it with `core`, while + # Build Core, Dogfood and Temporal Conformance stay filtered exactly as + # they are today. `scripts/**` rather than the six declared paths: the + # narrower filter does not exist while @objectstack/spec declares + # `scripts/**`, and a hand-kept list of script paths is the failure mode + # #7802 recorded ("a list you must remember to update"). + scripts: + - 'scripts/**' test: # Sharded 3-way BY PACKAGE: a core-touching PR ran the affected suite @@ -129,8 +156,11 @@ jobs: name: Test Core (${{ matrix.shard }}/3) needs: filter # "Skip only when the filter EXPLICITLY said no core paths changed" — see - # THE FILTER CONTRACT on the filter job's outputs (#4928). - if: ${{ !cancelled() && needs.filter.outputs.core != 'false' }} + # THE FILTER CONTRACT on the filter job's outputs (#4928). `scripts` is ORed + # in, never ANDed: two independent reasons to run this job, and it skips only + # when BOTH filters explicitly said false, so the contract's "when in doubt, + # RUN EVERYTHING" posture is unchanged (#9829). + if: ${{ !cancelled() && (needs.filter.outputs.core != 'false' || needs.filter.outputs.scripts != 'false') }} runs-on: ubuntu-latest # Backstop only — the stall guard on the test steps is the primary # detector for a #4250-style hang and fires well before this. 30 min is