ci(console-pin): a head that moves the spec's entry layout runs Console Pin Gate and misses its dist cache - #20945
Merged
objectstack-fleet[bot] merged 3 commits intoSep 30, 2026
Conversation
…le 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 <[email protected]>
…ssue-20765-console-filter-spec-entry
…e.yml's key comment, say the two dist keys now differ (#20765) ci.yml's console dist key hashes a superset of release.yml's since the previous commit, so three pieces of prose went false: the header of scripts/check-console-injection.mjs restated the old key and said packages/spec was out of the console filter, its remedy() fallback printed that key as the one to delete, and release.yml asked the two keys to stay in step. The header and the fallback now point at each workflow's restore step instead of carrying a third copy of the key, and release.yml's comment says its narrower key is deliberate. No key, step or env changes. Claude-Session: https://claude.ai/code/session_017VaLJnYwhPsanVCe9dMCJU Co-authored-by: Claude <[email protected]>
objectstack-fleet
Bot
deleted the
claude/issue-20765-console-filter-spec-entry
branch
September 30, 2026 22:49
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #20765
Clause-②: no
A head that moves the spec's entry layout now runs
Console Pin Gateon the PR, and misses the dist cache so the gate judges it. Before this, such a head skipped the job on every push, and the first run was the merge-queue build, where a check outside the required set cannot stop a merge (#20695:mainred on this check for about 2 h 17 min).What changes
ci.ymlfilterjob,console:list gainspackages/spec/package.jsonandpackages/spec/tsup.config.ts. The comment above the list now says which entries are build inputs and which are guards, and why the rest ofpackages/specstays out.ci.ymlconsole-pinjob, dist-cache key (all three spellings: restore, theCONSOLE_DIST_CACHE_KEYenv, save) now hashes every build input the list names: the pin,scripts/build-console.sh, the two probe scripts (scripts/assert-console-spec-injection.mjs,scripts/console-spec-probes.mjs), and the two spec files. Comments that described the old key are corrected. The restore step name now says what it is keyed on.scripts/check-ci-filter-parity.mjsgains a second subject,judgeConsole, run bymain()in both lint.yml legs. It holds five things of the checked-in ci.yml: everyconsoleentry is a literal path; the key is spelled one way everywhere the job uses it; every hashed path is aconsoleentry; everyconsoleentry is hashed or a declared guard (CONSOLE_GUARDS: ci.yml and the two check scripts, which run on a hit and a miss alike); and every declared guard is still listed and not hashed. There is a new self-test battery (8) with 20 cases, and the battery floor goes from 7 to 8.scripts/pm/check-expected-skips.mjs: theConsole Pin Gaterow's reason text names the two spec files. Only the prose changes. The row's gate, the job'sif:and the roster shape are unchanged.scripts/check-console-injection.mjs(comments and theremedy()fallback text only; seat ruling 5920200169, Q2): the header no longer restates the dist key. It names the two restore steps that each spell one, and says the keys now differ. Its section is renamed "Why the rest of packages/spec is NOT in ci.yml's console filter" and says the spec's entry layout is in both the filter and ci.yml's key. Theremedy()fallback, used only whenCONSOLE_DIST_CACHE_KEYis unset, printsgh cache delete "KEY"plus a line naming where each workflow spells its key, instead of a stale copy of the old key..github/workflows/release.yml(comment only; no key, step or env change; seat ruling 5920200169, Q1 = B): the comment above the dist restore step says its key hashes the pin and the build script, ci.yml's hashes a superset, the two workflows no longer share dist entries, and the narrower key is deliberate.Why the key had to move too (H1, measured)
On a cache hit the job skips
Build the Console SPA, andassert-console-spec-injection.mjsruns inside that step. What runs on a hit ischeck:console-injection, which replays the stamp the earlier build wrote. The only thing it reads from the tree is the probe-expiry check over the union of all exports entries, and moving text between entries does not change that union.Restore vendored Console disttook 0 s,Build the Console SPA…ran 23:37:45 to 23:41:37 and failed, and the only failure annotation is "Process completed with exit code 2" (the assert script's inconclusive exit, "Neither spec appears in the built console"). The job log itself could not be read from this container because the log blob host is refused by the egress policy../migrationsentry the console does not import; the assert and probe scripts as they stood at9c8f113c~1):node scripts/check-console-injection.mjs --dist dist-pre --spec tree-post --require-stampprints "✓ @objectstack/spec: the dist carries this tree's copy, and not the published one." with exit 0. The hit is green without judging the change.merge_group, 32 of the 38 finishedConsole Pin Gateruns from 2026-09-30T13:46Z to 20:23Z rebuilt, and 6 hit. On the hourlyschedule, 25 of 30 runs from 2026-09-24 to 09-27 rebuilt. So widening the filter alone would have judged this shape most of the time, but not every time. Moving the key makes the rebuild certain for such a head.Which spec paths (H2, measured)
What the console build and the probe derivation read from
packages/spec:package.jsonexports: objectui'sOBJECTSTACK_SPEC_DISThook derives one alias per exports entry and refuses a missing target (apps/console/vite.config.ts,scripts/vite-objectstack-spec-dist.ts), andreadSpecBlobinscripts/console-spec-probes.mjsresolves every entry.tsup.config.ts: itsentrieslist decides which built file each exports entry points at, andbuild-console.shbuilds the spec through it.dist/index.mjsandjson-schema/openapi.jsonare build sentinels. They are generated, not authored.Over the first-parent commits on
mainfrom 2026-08-31 to9905e61ca2(3219 commits, window proven byscripts/pm/git-history.mjs), 4 commits moved the entry layout:fbec216e2(#20695),c23cfb346,9165d5cd4,776d64cd3. Every one of them touched bothpackage.jsonandtsup.config.ts.src/index.tschanged in 5 other commits, all of them content (a new metadata kind, a cycle fix, schema edits, citation re-anchoring), so it is not in the list.browser-reachable-entries.json(touched by #20695) is read only by a lint gate, not by the console build, so it is not in the list either. The triage control holds: noconsoleentry reachespackages/spec/src.Where the pins live (H3)
Before this PR, nothing asserted the
consolelist. The only readers weredispatch-gates.mjsfixtures, andcheck-expected-skips.mjs, which reads outputs andif:but never paths. The pins go incheck-ci-filter-parity.mjs, the gate that already reads and pins the filter job's lists, through the same YAML reader. Its battery (8) pins the following:packages/spec/package.jsonselects the job and moves the key. The same holds fortsup.config.ts.packages/spec/src/ui/view.zod.ts(a tracked file) does not select the job.A bounded in-place fix, declared
The two probe scripts were already
consoleentries but not in the key, so a head that changed the derivation could also get a green hit without the new derivation ever running. The new rule, applied to the baseci.yml, turns red on exactly those two (leg B below). It is the same defect class and the same parameter, a mechanical edit to the same file, and it adds no new verification surface, so it is fixed here. Cost: 1 commit in the 30-day window touched either script.Cost
packages/spec/package.json, so its runs now select the job (93 CI runs on that branch in the same 30 days). The first push of each version rebuilds; later pushes with the same file content hit that PR's own entry.merge_groupbuild and rebuilds most of the time (above), so the queue's cost barely moves.Reverse verification (from the committed state,
1bd002357)node scripts/ablation-replace.mjs --file .github/workflows/ci.yml --anchor NEW_HASHFILES_ARGS --replacement OLD_HASHFILES_ARGS --expect 3 -- node scripts/check-ci-filter-parity.mjsreported anchor 3 → 0 and blob24aac400cf2d→14c292d64529. The gate printed "FAIL: ci.yml'sconsolefilter and the console dist key are out of step", naming the four unhashed entries, and exited 1. The restore brought the blob back to24aac400cf2d, equal to HEAD, andgit diff HEADwas empty.ci.yml(bee75cebe6): exit 1, naming exactlyscripts/console-spec-probes.mjsandscripts/assert-console-spec-injection.mjs. That is the in-place fix above.Gates
Derived from this diff with
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackat1bd002357(57 commands). Exit codes and verdict lines are in the report on #20765. On-point results:node scripts/check-ci-filter-parity.mjs: exit 0, "OK: all 9consoleentr(ies) are literal paths; 6 are build inputs the console dist key hashes and 3 are declared guards; the key is spelled one way in all 3 place(s)…"node scripts/check-ci-filter-parity.mjs --self-test: exit 0, 68 assertions.pnpm check:pm-expected-skips,pnpm check:console-injection,pnpm check:console-sha,pnpm check:workflow-status-functions,pnpm check:workflow-step-name-quoting,node scripts/check-self-test-workflow-commands.mjs(both legs) andpnpm check:nul-bytes: all exit 0.check:dts-closure,check:dual-build-cjs-loads,check:lean-entry-closureandcheck:sourcemap-no-sources-contentneed built packagedist/trees. This diff touches no package source; CI builds before them.eslint.config.mjs's**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}block, which includes both edited.mjsfiles.--format jsonreports 2 files, 0 errors and 0 warnings. The config enables no type-aware linting, so this diff cannot move an untouched file's verdict, andci.ymlis not an ESLint input.Console Pin Gateruns on this PR's own head, becauseci.ymland the key's inputs moved. Its verdict is read from CI.f57066dd5(comment and fallback text incheck-console-injection.mjsandrelease.yml, oneci.ymlcomment):dispatch-gates.mjs --commandsnames the same 57 commands; reconciled with--ran(53 run, 4 NOT MEASURED as above).node scripts/check-console-injection.mjs --self-testexit 0 (44 assertions); bothremedy()branches exercised on a scratch dist.release.yml's key (seat ruling 5920200169, Q1 = B)
release.ymlkeeps its narrower key, and only its comment changes. The two workflows no longer share dist entries. Following ci.yml would rebuild the console on every Version Packages merge, and publishing a cached console whose spec content lags is the existing cache design.Acceptance notes
scripts/check-console-injection.mjsandrelease.ymlis corrected in the patch round (f57066dd5). Neither the header nor the remedy fallback restates the key any more, socheck-ci-filter-parity.mjs's hold on ci.yml's spellings is the only copy that matters.build-console.shcopies the trackedsdui.manifest.jsoninto the dist, and that file is in neither the key nor the list.a093ce3e2changed it without a pin move, so a cached dist would carry the older copy until the pin moves. Carrier: none.platform-readings.md:42#20764 (the seat protocol watching non-required queue checks) and making this job a required context are out of scope here, as triage ruled.Body edited by the
domain:specseat 2 PM (session_017VaLJnYwhPsanVCe9dMCJU) after the patch round, from the dev's wording in its report on #20765.