Skip to content

ci(ui): start the Test UI shards when a package ui consumes changes - #28501

Merged
os-zhuang merged 2 commits into
mainfrom
claude/project-thread-cbuqpy
Nov 1, 2026
Merged

os-zhuang merged 2 commits into
mainfrom
claude/project-thread-cbuqpy

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Sam · project thread
domain:devx#1 · os-zhuang · session_01QUs3BvP5Aq9USg5GtmgAAR
Fixes #28220

Ruling-ref: 6545651687 (letter A, maintainer 「同意」 2026-10-31T15:50Z)

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么: 只改了 lint、spec 等「ui 依赖的 7 个平台包」的 PR,现在合并前也会跑 ui 的 16 片单元测试(Test UI)。只改了根 ci.yml。
  • 为什么改: 以前这类 PR 只编译和打包 ui,不跑 ui 测试;坏了要等下一个无关 PR 进队列时才红,把别人踢出队列(Test UI red on main: AutomationsPillar.problemsAgree-27799 still expects the node config finding's where prose, and #28192 now gives it a key path #28219、4-hourly full run: red on main (CI) #28491 等四次)。
  • 风险与回滚: 不影响产品代码。代价是 runner 分钟:每个这类合并约多 376 runner-min(PR 一次 + 队列一次,各约 188)。回滚 revert 这一个 PR 即可。
  • 先看: .github/workflows/ci.yml 的 test-ui 的 if: 一行;其次 test-ui-gate 判「跳过是否合理」的那段 shell。
  • 两分钟验证: 合并后第一个只改 packages/spec/** 或 packages/lint/** 的 PR,检查列表里 Test UI (k/16) 显示 success 而不是 skipped。
  • 需要你做的: 这个 PR 改了 .github/workflows/**(Tier H),需要你的批准,然后席位落地。

What changes

.github/workflows/ci.yml only.

  • test-ui (Test UI (n/16)) — the job-level if: now reads needs.filter.outputs.ui != 'false' || needs.filter.outputs.ui_consumers != 'false' (inside the existing ui_tree conjunct), the exact shape ui-deps, type-check-ui and build-e2e-ui already use. Which files run is unchanged: ui/scripts/select-affected-tests.mjs already answers mode=full for any of the seven packages (outsideTreeReasons), so a ui_consumers-only diff runs the whole suite across the 16 shards.
  • test-ui-gate (the required Test UI context) — its filter job 一旦失败,Test Core / Build Core / Dogfood 会全部 skipped 而分支保护判为通过 —— 隐式 success() 今天已第三次咬人 #4928 guard explains a skipped rollup only when filter succeeded and said ui_tree=false, or ui=false and ui_consumers=false (it read ui alone). This follows from the same if:: a skip is "expected" only on the diffs the shards' condition excludes. It also makes the gate the regression check for this card: if the if: ever drops ui_consumers again, the first ui_consumers-only diff reds Test UI instead of passing a silent skip.
  • lint-ui — not changed. Measured below: ui's eslint reads none of the seven packages.
  • Comments: the filter outputs block, the ui_consumers: list and a new "when the shards start" paragraph above test-ui say what starts the shards and why.

scripts/pm/check-expected-skips.mjs and scripts/check-ci-filter-parity.mjs — no edit needed, measured. check-expected-skips derives its roster from each job's if: (deriveGatedSkips), so the row moves by itself: --roster printed Test UI (${{ matrix.shard }}/16) [ci.yml › test-ui; gate: job-gate filter ui_tree + filter ui; derived] on origin/main ea74ebf93 and prints … gate: job-gate filter ui_tree + filter ui|ui_consumers; derived on this head; its --self-test pins no live job name by design and passes unchanged. check-ci-filter-parity has no ui subject (it holds core/crosspkg to the cross-package declarations, Build Core's inputs and Test Core's turbo inputs); its scan and --self-test pass on this head.

Why

A diff confined to packages/lint/** (or spec, client, core, formula, sdui-parser, types) compiled and built the Console against the new shape (Type Check UI, Build & E2E UI) but never started the ui tests that pin that shape. The red surfaced on the next unrelated PR that did start the shards, and that bystander was ejected from the queue: #28192 → #28197 (#28219), and PR #28467 → p0 #28491, the fourth recurrence.

Tests

Reproduce first: the skip on origin/main ea74ebf

Measured run — the queue build of #28192 (3efccfd5, packages/lint/** + packages/metadata-protocol/**), run 41091107812, merge_group, read from the Actions jobs endpoint:

Build UI Deps                      success
Type Check UI                      success
Build & E2E UI                     success
Live E2E UI                        success
Test UI (${{ matrix.shard }}/16)   skipped
Test UI                            success   (the aggregate read the skip as explained)
Lint UI                            skipped

Filter evaluation — the live filter job's dorny/paths-filter block read out of ci.yml, matched with picomatch 4.0.7 (the matcher dorny/paths-filter uses), then each ui job's job-level if: evaluated with those outputs on a pull_request event (a one-off scratch script, not committed):

before (origin/main ea74ebf93)                     after (this head)
diff: packages/lint/src/index.ts                   diff: packages/lint/src/index.ts
filter outputs: ui=false ui_consumers=true         filter outputs: ui=false ui_consumers=true
Build UI Deps                STARTS                Build UI Deps                STARTS
Type Check UI                STARTS                Type Check UI                STARTS
Test UI (n/16)               SKIPPED               Test UI (n/16)               STARTS
Lint UI                      SKIPPED               Lint UI                      SKIPPED
Build & E2E UI               STARTS                Build & E2E UI               STARTS
Live E2E UI                  STARTS                Live E2E UI                  STARTS

packages/spec/src/ui/action.zod.ts reads the same as lint on both sides; ui/packages/core/src/index.ts starts every ui job on both; scripts/foo.mjs starts none on both.

The selector already answers full for such a diff: outsideTreeReasons({ outside: ['packages/lint/src/index.ts'] }) returns packages/lint/src/index.ts: @objectstack/lint changed, and this tree's tests read its built output (through @object-ui/console), and over every tracked packages/*/package.json it turns full for exactly the seven ui_consumers packages (client, core, formula, lint, sdui-parser, spec, types).

The test-ui-gate verdict, every case

The gate's run: block read out of this head's ci.yml and executed with each input set (OS_TEST_UI_RESULT OS_FILTER_RESULT OS_UI_TREE OS_UI OS_UI_CONSUMERS -> exit):

skipped success true  false false -> exit 0   no ui path, no consumed package: explained
skipped success true  false true  -> exit 1   a consumed package changed and the shards skipped: red (was exit 0)
skipped success true  true  false -> exit 1   unchanged
skipped success false true  true  -> exit 0   no ui tree: explained, unchanged
skipped failure true  false false -> exit 1   filter failed: red, unchanged
success success true  false true  -> exit 0   unchanged

Acceptance notes

  • Lint UI: not added, measured. ui/eslint.config.js sets no parserOptions.project/projectService (no type-aware rules, so no rule resolves a type from an @objectstack/* dist), and the two local rules that name @objectstack/* (ui/eslint-rules/no-inline-spec-config.js, no-unprefixed-query-params.js) match AST shapes and read no file. The ruling's condition ("only if ui's eslint reads those packages") is not met. Worth the seat's eye, not acted on here: other Lint UI steps do read the seven packages — pnpm --filter @objectstack/spec check:liveness runs over spec's sources, and the skill-example / doc-snippet / doc-example / readme-export type checks compile against built @object-ui/* closures that include the @objectstack/* dists (check:skill-examples --build-filter derives 12 filters, @object-ui/data-objectstack... among them). So a spec change can still red Lint UI first on a bystander's ui PR. carrier: domain:devx seat (a card if the seat wants Lint UI on ui_consumers too, by the same one-line shape).
  • The same gap one step wider (noted, not filed). The selector also turns full for every non-documentation workspace-root file and for patches/**; 21 such root files are tracked (.npmrc, eslint.config.mjs, vitest.preset.mts, paseo.json, .gitattributes, …) and several are not in the ui: list, nor is patches/**, so a diff confined to one of them still skips the shards. Not the ruling's scope; dedupe words: ui filter patches npmrc Test UI skipped root file. carrier: domain:devx seat.
  • No permanent pin added. A select-affected-tests pin reconciling the ui_consumers list against the selector's package graph was drafted and dropped: it is the second, reconciling check the ruling declined with option B. The test-ui-gate change above is the in-surface regression check instead.
  • Assignees: none set on this PR (the dispatch contract leaves the PR's assignee to the seat).

Runner-minute cost (against #25869 and #25881)

  • Per full Test UI battery: ~188 runner-min. Measured on the last four push runs of main (41258652854, 41261634108, 41261929545, 41262951436; 16/16 shards each): 185.2 to 192.1 runner-min summed, mean shard 11.6 to 12.0 min, slowest shard 13.6 to 14.4 min wall. A ui_consumers-only diff is always a full battery (the selector's outsideTreeReasons).
  • Per such merge: at least ~376 runner-min — one PR-head run plus one queue entry; each extra push to the PR adds ~188. Build UI Deps already runs on these diffs, so the seven-package build ci(ui-merge): twenty ui jobs each rebuild the same seven @objectstack packages (1.6–2.5 min apiece, mostly turbo cache misses on PRs) — build them once per run and hand the dists to the ui lanes (parent #25677) #25869 de-duplicated is replayed from its cache and adds nothing.
  • How many: 220 of 1410 merges in the 7 days to 2026-11-01T02:00Z (15.6%) touched one of the seven packages and no ui: path (first-parent origin/main, the live filter globs, picomatch); 22 of 138 in the last 24 h. At 22 to 31 a day that is ~8,300 to ~11,700 runner-min a day, lower bound.
  • Against ci(ui-merge): queue builds decide the four ui lanes from the merged diff against merge_group.base_sha #25881: that card measured ~220 runner-min per queue entry for the four ui lanes and stopped paying it on entries that touch no ui path. This PR pays a part of it back (~188, Test UI alone) on the ~16% of entries that touch a package ui consumes — exactly the entries where it was skipped though its inputs changed. Entries touching neither stay skipped.
  • Wall clock: not on the measured critical path. On run 41091107812 (lint-only queue entry) Build UI Deps finished by 16:37:32Z and Test Core's slowest shard ended at 16:54:40Z of a run that finished at 16:55:38Z; sixteen shards of at most 14.4 min, started when Build UI Deps ended, would have finished by about 16:52Z.

Verification

Gate list derived with node scripts/pm/dispatch-gates.mjs --commands on this head (1 path vs merge base ea74ebf93): 53 derived, 53 run, 53 exit 0; --ran reconciles 53 derived, 53 run, 0 NOT-MEASURED, 0 UNRUN (a derived zero: every row records its exit code). Run at head 33b9f44f31, after a full pnpm build under scripts/pm/os-verify-lock.sh (exit 0, 13m42s) for the four dist/-reading Build Core guards; check:pm-dispatch-gates ran its fast tier (1937 cases pass, 7 slow sections deferred). Beyond the derived list, the ui tests that read the root ci.yml's ui jobs: pnpm --dir ui exec vitest run on select-affected-tests.test.ts, select-affected-tests-wiring.test.ts, turbo-cache-save-bound.test.ts, check-action-forward-parity.test.ts, lint-workflow.test.ts — 5 files, 148 tests pass. node scripts/pm/check-expected-skips.mjs --self-test: 206 cases pass. Every CI family is the PR's own check-runs.

Every derived command and its exit code
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-dts-emitted.mjs --self-test :: exit 0
node scripts/check-self-test-wired.mjs :: exit 0
node scripts/check-self-test-wired.mjs --self-test :: exit 0
node scripts/check-self-test-workflow-commands.mjs :: exit 0
node scripts/check-self-test-workflow-commands.mjs --self-test :: exit 0
node scripts/check-step-collectors.mjs :: exit 0
node scripts/check-step-collectors.mjs --self-test :: exit 0
node scripts/check-whole-set-label-write.mjs :: exit 0
node scripts/check-whole-set-label-write.mjs --self-test :: exit 0
node scripts/ci/assert-turbo-remote-cache.mjs --self-test :: exit 0
node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0
node scripts/ci/prune-turbo-cache.mjs --self-test :: exit 0
node scripts/ci/scheduled-full-run.mjs --self-test :: exit 0
node scripts/docs-audit/check-drift-comment.mjs :: exit 0
node scripts/pm/ci-failure.mjs --self-test :: exit 0
pnpm check:agent-test-spelling :: exit 0
pnpm check:aggregator-roster :: exit 0
pnpm check:ci-filter-parity :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:declared-population-live :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:node-version :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:pm-close-cards :: exit 0
pnpm check:pm-expected-skips :: exit 0
pnpm check:pm-fleet-write-dispatch :: exit 0
pnpm check:pm-fleet-write-execute :: exit 0
pnpm check:pm-fleet-write-validate :: exit 0
pnpm check:pm-governed-merges :: exit 0
pnpm check:pm-issue-transfer :: exit 0
pnpm check:pm-post-stamped :: exit 0
pnpm check:pm-record-recognisers :: exit 0
pnpm check:pnpm-acquisition :: exit 0
pnpm check:pnpm-filter-targets :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:required-contexts :: exit 0
pnpm check:shard-attestation :: exit 0
pnpm check:stall-guard-budget :: exit 0
pnpm check:stall-guard-headroom :: exit 0
pnpm check:type-check-coverage :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:workflow-status-functions :: exit 0
pnpm check:workflow-step-name-quoting :: exit 0
pnpm check:dts-closure :: exit 0
pnpm check:dual-build-cjs-loads :: exit 0
pnpm check:lean-entry-closure :: exit 0
pnpm check:sourcemap-no-sources-content :: exit 0
pnpm check:type-check-debt :: exit 0
pnpm check:pm-dispatch-gates :: exit 0

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (not touched)
  • 2. Every name I added uses the casing for its kind. (OS_UI_CONSUMERS, a CI-only step env)
  • 3. No formatter ran; pnpm lint passes. (no lintable file changed; CI)
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset. (publishes nothing: skip-changeset)
  • 6. packages/spec touched: its generated artifacts are regenerated and committed. (not touched)
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English.
  • The Before Submitting list in CONTRIBUTING.md is done.

Generated by Claude Code

The shards read the `ui` filter output alone, so a diff confined to one
of the seven packages ui consumes (packages/lint/**, spec, ...) never ran
a ui test, though the affected-test selector already answers mode=full
for it. They now start on `ui || ui_consumers`, the shape Build UI Deps,
Type Check UI and Build & E2E UI read; the Test UI aggregate explains a
skip only when both outputs said false. A select-affected-tests pin holds
the shards' if: to every outer package the selector turns full for.

Claude-Session: https://claude.ai/code/session_01QUs3BsPpUdAGmm7wLdoKaK
Co-authored-by: Claude <noreply@anthropic.com>
The select-affected-tests pin reconciled the ui_consumers list against
the package graph, a second check the ruling did not take; the Test UI
aggregate, which now explains a skip only when ui and ui_consumers both
said false, already reds on the first diff an if: regression would skip.

Claude-Session: https://claude.ai/code/session_01QUs3BsPpUdAGmm7wLdoKaK
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Nov 1, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:devx#1 · os-zhuang · session_01QUs3BvP5Aq9USg5GtmgAAR

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 33b9f44f31db4e1f915867a39e13875a555d0258
Local-runs: none
Scope: .github/workflows/ci.yml + ui/scripts/select-affected-tests.mjs (outsideTreeReasons), scripts/pm/check-expected-skips.mjs (gateTerm / jobGate / deriveGatedSkips and its ui-deps fixture), scripts/check-ci-filter-parity.mjs, ui/eslint.config.js, ui/eslint-rules/no-inline-spec-config.js, ui/eslint-rules/no-unprefixed-query-params.js, ui/scripts/tests/check-pending-changeset-literals.test.ts, ui/scripts/tests/check-action-forward-parity.test.ts, ui/scripts/tests/turbo-cache-save-bound.test.ts

Rendered by an isolated subagent at CONTRACT_REVIEW_TIER (not the implementer), adopted, with light trimming of its prose and no change to any judgment, by the session named in Reviewed-by:. Inputs read: card #28220 body and all six comments (triage 6529894991, seat-2 letter 6529967823, brief-claim 6545296123, ruling 6545651687 letter A, seat-1 stroke 6552157492, claim 6552181644); PR #28501 body, file list (one file, +36/-14) and the net diff of the head against origin/main ea74ebf93; the check-runs on the head.

① Derived judgments

  • Accept-set widened, right: the test-ui job-level if: moves from ui_tree != 'false' && ui != 'false' to ui_tree != 'false' && (ui != 'false' || ui_consumers != 'false'). This is byte-identical to the conjunct ui-deps (ci.yml:3326), type-check-ui (:3454) and build-e2e-ui (:4315) already carry, so the seven ui_consumers globs (ci.yml:566-573) now start the sixteen shards on pull_request, merge_group and push alike. The filter contract's both halves hold: ui_consumers is declared with || 'true' (:214) and read through != 'false', so a dead filter still runs the shards.
  • Accept-set of the Test UI required context narrowed, right: test-ui-gate now explains a skipped rollup only when filter succeeded and ui_tree == false, or ui == false AND ui_consumers == false. That is exactly the negation of the new shards' if: under !cancelled(), so a skip the shards' own condition does not produce is red. A future revert of the if: alone reds the first ui_consumers-only diff; a revert of the gate alone changes nothing. Shell shape { a || { b && c; }; } is valid bash and matches the stated truth table.
  • Which tests run: unchanged, right. outsideTreeReasons (select-affected-tests.mjs:511-568) returns a reason for any file whose owning outer package is reached from a ui package's dependency fields (transitively through runtime fields), which on this tree is the seven ui_consumers packages; selectAffectedTests turns mode=full on any reason (:662, :698). So a ui_consumers-only diff is a full battery. The select step in test-ui (ci.yml:3833-3870) computes its base the same way for any pull_request / merge_group diff, so nothing in the job assumes a ui path moved.
  • Lint UI accept-set unchanged, right under the ruling's own condition: ui/eslint.config.js sets no parserOptions.project / projectService, and the two local rules under ui/eslint-rules/ read no file. See ③ for the wider Lint UI observation.
  • Derived roster of check-expected-skips: the parser (gateTerm, check-expected-skips.mjs:444-456) reads a parenthesised || of needs.filter.outputs.X != 'false' terms as one filter-output group; the fixture at :1318 and the self-test case at :1526 already pin that spelling for ui-deps. The test-ui row therefore re-derives to filter ui_tree + filter ui|ui_consumers with no edit, which is why the ruling's "update the check-expected-skips derivation / self-test" resolves to no diff. check-ci-filter-parity.mjs has no ui subject (its four subjects are core / crosspkg / Build Core / Test Core); no parity edit was owed.
  • Live tests that pin the test-ui if: keep passing by inspection: check-pending-changeset-literals.test.ts:199 requires the substring needs.filter.outputs.ui != 'false' in the test-ui block (still present inside the parentheses); check-action-forward-parity.test.ts:765 pins the same on type-check-ui (untouched); turbo-cache-save-bound.test.ts:222-226 requires ui-deps, type-check-ui and build-e2e-ui if: to be equal (untouched; test-ui is not in that equality).
  • Public surface: none. A root workflow only; no package, schema or export moves.

② Semver level

Correct. The PR carries skip-changeset and Clause-②: no; Check Changeset concluded success on the head. The diff publishes nothing (a CI workflow and its comments), so no changeset is owed and no Clause-② narrowing exists. Metadata-protocol: none is right: no protocol file is touched.

③ Boundary flags

  • Lint UI not added (dev flag): answered and in-ruling. Ruling 6545651687 letter A binds Lint UI only "if the dev measures that ui's eslint reads these packages"; measured no. The dev's wider observation is correct and read in the tree: Lint UI's other steps DO read the seven, namely pnpm --filter @objectstack/spec check:liveness (ci.yml:4167) and the check:skill-examples / check:doc-snippets builds whose --build-filter closures include the @objectstack/* dists (:4214-4289), so a spec change can still red Lint UI first on a bystander's ui PR. Outside the ruling's letter (eslint only); escalated to the domain:devx seat as carrier; should-fix at most, not a FAIL. spec-liveness-check.yml:58 already runs the liveness check on spec-side diffs, so that one step is not unguarded.
  • Root files not in ui: (.npmrc, eslint.config.mjs, vitest.preset.mts, patches/** etc.; dev flag): the selector turns full for them (select-affected-tests.mjs:548-556) but the shards never start. Correctly scoped out of this ruling; escalated to the seat with dedupe words. Not acted on, rightly.
  • No permanent pin reconciling ui_consumers against the selector's package graph (dev flag): consistent with the ruling declining option B's second list and its reconciling gate; the test-ui-gate change is the in-surface regression check instead. Accepted.
  • Assignees none (dev flag): a seat matter, no judgment owed here.
  • open_questions: none in the body.
  • Runner-minute cost (ruling requirement): stated against ci(ui-merge): twenty ui jobs each rebuild the same seven @objectstack packages (1.6–2.5 min apiece, mostly turbo cache misses on PRs) — build them once per run and hand the dists to the ui lanes (parent #25677) #25869 / ci(ui-merge): queue builds decide the four ui lanes from the merged diff against merge_group.base_sha #25881 with measured numbers. One should-fix on the prose only: the "at least ~376 per merge" count names one PR-head run and one queue entry, but push: branches: [main] (ci.yml:4-6) also runs the shards on the merged commit (a ui_consumers-only merge previously skipped them there too), so the per-merge floor is closer to ~564 runner-min, and the daily lower bound is correspondingly higher. "At least" keeps the sentence true; the decomposition undercounts. Not a tree defect.

④ Design judgment

.github/workflows/ci.yml (the only hits file), held against the ruling scenario (6545651687 letter A, adopting 6529967823): a diff confined to packages/lint/** (or any of the seven) starts the Test UI shards on the PR and in the queue, with the same shape the other ui jobs use, which tests run left to the selector, and the derivation / parity gates updated in the same PR.

  • Holds on the PR event: a packages/lint-only diff yields ui=false, ui_consumers=true; the new if: runs the shards; the select step diffs against merge-base(origin/main, HEAD); outsideTreeReasons names @objectstack/lint changed, and this tree's tests read its built output; mode=full; sixteen shards run the whole suite including AutomationsPillar.problemsAgree-27799.test.tsx. The fix(lint): a flow node config-value finding carries the slot's key path, so Studio's Problems row selects the node #28192 → fix(ui): Setup metadata lists take their title from the sidebar and lead with the label #28197 scenario is closed.
  • Holds in the queue: on merge_group the ui outputs come from the queue diff against merge_group.base_sha; the same ui_consumers=true starts the shards; the select step uses QUEUE_BASE; full battery. If the queue diff step fails, outputs are empty, != 'false' runs the shards: fail-open preserved.
  • Holds on push to main: the full battery runs (select step's * branch); formerly skipped for seven-package-only merges, now run. A cost, not a defect.
  • Gate consistency: tried each (ui_tree, ui, ui_consumers) combination; the shell condition is the exact complement of the shards' if: and no combination lets a skipped rollup pass that the if: would have started. The failed-filter branch stays red. name: Test UI and if: always() untouched, so the required context still publishes.
  • Attempted break, selector disagreement: a ui_consumers-only diff cannot start the shards and then select an empty affected set: any outer-package file reached by the graph is a reasons entry and reasons.length > 0 forces full before the affected path is computed (:662-698); an unresolvable parse is also full; the step's fail-open wrapper treats any non-verdict as full.
  • Attempted break, parser refusal: the spelling used is the one the fixture at check-expected-skips.mjs:1318 already accepts and the self-test at :1526 pins; refusal is not possible for this line.
  • Attempted break, comment claims: the new comment above test-ui and the two edited comments in filter and ui_consumers match the code they describe; the ui-deps header's union claim (:3323-3324) and the filter header's "five ui jobs" remain true. "Lint UI keeps ui alone: its eslint reads none of the seven" is literally true and does not claim Lint UI as a whole reads none.
  • Should-fix (prose, carried to the seat, not blocking): the per-merge runner-minute decomposition omits the push-to-main run (③ above); the Lint UI residue is a card for the devx seat as the dev names.
  • Must-fix: none.

Check-runs on the head at review time: filter, Check Changeset, Check PR Size, Auto Label, Governed Surface Queue Guard, PR-body guards, Dogfood Regression Gate, Plan Test Core shards, Lint · ESLint and the three Lint · scoped jobs concluded success; Build Core, Build Docs, Build UI Deps, Live E2E UI, Test Core (1/8), the four Type Check · jobs and four Lint · jobs were in_progress; the Test UI (k/16) shards, Type Check UI, Lint UI and Build & E2E UI had not yet been created (they wait on Build UI Deps). No check-run had failed. An in-progress run is not a pass; this verdict is on the design and the diff, and the Tier H landing still waits on every gate family concluding green on this head and the maintainer's approval.

Implemented-by: claude/project-thread-cbuqpy
Reviewed-by: session_01QUs3BsPpUdAGmm7wLdoKaK

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:devx#1 · os-zhuang · session_01QUs3BvP5Aq9USg5GtmgAAR

席位核对 — ACCEPT

Reviewed head 33b9f44f31 against origin/main for #28220 (ruling 6545651687, letter A; claim on the card, branch claude/project-thread-cbuqpy).

Check Reading
Path surface (/pulls/28501/files) .github/workflows/ci.yml +36/−14 only — inside the claimed surface; the dev's transient ui/scripts pin was dropped before the head
Governed check-governed-merges.mjs --pr 28501 → ⛔ GOVERNED, 50 changed lines: a human merge is the review record. The seat does not approve, queue or merge this PR
Ruling fit test-ui if: now ui_tree != 'false' && (ui != 'false' || ui_consumers != 'false') — the shape Build UI Deps / Type Check UI / Build & E2E UI already read. Not taken: B's hand list, C's import-precise selection
Aggregate consistency the Test UI verdict explains skipped only when ui_tree='false' or both ui and ui_consumers are 'false' — the exact negation of the shards' if:; an empty output (filter step failed) still runs the shards
Lint UI kept on ui alone: its eslint reads none of the seven (dev's measurement); see the noted finding below
Derivations check-expected-skips and CI filter parity derive from the workflow, so no edit was needed; Lint · repo & tooling gates, Lint · CI, release & engine gates, Lint · PM dispatch-gates and Lint · scoped · tooling self-tests are green on this head
Live proof this head's own run started all sixteen Test UI shards, 16/16 success, aggregate success
Contract review PASS at CONTRACT_REVIEW_TIER, record 6552512600

Correction the seat carries (dev's should-fix, not patched into the body): the per-merge cost in the body ("~376 runner-min", PR head + queue) omits the push run on main, which also starts the full battery for these diffs. The floor is ~564 runner-min per such merge (3 × ~188), so the daily lower bound at 22–31 such merges is roughly 12,400–17,500 runner-min, not 8,300–11,700.

Noted, not filed (seat is carrier):

  1. Lint UI's non-eslint steps (spec check:liveness, doc-snippet / skill-example type checks) do read the seven packages, so the same skip gap exists there at a smaller scale.
  2. Root non-doc files and patches/** make the ui selector answer mode=full but start no Test UI shards.

Verdict: ACCEPT. Landing waits on the maintainer's merge (Tier H).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants