Skip to content

Commit d78bd01

Browse files
ci: Build Core starts on a diff confined to one of its own guard scripts, held to the job steps by check-ci-filter-parity (#21368)
Fixes #21341 Clause-②: no ## What changes 1. **`.github/workflows/ci.yml`, the `core:` filter**, gains five literal rows after the build-input rows #21202 added: - `scripts/check-dts-closure.mjs` - `scripts/check-dual-build-cjs-loads.mjs` - `scripts/check-lean-entry-closure.mjs` - `scripts/check-sourcemap-no-sources-content.mjs` - `scripts/population-floor.mjs` (the dual-build guard imports it) A PR that changes only one of them now starts Build Core, so the changed guard runs its `--self-test` and its real leg at PR time. Before this change Build Core skipped on such a PR. PR #17100 changed only `scripts/check-lean-entry-closure.mjs`, and its Build Core concluded `skipped`. 2. **`scripts/check-ci-filter-parity.mjs`** holds those rows. The Build Core subject #21202 added (`judgeBuildInputs`) now judges a second population beside the build inputs from turbo.json: every repository script that a `build-core` `run:` step runs, plus that script's first-party imports. - **Derived from the job, not kept by hand.** Each step goes through `collectInvocations`. That is the recognizer `check:self-test-wired` already uses to answer "which repository scripts does this CI command run": a `scripts/...` path named directly, or one reached through a root `package.json` alias chain. Each script it names brings its first-party closure from `first-party-closure.mjs`. - **Uncovered goes red.** If no `core` row covers a script that a step runs, the gate fails and names the path and the step. So a guard step added to Build Core without a filter row goes red. - **Leftovers go red.** #21202's leftover rule now covers this population too. A literal `core` row next to the build inputs and the guards that covers neither is reported. So a row left behind by a deleted guard step goes red too. - **No new gate, no new workflow step, no new list.** The existing parity gate gains a population, and the rows in ci.yml are the one list. ## Why derived, and not a `BUILD_GUARDS` table like `CONSOLE_GUARDS` `CONSOLE_GUARDS` exists because the console subject has to classify each row. A `console:` row is either hashed into the dist key or a guard, and nothing in the workflow says which. Build Core rows need no classification: the scripts its steps run can be read from the steps, just as #21202 reads build inputs from turbo.json. A `BUILD_GUARDS` table would be a second list of the same fact, kept in step by hand. It would also need two holding rules of its own: table against steps, and table against rows. The derivation needs only one rule in each direction, and the self-test pins it against the real job. ## Measurements Branch head `aa0b21ae2c`, base `96b12b589f`. **The card's instance, through the matcher.** `core` is read at base and at head. Each path is evaluated with picomatch using `{ dot: true }`, which is how `dorny/paths-filter@v4` calls it. Version 2.3.1 is the one the action bundles; 4.0.5 is the one this tree resolves. | path | before, 2.3.1 | before, 4.0.5 | after, 2.3.1 | after, 4.0.5 | |---|---|---|---|---| | `scripts/check-dts-closure.mjs` | false | false | true | true | | `scripts/check-dual-build-cjs-loads.mjs` | false | false | true | true | | `scripts/check-sourcemap-no-sources-content.mjs` | false | false | true | true | | `scripts/check-lean-entry-closure.mjs` | false | false | true | true | | `scripts/population-floor.mjs` | false | false | true | true | | `scripts/check-dts-emitted.mjs` (already a build input) | true | true | true | true | | control: `scripts/check-ci-filter-parity.mjs` | false | false | false | false | | control: `scripts/check-self-test-wired.mjs` | false | false | false | false | | control: `scripts/pm/dispatch-gates.mjs` | false | false | false | false | | control: `scripts/dual-build-cjs-loads.baseline.json` | false | false | false | false | The two versions agree on every row. **Width.** The window is the 3287 first-parent commits of `main` in the 30 days to `96b12b589f`. `scripts/pm/git-history.mjs` proved it complete; the history floor is 2026-06-27. - `core` matched 2276 of those commits. With the five rows it matches 2278: +2, identical under 2.3.1 and 4.0.5. - Seven commits touched a guard path. Only one touched nothing but guard paths: `91f65c4ea4`, which is PR #17100, the card's own instance. - The other newly scheduled commit is `83649b870a`. It changed `check-dual-build-cjs-loads.mjs` and `population-floor.mjs` along with two lint-side gates. `population-floor.mjs` on its own adds 0 commits in the window. **The gate before the rows.** This is the new script run over the base ci.yml: exit 1, naming exactly five scripts, each with the step that runs it. ```text - 5 script(s) the `build-core` job's own steps run are covered by no `core:` entry in .github/workflows/ci.yml. ... scripts/check-dts-closure.mjs (Sweep the built closure for vanished declarations (pnpm check:dts-closure)) scripts/check-dual-build-cjs-loads.mjs (Every published require entry point actually loads (pnpm check:dual-build-cjs-loads)) scripts/population-floor.mjs (imported by scripts/check-dual-build-cjs-loads.mjs) scripts/check-sourcemap-no-sources-content.mjs (No published source map embeds source text (pnpm check:sourcemap-no-sources-content)) scripts/check-lean-entry-closure.mjs (The lean engine entry loads no forbidden or unlisted package (pnpm check:lean-entry-closure)) ``` **After:** exit 0. The gate prints: `all 20 build input(s) ... and all 11 script(s) the build-core job's own steps run (5 named by a step, 6 only imported by one) are covered by core ... no literal core entry beside them is a leftover`. **Self-test.** 131 assertions, exit 0. The new battery (10) has a floor of 30, which is the count it registers. The roster floor goes from 9 to 10. On fixtures, battery (10) checks each of these: - A guard row dropped from `core:` goes red, naming the path and the step. - A guard step added with no row goes red, whether it reaches its script through an alias or names it directly. - An imported module dropped from `core:` goes red. - An alias chain is followed to its script. - A commented-out line requires nothing. - A guard that is also a build input needs only its one row. - A row whose step is gone is reported as a leftover, including in a directory that holds only a guard. - A script whose closure cannot be computed is refused. Over the real tree, it checks: - The five step-run scripts and `population-floor.mjs` are each covered by their own literal row. - With the lean-entry row dropped, `main()` returns 1. - The two recognizer bounds below are pinned to the real job. The scratch tree that the report-path cases run in now stages the guards' closures. Without that, a "returns 1" case could pass only because the build subject refused. **Ablation, run once on the real script and then restored.** The step loop in `buildGuardsOf` was mutated to read nothing, through `scripts/ablation-replace.mjs`: anchor 1 → 0, blob `bfb0ec815a` → `2f03c5416d`. - `--self-test`: exit 1, with 27 of 131 assertions failing. - 24 of battery (10)'s 30 failed. - Three real-tree cases in (5) and (9) that read the same verdict also failed. - Six cases in (10) held. Five of them do not depend on the derivation: the anchor, the control, the two bound pins, and the shared-row case. - The sixth is the report-path `main() === 1`, which the leftover direction below satisfied. Its sibling, which asserts the exact finding, failed. - The production gate: exit 1 as well, but in the reverse direction. With no population derived, the five rows became leftovers. So the rows are held to the steps from both sides, and an emptied derivation cannot pass silently. - Restore: the blob equals HEAD `bfb0ec815a`, `git diff HEAD` is empty, and the marker count is 0. ## Known bounds (stated in the header, two of them pinned in the self-test) - **Package-scoped scripts are not followed.** This means a script that a package's own manifest runs, via `pnpm --filter PKG SCRIPT`. Build Core's only such step today is the spec's `analyze`, which runs a file inside `packages/spec`, and `packages/**` covers that. Pinned. - **Local composite actions are not followed.** Build Core's only one is setup-pnpm. Its `run:` steps name no repository script, and every job in every workflow runs it. Pinned. - **`pnpm install`'s lifecycle is not a guard.** `prepare` runs `scripts/setup-git-hooks.mjs`, but every job installs, including the unfiltered lint.yml jobs, so a change to it already runs at PR time. - **Some extensions are not read.** A script in `.cjs` or `.ts` is not seen. This is the same as `check:self-test-wired`, because the recognizer is shared. - **Data files are not scripts.** A data file a guard reads is not derived. ## Acceptance notes - **The dual-build baseline is not covered.** `scripts/dual-build-cjs-loads.baseline.json` is a shrink-only ledger that `check:dual-build-cjs-loads` reads. A diff confined to it still skips Build Core (control row above). Closing that needs either a declared data-input list or reading the gate's own declaration, which goes beyond this card's "guard scripts". There is one such baseline today. Noted, not filed. - **A pre-existing comment overstates when Build Core runs.** `scripts/check-dual-build-cjs-loads.mjs` (its dispatch-gates declaration note) says Build Core is "a required context that runs on every PR". Build Core is path-scoped by `core:`, so that was not true before this PR either. Its operative claim, that a card touching `packages/**` still gets Build Core, holds. This PR does not make the sentence false, and the file is outside this card's surface. Noted, not edited. - **Comments this PR corrects.** These sentences were made false by the new population and are corrected here: - ci.yml's build-input comment ("a literal entry beside them covers none (a leftover)") - the parity header's leftover sentence - the parity header's known-bounds sentence ## Local verification All readings at `aa0b21ae2c`. - **Derived gate families.** `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` (no paths) derives 58 commands, the same 58 the seat derived at dispatch. - 53 exit 0. - The four dist-reading guards (`check:dts-closure`, `check:dual-build-cjs-loads`, `check:lean-entry-closure`, `check:sourcemap-no-sources-content`) pass their `--self-test` legs, and their real legs exit 3 PREREQUISITE NOT MET because there is no `dist/` here. NOT MEASURED locally, reason: no build. This PR's ci.yml change schedules Build Core, which runs them. - `check:pm-dispatch-gates` exits 0: 1976 cases, and the battery took 791.9s. - `--ran` reconciliation: 58 derived, 54 run, 4 NOT-MEASURED (the exit-3 four), 0 unrun. - **Roster gates.** Those whose roster sits under a directory this diff touches: `check-platform-checklist-watchdog`, `check-published-list-mirrors`, `check:engine-double-contract` and `check:i18n-stale-fill` exit 0. `check:published-readme-exports` exits 3: NOT MEASURED, it needs built `.d.ts`. - **ESLint.** Narrowed to the changed lintable files and run with `--no-inline-config`. - That is one file, `scripts/check-ci-filter-parity.mjs`; ci.yml matches no `files` glob in `eslint.config.mjs`. - JSON output: 1 file, 0 errors, 0 warnings. - The config has no `parserOptions.project`, so linting is not type-aware and this diff cannot move any untouched file's result. - **The edited script's own tests.** No `*.test.*` in `scripts/` or in the root package names `check-ci-filter-parity`. The three hits elsewhere (`packages/types`, `packages/objectql`, `packages/runtime`) mention it in comments only. No changeset: workflow and repo-gate code only, nothing any package publishes (`skip-changeset`). --- _Generated by [Claude Code](https://claude.ai/code/session_01UtnxvdiN376GF3sgXwAw4d)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 3937ad2 commit d78bd01

2 files changed

Lines changed: 413 additions & 39 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,8 @@ jobs:
191191
# scripts/check-ci-filter-parity.mjs DERIVES the required set from
192192
# turbo.json and the root `build` script, and reds while a declared
193193
# build input is uncovered here, or while a literal entry beside
194-
# them covers none (a leftover). `tsconfig.json` and the
194+
# them covers none of them and none of Build Core's guards below
195+
# (a leftover). `tsconfig.json` and the
195196
# `packages/cli/...` input are covered by the entries above;
196197
# `@objectstack/docs#build` (its input is `content/**`) is the
197198
# docs filter's, since `pnpm build` excludes that package.
@@ -220,6 +221,34 @@ jobs:
220221
- 'scripts/ts-parse.mjs'
221222
- 'scripts/tsup-drop-sources-content.mjs'
222223
- 'scripts/workspace-enumerator.mjs'
224+
# BUILD CORE'S GUARDS (#21341): every repository script the
225+
# build-core job's own steps run (named by a `run:` step or
226+
# reached through its `pnpm check:*` alias) and each one's
227+
# first-party imports. Build Core is the only job that runs
228+
# them, so before these rows a diff confined to one started no
229+
# Build Core and the changed guard ran nowhere before the merge
230+
# queue (PR #17100 changed only check-lean-entry-closure.mjs;
231+
# Build Core skipped). A change to the guard runs the guard, as
232+
# the `console:` filter below already does for its own.
233+
#
234+
# Not a hand-kept list: scripts/check-ci-filter-parity.mjs
235+
# DERIVES the set from the job's steps and reds while a script a
236+
# step runs is uncovered here, or while a literal row covers
237+
# neither a build input nor such a script (a leftover of a
238+
# deleted step). Scripts that are also build inputs
239+
# (check-dts-emitted.mjs and the helpers the guards import) are
240+
# covered by the rows above and are not repeated.
241+
# population-floor.mjs is here because check-dual-build-cjs-
242+
# loads.mjs imports it.
243+
#
244+
# WIDTH: over the 3287 first-parent commits of `main` in the 30
245+
# days to `96b12b589f`, through picomatch 2.3.1 and 4.0.5 (they
246+
# agree on every row), `core` matched 2276; these rows add 2.
247+
- 'scripts/check-dts-closure.mjs'
248+
- 'scripts/check-dual-build-cjs-loads.mjs'
249+
- 'scripts/check-lean-entry-closure.mjs'
250+
- 'scripts/check-sourcemap-no-sources-content.mjs'
251+
- 'scripts/population-floor.mjs'
223252
# Build inputs of the vendored Console SPA — see the Console Pin Gate
224253
# job at the bottom of this file. `.objectui-sha` is a ROOT DOTFILE, so
225254
# it matches neither filter above: a pin-only diff skipped `core` and

0 commit comments

Comments
 (0)