@@ -183,16 +183,38 @@ jobs:
183183 # it matches neither filter above: a pin-only diff skipped `core` and
184184 # `docs` alike, which is how #4288 moved the pin 76 commits with six of
185185 # fourteen checks skipped and nothing anywhere building the new SHA.
186- # The last two entries are the "a change to the guard runs the guard"
187- # rule the repo applies to every other filtered gate; they are close to
188- # free here because an unmoved pin hits the dist cache.
186+ #
187+ # Every entry is one of two kinds, and scripts/check-ci-filter-parity.mjs
188+ # holds each one to its kind (#20765):
189+ # - a BUILD INPUT: something the console build reads to produce the
190+ # cached dist or the injection stamp inside it. Each one is ALSO
191+ # hashed into the `console-pin` job's dist-cache key. A head that
192+ # selects the job by moving a build input and then restores the old
193+ # dist from cache runs the gate without judging the change, because
194+ # the build-time assertion lives inside the build step a hit skips.
195+ # - a GUARD: code every run executes, cache hit or miss (the two check
196+ # scripts, this workflow). That is the "a change to the guard runs the
197+ # guard" rule the repo applies to every other filtered gate, and it is
198+ # close to free here because an unmoved key hits the dist cache.
199+ # The two packages/spec entries are the spec's ENTRY LAYOUT: the exports
200+ # map that objectui's OBJECTSTACK_SPEC_DIST hook and the probe derivation
201+ # both resolve entry by entry, and the tsup entry list that decides which
202+ # built file each entry points at. A head that moves them runs this gate
203+ # before it enqueues. #20695 moved the migration registries to a new
204+ # entry, skipped this job on every push, and went red first in the merge
205+ # queue, where a check outside the required set cannot stop a merge.
206+ # ⛔ Not packages/spec/**: the rest of the spec is bundled CONTENT, and
207+ # its lag behind a cached dist is the ruled cache design (#9667, #9706;
208+ # see scripts/check-console-injection.mjs).
189209 console:
190210 - '.objectui-sha'
191211 - 'scripts/build-console.sh'
192212 - 'scripts/check-console-sha.mjs'
193213 - 'scripts/check-console-injection.mjs'
194214 - 'scripts/console-spec-probes.mjs'
195215 - 'scripts/assert-console-spec-injection.mjs'
216+ - 'packages/spec/package.json'
217+ - 'packages/spec/tsup.config.ts'
196218 - '.github/workflows/ci.yml'
197219 # Test inputs that live OUTSIDE every package (#9829, #10015). Packages
198220 # whose suites read across their own boundary declare that radius in
@@ -2307,10 +2329,12 @@ jobs:
23072329 # already covered in practice; the hole is a hand-edited pin, or that single
23082330 # line cherry-picked out of another branch.
23092331 #
2310- # Cost is real but narrowly aimed. A moved pin is the only trigger that pays
2311- # the full clone + vite build, and #4288's 76 commits of staleness are the
2312- # measure of how rare that is. The other triggers (this file, the drift guard)
2313- # leave the pin untouched, so they hit the dist cache and cost about a minute.
2332+ # Cost is real but narrowly aimed. A moved BUILD INPUT (the pin, the build and
2333+ # probe scripts, the spec's entry layout; see the `console` filter's comment)
2334+ # is the only trigger that always pays the full clone + vite build: 32 of the
2335+ # 3219 first-parent commits on main from 2026-08-31 to 9905e61ca2 moved one.
2336+ # The other triggers (this file, the check scripts) leave the key untouched,
2337+ # so they cost about a minute whenever the entry is still in the cache.
23142338 #
23152339 # Scope, stated plainly: this proves the PINNED SHA builds. It does NOT cover a
23162340 # packages/client change breaking the injected-client bundle — that input lives
@@ -2402,10 +2426,20 @@ jobs:
24022426 - name : Build @objectstack/client and its dependencies
24032427 run : pnpm exec turbo run build --filter=@objectstack/client... --concurrency=4
24042428
2405- # Same key as release.yml's "Cache vendored Console dist" step. Actions
2406- # caches are repo-scoped, so a PR reads whatever main already built for this
2407- # pin — that is what makes the non-pin triggers cheap. Only a pin the repo
2408- # has never built misses, and a miss is exactly when this gate has work to do.
2429+ # Keyed on every BUILD INPUT the `filter` job's `console` list names (the
2430+ # pin, the build script, the two scripts that derive and stamp the injection
2431+ # probes, and the spec's entry layout) and on nothing else.
2432+ # scripts/check-ci-filter-parity.mjs holds this key and that list in step
2433+ # (#20765). A head that moves a build input MISSES, so the build and the
2434+ # assertion inside it judge the change on the PR head, not first in the
2435+ # merge queue. A head that moves only a guard hits, which is what keeps
2436+ # those triggers cheap. A PR reads what main, or its own earlier pushes,
2437+ # saved under the same key.
2438+ #
2439+ # ⚠️ release.yml's restore step still keys on the pin and the build script
2440+ # alone, so the two workflows no longer share dist entries: each builds its
2441+ # own on a miss. That file's comment asks for the two to stay in step; which
2442+ # way to reconcile them is recorded on #20765.
24092443 #
24102444 # Split restore/save where release.yml uses the combined action, because the
24112445 # combined form's post-step saves even when the job FAILED. build-console.sh
@@ -2414,12 +2448,12 @@ jobs:
24142448 # run restores it, skips the build, and sails through the stamp check. Saving
24152449 # only once every assertion below is green is what lets a restored dist carry
24162450 # the same guarantees as a freshly built one.
2417- - name : Restore vendored Console dist (keyed on the objectui pin)
2451+ - name : Restore vendored Console dist (keyed on the pin and its other build inputs )
24182452 id : console-dist
24192453 uses : actions/cache/restore@v6
24202454 with :
24212455 path : packages/console/dist
2422- key : ${{ runner.os }}-console-dist-${{ hashFiles('.objectui-sha', 'scripts/build-console.sh') }}
2456+ key : ${{ runner.os }}-console-dist-${{ hashFiles('.objectui-sha', 'scripts/build-console.sh', 'scripts/assert-console-spec-injection.mjs', 'scripts/console-spec-probes.mjs', 'packages/spec/package.json', 'packages/spec/tsup.config.ts' ) }}
24232457
24242458 # The gate. Shallow-clones objectui at the pinned SHA, builds
24252459 # @object-ui/console against this tree's client, copies the dist into
@@ -2458,10 +2492,11 @@ jobs:
24582492 # The spec-injection half of the same question, and the reason it is a
24592493 # SEPARATE step from build-console.sh (#9667).
24602494 #
2461- # The dist cache key is hashFiles('.objectui-sha', 'scripts/build-console.sh')
2462- # — packages/spec is deliberately NOT in it, because adding it would bust the
2463- # key on every spec change and force a ~20 min cold console rebuild on a repo
2464- # doing ~18 merges a day. The cost model stays; what does not stay is the
2495+ # The dist cache key carries only the spec's ENTRY LAYOUT (package.json and
2496+ # tsup.config.ts, see the restore step). The rest of packages/spec is
2497+ # deliberately NOT in it, because adding it would bust the key on every spec
2498+ # change and force a ~20 min cold console rebuild on a repo doing ~18 merges
2499+ # a day. The cost model stays; what does not stay is the
24652500 # silence. scripts/assert-console-spec-injection.mjs runs INSIDE
24662501 # build-console.sh, so the `if: cache-hit != 'true'` above skips it exactly
24672502 # when the dist was NOT built here — which is the run that most needs asking.
@@ -2482,7 +2517,7 @@ jobs:
24822517 # a poisoned entry is reachable from here and must not pass quietly.
24832518 - name : Verify the restored Console dist bundles this tree's spec
24842519 env :
2485- CONSOLE_DIST_CACHE_KEY : ${{ runner.os }}-console-dist-${{ hashFiles('.objectui-sha', 'scripts/build-console.sh') }}
2520+ CONSOLE_DIST_CACHE_KEY : ${{ runner.os }}-console-dist-${{ hashFiles('.objectui-sha', 'scripts/build-console.sh', 'scripts/assert-console-spec-injection.mjs', 'scripts/console-spec-probes.mjs', 'packages/spec/package.json', 'packages/spec/tsup.config.ts' ) }}
24862521 run : pnpm check:console-injection --require-stamp
24872522
24882523 # Reached only with every assertion above green (see the split-restore note).
@@ -2495,4 +2530,4 @@ jobs:
24952530 uses : actions/cache/save@v6
24962531 with :
24972532 path : packages/console/dist
2498- key : ${{ runner.os }}-console-dist-${{ hashFiles('.objectui-sha', 'scripts/build-console.sh') }}
2533+ key : ${{ runner.os }}-console-dist-${{ hashFiles('.objectui-sha', 'scripts/build-console.sh', 'scripts/assert-console-spec-injection.mjs', 'scripts/console-spec-probes.mjs', 'packages/spec/package.json', 'packages/spec/tsup.config.ts' ) }}
0 commit comments