Skip to content

Commit fd3dc6e

Browse files
ci: schedule the ui jobs, Build Docs and Test Core for the root inputs they read (package.json, five ui test inputs) (#28089)
<!-- claude-projects-attribution: {"github_login":"os-tesla"} --> _Requested by **Tesla** · [project thread](https://claude.ai/code/project/chan_01AXsQkLFKRdQCqQ3RTr6Jao?thread=cmsg_01AXsQkLFKRdQCqQ3RTr6JaoQw2SaxD3ZKQgJ2Det3rETu)_ Fixes #27717 Fixes #27961 Clause-②: no Metadata-protocol: none Tier H: the diff touches `.github/workflows/ci.yml` and `.github/workflows/lint.yml`. The maintainer approves in this PR. ## 维护者速读 - **改了什么:** 根 `package.json` 进入 `ci.yml` 的 `ui:` 和 `docs:` 过滤列表;五个 ui 测试输入进入 `core:`;`check-ci-filter-parity` 增加第四个对账主题(通用 `test` 任务的根输入);`lint.yml` 一个步骤的名称、注释和结尾文字不再写死成员个数。 - **为什么改:** 只升级 pnpm 版本(`packageManager` 字段)的 PR 现在一个 UI 任务、Build Docs 都不跑;改 `ui/vitest.config.mts` 这类文件的 PR 不跑 Test Core。两种情况都要等到合并队列才第一次运行。 - **风险与回滚:** 只多调度、不少调度。30 天回放:UI 任务多 11/3683 次,Build Docs 多 18/3683 次,Test Core 多 2/3683 次。回滚 = revert 本 PR。 - **先看:** ① `.github/workflows/ci.yml` 的 `ui:` / `docs:` / `core:` 三处新增行;② `scripts/check-ci-filter-parity.mjs` 的 `judgeTestInputs` 和 `judgeBuildInputs` 里 `reasons` 那一行;③ `.github/workflows/lint.yml` 的 shared-module self-tests 步骤。 - **两分钟验证:** `node scripts/check-ci-filter-parity.mjs --self-test && node scripts/check-ci-filter-parity.mjs`,预期 138 条断言通过,三个 OK 行,其中一行是 "all 8 root input(s) of `test` / `test:repo`"。 - **需要你做的:** 在本 PR 上批准(Tier H)。 ## What changes **#27717: the root manifest schedules every job that reads its `packageManager` field.** `.github/actions/setup-pnpm/action.yml` reads `$GITHUB_WORKSPACE/package.json`'s `packageManager` to pick the pnpm it materialises. Measured which `ci.yml` jobs run it, at `origin/main` `2e955dde`: | filter list | jobs that run setup-pnpm | `package.json` row before | |:--|:--|:--| | `core` | every core job, through `setup-workspace` | yes | | `ui` | `ui-deps`, `type-check-ui`, `test-ui`, `lint-ui`, `build-e2e-ui`, `live-e2e-ui` (directly) | no | | `docs` | `build-docs`, through `setup-workspace` | no | `ui` is the narrowest list that starts all six ui jobs (`ui_consumers` starts four of them and is held to the seven packages objectui installs), so the row goes there. **Declared increment, approved by the dispatching seat:** the same row joins `docs:`, because Build Docs has the same hole, in the same file, the same gate family and the shape #27673 used for `.nvmrc` and the setup actions. No other list changes. The parity gate header named setup-pnpm as "today's one" local composite action the guard recognizer does not follow, and said every job in every workflow runs it. Build Core's step `uses:` setup-workspace (which calls setup-pnpm), and 18 of 30 workflows use either action. The bound now names setup-workspace, states that neither action's `run:` steps name a repository script (what the self-test's bound-2 case already pins against the real job), and says the files they read are `core:` rows of their own. **#27961: Test Core starts on a diff confined to a generic test input.** turbo.json's generic `test` task has eight `$TURBO_ROOT$` inputs. Three were already covered (`vitest.preset.mts` by hand, `ui/scripts/vite-*.ts` and `ui/scripts/vitest-invocation-guard.mjs` by the #27283 build rows). The five below matched no list Test Core's `if:` reads, and now sit in `core:` beside the six ui build rows: - `ui/vitest.config.mts` - `ui/vitest.setup.*` - `ui/scripts/test-shard-*` - `ui/apps/console/vitest.config.ts` - `ui/apps/console/vite.config.ts` The `ci.yml` authorisation precedent is ruling 6513291766 on #27283 (letter A, maintainer 「其他同意」), which this card inherits per triage 6516081082 ("Item 1 is the same shape as ruling `6513291766` (the six `build` inputs), one task over. It adds no gate"): > **What the ruling binds (letter A):** the six ui-root build inputs that PR #27571 adds to the root `turbo.json` [...] also enter `.github/workflows/ci.yml`'s `core:` path filter, in the same PR, so a diff confined to one of them schedules Build Core and Test Core on the PR instead of first in the merge queue, and `check-ci-filter-parity` is green by parity, not by blindness. **The fourth parity subject, and why it is required rather than optional.** With the five rows added and the gate unchanged, `check:ci-filter-parity` went RED: the third subject's leftover rule reported `ui/vitest.config.mts` as a leftover `core:` row. That rule judges a literal row in any directory holding a literal build input, `ui/` holds `ui/tsconfig.json`, and no reason it knew covered the new row. (`ui/vitest.setup.*` and `ui/scripts/test-shard-*` carry wildcards and are not judged; `ui/apps/console/` holds no build input.) So the gate now: - derives the root inputs of turbo.json's generic `test` and `test:repo` tasks (the pair Test Core's `turbo run test test:repo` names, pinned against the real job) and judges them with the existing pure-string rule against `core` + `crosspkg`, read back out of the `test` job's `if:` exactly as the first subject reads them. Remedy: a `core:` row, never `crosspkg:`, which the first subject holds to the cross-package declarations; - counts those inputs as reasons for the third subject's leftover rule, so a `core:` row kept for a test input is no leftover. This strengthens an existing gate; no new gate, no new CI step. Per-package `#test` inputs stay the first subject's population (measured: with this diff, every `$TURBO_ROOT$` input of every `test` / `test:repo` task in turbo.json is covered by `core` + `crosspkg`, 0 uncovered). **#27961 item 2: `lint.yml`'s shared-module self-test step.** It runs five self-tests since #27571 added `ui/scripts/invoked-as.mjs --self-test`, but its name listed four modules, its comment said "The four modules" and its last line printed "all four ran and passed". The name now lists the members (`parse · entry predicate, root and ui · comment mask · prerequisite frame`), the comment explains why the ui twin is there and pins no count, and the last line reads "every one ran and passed". The comment's "~0.6 s combined" for the in-process self-tests is re-measured at ~0.9 s for the four of them (four node starts, this container). Nothing reads the step name or the message (`git grep` outside `lint.yml`: no hits); the step `id` is unchanged. ## Tests `scripts/check-ci-filter-parity.mjs --self-test` gains battery (11), 18 cases, and the roster floor goes 9 → 10 batteries: a `test` and a `test:repo` root input each reported by path and task; an input covered only by `crosspkg` covered; package-task and negated inputs left out; five refusals (turbo.json unreadable, not JSON, no `tasks`, no generic `test` task, the `test` job's `if:` dropping `crosspkg`); the leftover rule keeping and then losing a `core:` row as its test input appears and disappears; the real tree clean; the real `ci.yml` with the five rows dropped reporting exactly those five, through `main()` too; and the task pair pinned to the real `test` job. Total 120 → 138 assertions, all passing. ## Acceptance notes - What Test Core runs on such a diff is the existing shard plan's answer (`turbo ls --affected` unioned with declarations); this PR only makes the job start. Not measured here. - Scheduling price, replayed through picomatch 4.0.7 over the 3683 first-parent commits of `main` in the 30 days to `2e955dde`, current filters against current filters plus these rows: `ui` 690 → 701, `docs` 1037 → 1055, `core` 2758 → 2760, Test Core (`core` or `crosspkg`) 3255 → 3257. ## Verification All readings on head `115b9df6f` (`objectstack-ai/objectstack`), merge base `2e955dde6`. - `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack`: 66 commands over 3 paths. Each was run with stdin from `/dev/null` and its exit code recorded before any pipe: **66 exit 0**. `check:type-check-debt` first hit my 280 s per-command cap (exit 124) and was re-run uncapped: exit 0. The four dist-reading Build Core guards (`check:dts-closure`, `check:dual-build-cjs-loads`, `check:lean-entry-closure`, `check:sourcemap-no-sources-content`) ran after the type-check-debt closure build and each swept real `dist/` (e.g. "76 built package(s) swept - 235/235 declared declaration file(s) present"). - `dispatch-gates --ran`: "66 derived famil(ies) accounted for — 66 run, 0 NOT-MEASURED (a DERIVED zero — all 66 recorded an exit code and none of them is 3)". - In that set: `check:ci-filter-parity` (self-test 138 assertions, three OK lines), `check:pm-expected-skips`, `check:required-contexts`, `check:workflow-status-functions`, `check:self-test-wired`, `check:step-collectors`, `check:node-version`, `check:pnpm-acquisition`, `check:pm-dispatch-gates`: all exit 0. - Red before green, on a scratch copy of this head with only the five #27961 rows removed from `ci.yml`: `check-ci-filter-parity` exit 1, "5 root input(s) of `test` / `test:repo` in turbo.json are covered by NEITHER `core` nor `crosspkg`", naming exactly the five. With the five rows and the gate as it was on `main`: exit 1, "`ui/vitest.config.mts`" reported as a leftover `core:` row (the reason the fourth subject is required). - `eslint --no-inline-config scripts/check-ci-filter-parity.mjs`: exit 0. - No package source changed, so no package build or test is owed; no changeset (`skip-changeset`: `.github/workflows/**` and a root `scripts/` gate publish nothing). --- _Generated by [Claude Code](https://claude.ai/code/session_01L9qibxnVpxAbqTXMwJU6LM)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 48c7780 commit fd3dc6e

3 files changed

Lines changed: 280 additions & 22 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,11 @@ jobs:
280280
- '.nvmrc'
281281
- '.github/actions/setup-workspace/**'
282282
- '.github/actions/setup-pnpm/**'
283+
# setup-pnpm materialises the pnpm the root manifest's
284+
# `packageManager` field pins (#27717), so a diff confined to
285+
# that field changes the pnpm Build Docs installs with. `core:`
286+
# already carries this row for its own jobs.
287+
- 'package.json'
283288
core:
284289
- 'packages/**'
285290
- 'examples/**'
@@ -361,6 +366,19 @@ jobs:
361366
- 'ui/scripts/check-dist-completeness.mjs'
362367
- 'ui/scripts/invoked-as.mjs'
363368
- 'ui/scripts/vitest-invocation-guard.mjs'
369+
# TEST INPUTS outside every package (#27961): each `$TURBO_ROOT$/…`
370+
# input of turbo.json's generic `test` task. That task runs for
371+
# every package Test Core tests, so a diff confined to one moves
372+
# every `#test` hash, and before these rows Test Core first ran on
373+
# it in the merge queue. `vitest.preset.mts` above and the
374+
# `vite-*.ts` / `vitest-invocation-guard.mjs` build rows cover the
375+
# other three; check-ci-filter-parity derives the set from
376+
# turbo.json and reds while one is uncovered here.
377+
- 'ui/vitest.config.mts'
378+
- 'ui/vitest.setup.*'
379+
- 'ui/scripts/test-shard-*'
380+
- 'ui/apps/console/vitest.config.ts'
381+
- 'ui/apps/console/vite.config.ts'
364382
# BUILD CORE'S GUARDS (#21341): every repository script the
365383
# build-core job's own steps run (named by a `run:` step or
366384
# reached through its `pnpm check:*` alias) and each one's
@@ -533,6 +551,11 @@ jobs:
533551
# PR, nor in the queue, where these outputs read the same diff.
534552
- '.nvmrc'
535553
- '.github/actions/setup-pnpm/**'
554+
# The root manifest, because setup-pnpm reads its `packageManager`
555+
# field to pick the pnpm every ui job runs (#27717). A diff
556+
# confined to a pnpm bump matched no list here, so the ui jobs
557+
# skipped on the PR and in the queue alike.
558+
- 'package.json'
536559
# The seven packages objectui installs from this tree (the set its
537560
# `.objectstack-pin` used to pack). A change here compiles and builds
538561
# the Console in the same PR — the check the retired Spec Shape Gate

‎.github/workflows/lint.yml‎

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -790,34 +790,41 @@ jobs:
790790
# explicitly does not ask for. ⛔ It is also not a licence to sweep the
791791
# rest of that remainder from here; that is a separate card.
792792
#
793-
# What runs here is the self-tests, not the modules' callers: for the first
794-
# three the callers are the two routing gates named above, which already run on
793+
# `ui/scripts/invoked-as.mjs`, the ui tree's twin of the entry predicate,
794+
# joined beside it (#27283): `ci.yml`'s `core:` filter names it as a build
795+
# input, so `check:self-test-wired` counts it as a script CI runs, and its
796+
# `--self-test` ran nowhere else.
797+
#
798+
# What runs here is the self-tests, not the modules' callers: for the three
799+
# #10608 modules the callers are the two routing gates named above, which already run on
795800
# every PR; for `import-prerequisite` the callers are the 45 importing
796801
# gates, whose ordinary runs take the branch where the dependency IS
797802
# present and therefore observe none of what the self-test pins.
798803
# Invoked as `node` rather than through `pnpm check:*`: see the GATE
799804
# INVOCATION IDIOM note at the top of this file.
800805
# `ts-parse` spawns ~20 real node children (~10 s measured, and the spawns
801806
# are the point — they pin that a caller's try/catch cannot swallow the
802-
# refusal); the other three are in-process fixtures, ~0.6 s combined.
807+
# refusal); the others are in-process fixtures, ~0.9 s combined.
803808
#
804809
# ⭐ Collected rather than sequenced, for the reason spelled out at the
805810
# `Shallow-history guard self-tests` step below (#10814): under `bash -e` a
806811
# bare sequence stops at the first failure, so a red `ts-parse` would leave
807812
# the entry-predicate, comment-mask and prerequisite-frame self-tests UNRUN
808813
# while the log shows only the one failure. `ts-parse` is both the slowest
809-
# of the four and the one that spawns real children, so it is the likeliest
810-
# to be red — precisely the masking direction. The four modules are
811-
# independent of one another, so collecting loses nothing.
814+
# of them and the one that spawns real children, so it is the likeliest
815+
# to be red — precisely the masking direction. The modules are
816+
# independent of one another, so collecting loses nothing. This note, the
817+
# step name and its last line name no member count: the list below is the
818+
# one place that says how many run.
812819
#
813820
# ⭐ Collected is also why the fourth leg is HERE rather than in a step of
814821
# its own: Actions skips a job's remaining steps once a step fails, so a
815822
# standalone step further down this job would be masked by every gate above
816823
# it — the same defect one level up, and the split `check-step-collectors`
817824
# explicitly refuses as a remedy. Inside this block the frame's self-test
818-
# prints its own verdict whatever the other three do, and it does so early
825+
# prints its own verdict whatever the others do, and it does so early
819826
# in the job.
820-
- name: scripts/ shared-module self-tests (parse · entry predicate · comment mask · prerequisite frame)
827+
- name: scripts/ shared-module self-tests (parse · entry predicate, root and ui · comment mask · prerequisite frame)
821828
id: scripts-shared-module-self-tests-parse-entry
822829
continue-on-error: true
823830
run: |
@@ -850,7 +857,7 @@ jobs:
850857
printf "%s" "$failed"
851858
exit 1
852859
fi
853-
echo "scripts/ shared-module self-tests — all four ran and passed"
860+
echo "scripts/ shared-module self-tests — every one ran and passed"
854861
855862
# Every `scripts/**` TypeScript parse goes through ONE module (#10133 /
856863
# #10573), and this is the half that keeps it that way (#10574).

0 commit comments

Comments
 (0)