Skip to content

Commit 1bd0023

Browse files
committed
ci(console-pin): a head that moves the spec's entry layout runs Console Pin Gate and misses its dist cache (#20765)
The `console` filter gains packages/spec/package.json (the exports map the OBJECTSTACK_SPEC_DIST hook and the probe derivation resolve) and packages/spec/tsup.config.ts (the entry list), and the console-pin dist-cache key now hashes every build input the filter names: those two, plus the two scripts that derive and stamp the injection probes. A selection the key does not follow restores the pre-change dist and skips the build step the assertion lives in, so the run is green without judging the change. check-ci-filter-parity gains a second subject that holds the filter and the key in step: literal entries only, one key spelling, every hashed path selected, every selected path hashed or a declared guard. Claude-Session: https://claude.ai/code/session_017VaLJnYwhPsanVCe9dMCJU Co-authored-by: Claude <noreply@anthropic.com>
1 parent bee75ce commit 1bd0023

3 files changed

Lines changed: 429 additions & 45 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 54 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)