Skip to content

ci: gate steps run their root check:* alias, every check alias has a caller, and check-changeset-fixed gets a --self-test - #26482

Merged
os-zhuang merged 3 commits into
mainfrom
claude/project-thread-y83svr
Oct 27, 2026
Merged

os-zhuang merged 3 commits into
mainfrom
claude/project-thread-y83svr

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steve · project thread
Fixes #26091

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:lint.yml 顶部的 GATE INVOCATION IDIOM 说明改成新规则:门禁步骤跑根 package.json 里的 check:* 别名。有 7 个门禁的自测原来钉着「lint.yml 直接 node 调用」,现在改钉两半:步骤调别名,别名的值是先 --self-test 再跑门禁。原来没有调用方的 4 个别名,接上了 3 个,删了 1 个。check-changeset-fixed.mjs 补了 --self-test。
  • 为什么改:tooling: root scripts/ is about 462k lines, 20 scripts are referenced from nowhere, 22 root check:* aliases are called by no workflow, hook or script, the same gate is spelled two ways, and 5 check-* scripts have no --self-test #26091 Wanted 2 要的是「CI 只有一种调用拼法,package.json 是唯一的命令表」。原来的说明写的正好相反,还有 7 个自测把相反的写法钉死了。
  • 风险与回滚:门禁一个没删,也没有变弱:每个改动的步骤跑的还是同样的几段命令,只是写进了别名。只有 symbol-anchor 一处换了形状:原来一个收集器步骤跑 4 个自测、后面再跟 3 个扫描步骤,现在是 4 个独立步骤,每步都带 continue-on-error,一个红了不会挡住别的。回滚就是 revert。
  • 先看:lint.yml 第 109 行起的新说明、symbol-anchor 那几步,以及 7 个自测里的 WIRING 断言。
  • 两分钟验证:本地跑 pnpm check:aggregator-roster、pnpm check:changeset-fixed、pnpm check:adr-symbol-anchors,都是 exit 0;把 package.json 里任一别名的 --self-test 去掉,对应的自测就会红。
  • 需要你做的:Tier H(改了 .github/workflows/**,含 release.yml),要 GOVERNED_APPROVERS(os-zhuang 或 hotlong)批准。

What changes

The rule (lint.yml's GATE INVOCATION IDIOM note, rewritten). A gate step runs its root check:* alias, so package.json holds the one definition of the command. A new gate, or one whose step is being rewired, gets a key and CI calls it. A step still runs node scripts/… directly in three cases only:

The #9465 scope paragraph is kept verbatim, because check-platform-checklist.mjs points readers at it.

The pins move, they are not removed. These seven gates asserted that lint.yml invokes node <self>. Each now asserts both halves: the run: pnpm check:<name> line in lint.yml, and the alias value in root package.json. Deleting a leg from either side still reds.

Gate Alias Step now
check-aggregator-roster check:aggregator-roster (new) pnpm check:aggregator-roster
check-ci-filter-parity check:ci-filter-parity (new) pnpm check:ci-filter-parity
check-doc-frontmatter check:doc-frontmatter (new) pnpm check:doc-frontmatter
check-docs-nav-label check:docs-nav-label (new) pnpm check:docs-nav-label
check-adr-symbol-anchors check:adr-symbol-anchors pnpm check:adr-symbol-anchors
check-scripts-symbol-anchors check:scripts-symbol-anchors pnpm check:scripts-symbol-anchors
check-spec-docblock-symbol-anchors check:spec-docblock-symbol-anchors pnpm check:spec-docblock-symbol-anchors

The seat named three gates. The census found four with the same node <self> pin (check-docs-nav-label is the fourth), plus the three symbol-anchor corpora, which pin their lines the same way.

The four aliases with no caller (Wanted 2's second half).

  • check:adr-symbol-anchors, check:scripts-symbol-anchors, check:spec-docblock-symbol-anchors are now called. The run_self_test collector that ran four self-tests, followed by three bare sweep steps, becomes four steps. Each has its own id: and continue-on-error, so a red one still hides none of the others. The steps are pnpm check:symbol-anchors (new alias: the shared resolver's --self-test), then one pnpm check:<corpus>-symbol-anchors per corpus (that corpus's self-test, then its sweep).
    • The three corpus aliases no longer prefix the resolver's self-test. With it bundled in, the resolver's no-path-population marker became each corpus family's declaration while the corpus names paths. dispatch-gates --self-test reds on exactly that contradiction, and it did in the first gate round.
  • check:pm-closed-card-sweep is removed. Its only CI run is in half-state-patrol.yml, whose job deliberately sets up no pnpm, and which already runs the same --self-test before the sweep. That inline line is now the one definition, and the step's comment says so. No gate goes: the self-test and the sweep both still run.

check-changeset-fixed.mjs gets a --self-test, the leftover from Wanted 3. Its 12 cases cover the pure judge(): every missing, stale and declared-private rule, red and green. They include the privatePackages.version rule and the shipped PRIVATE_FIXED_MEMBERS rows. The shape matches check-node-version.mjs: a roster floored per row, a pinned roster size, and a verdict handshake. It runs through a new check:changeset-fixed alias in validate-deps.yml and in both release.yml lanes, and both lanes already install pnpm. With every caller on the alias, the script has one gate family, reached on pull_request from validate-deps. The first gate round showed that a direct node call left behind in release.yml becomes a second family, reached only by push or schedule.

Prose that the touched steps made false, and only that:

  • the symbol-anchor block comment;
  • the docs-nav-label pointer;
  • the route-spelling pointer, which said "same as the frontmatter step above";
  • the "Wiring" docblocks of check-ci-filter-parity.mjs and check-doc-frontmatter.mjs;
  • the half-state-patrol comment that named the removed alias;
  • one example in the issue-citations comment that named the removed alias.

The other pointer comments ("invoked as node rather than through a pnpm check:* alias: see the GATE INVOCATION IDIOM note") sit on steps whose scripts have no alias. They stay accurate under the new note, which names that case, so they are left as they are (#25919).

Tests

  • tooling: root scripts/ is about 462k lines, 20 scripts are referenced from nowhere, 22 root check:* aliases are called by no workflow, hook or script, the same gate is spelled two ways, and 5 check-* scripts have no --self-test #26091 acceptance.

    • Root check:* keys with no caller in any workflow, hook, scripts/ file or other package.json script: 4 on main, 0 here.
    • for f in scripts/check-*.mjs; do grep -q -- --self-test $f || echo $f; done prints scripts/check-changeset-fixed.mjs on main, and nothing here.
  • Every touched alias, run locally the way CI runs it: check:aggregator-roster, check:ci-filter-parity, check:doc-frontmatter, check:docs-nav-label, check:symbol-anchors, check:adr-symbol-anchors, check:scripts-symbol-anchors, check:spec-docblock-symbol-anchors, check:changeset-fixed. All exit 0.

  • Negative controls.

    • With --self-test removed from check:doc-frontmatter, its self-test fails on wiring: root package.json's check:doc-frontmatter runs the --self-test leg, then the gate.
    • With the check:adr-symbol-anchors alias changed, its self-test fails with the alias named.
    • In check-changeset-fixed, disabling the privatePackages.version rule reds its case, and emptying stale reds 3 cases.
  • The step census. It counts steps whose inline node lines equal a root alias byte for byte. The result is 2 on main and 2 here: the two install-free jobs, which the note names.

  • Gate run, standard order. dispatch-gates --commands derived 77 commands for this diff on the merged tree (44a3660e, after main at 67461403, which includes ci: one setup-workspace composite action replaces the setup steps copied into 38 jobs, and pull requests stop saving the pnpm store #26419). All 77 exit 0. dispatch-gates --ran reports 77 derived, 77 run, 0 NOT-MEASURED, 0 UNRUN. The first round on the pre-fix tree reded check:pm-dispatch-gates (2 of 1901 cases). The two causes are the ones described under the symbol-anchor and changeset-fixed bullets above, and both are fixed here.

Gate log (77 commands, exit code and seconds)
0  2s     node scripts/check-closing-keyword-parity.mjs
0  5s     node scripts/check-closing-keyword-parity.mjs --self-test
0  193s   node scripts/check-comment-mask-corpus.mjs
0  0s     node scripts/check-declaration-mirrors.mjs
0  0s     node scripts/check-declaration-mirrors.mjs --self-test
0  0s     node scripts/check-dts-emitted.mjs --self-test
0  1s     node scripts/check-merged-branch-reaper-outcome.mjs
0  0s     node scripts/check-merged-branch-reaper-outcome.mjs --self-test
0  0s     node scripts/check-osv-exemptions.mjs
0  0s     node scripts/check-osv-exemptions.mjs --self-test
0  2s     node scripts/check-self-test-wired.mjs
0  5s     node scripts/check-self-test-wired.mjs --self-test
0  125s   node scripts/check-self-test-workflow-commands.mjs
0  4s     node scripts/check-self-test-workflow-commands.mjs --self-test
0  0s     node scripts/check-step-collectors.mjs
0  5s     node scripts/check-step-collectors.mjs --self-test
0  2s     node scripts/check-whole-set-label-write.mjs
0  0s     node scripts/check-whole-set-label-write.mjs --self-test
0  0s     node scripts/ci/assert-turbo-remote-cache.mjs --self-test
0  0s     node scripts/ci/plan-test-core-shards.mjs --self-test
0  0s     node scripts/ci/prune-turbo-cache.mjs --self-test
0  1s     node scripts/ci/scheduled-full-run.mjs --self-test
0  1s     node scripts/docs-audit/check-drift-comment.mjs
0  0s     node scripts/osv-base-relative.mjs --self-test
0  30s    node scripts/pm/bare-root-worklist.mjs --self-test
0  0s     node scripts/pm/ci-failure.mjs --self-test
0  0s     node scripts/pm/sweep-closed-cards.mjs --self-test
0  1s     node scripts/release-dist-tags.mjs --self-test
0  7s     node scripts/release-verify-npm.mjs --self-test
0  8s     pnpm check:adr-symbol-anchors
0  3s     pnpm check:agent-test-spelling
0  3s     pnpm check:aggregator-roster
0  2s     pnpm check:bash32-floor
0  1s     pnpm check:changeset-fixed
0  1s     pnpm check:ci-filter-parity
0  17s    pnpm check:cli-command-ids
0  33s    pnpm check:cross-package-test-inputs
0  36s    pnpm check:declared-population-live
0  2s     pnpm check:doc-frontmatter
0  1s     pnpm check:docs-nav-label
0  5s     pnpm check:driver-memory-census
0  1s     pnpm check:dts-closure
0  22s    pnpm check:dual-build-cjs-loads
0  38s    pnpm check:entry-guard
0  1s     pnpm check:gitlink-declared
0  3s     pnpm check:lean-entry-closure
0  1s     pnpm check:manifest-repository-directory
0  23s    pnpm check:merge-driver
0  1s     pnpm check:node-version
0  4s     pnpm check:nul-bytes
0  1s     pnpm check:override-consistency
0  4s     pnpm check:parse-guard
0  376s   pnpm check:pm-dispatch-gates
0  5s     pnpm check:pm-expected-skips
0  10s    pnpm check:pm-governed-merges
0  1s     pnpm check:pm-post-stamped
0  4s     pnpm check:pm-record-recognisers
0  1s     pnpm check:pnpm-acquisition
0  3s     pnpm check:pnpm-filter-targets
0  6s     pnpm check:pnpm-flag-spellings
0  4s     pnpm check:ratchet-remedy-authority
0  23s    pnpm check:refd-timer-probe
0  4s     pnpm check:required-contexts
0  8s     pnpm check:scripts-symbol-anchors
0  4s     pnpm check:select-gate-families
0  3s     pnpm check:shard-attestation
0  1s     pnpm check:sourcemap-no-sources-content
0  10s    pnpm check:spec-docblock-symbol-anchors
0  2s     pnpm check:stall-guard-budget
0  4s     pnpm check:stall-guard-headroom
0  1s     pnpm check:turbo-task-graph
0  5s     pnpm check:type-check-coverage
0  358s   pnpm check:type-check-debt
0  12s    pnpm check:vendor-export-contract-resolve
0  6s     pnpm check:watch-hint-literal
0  2s     pnpm check:workflow-status-functions
0  1s     pnpm check:workflow-step-name-quoting

Acceptance notes

Verification

for a in aggregator-roster ci-filter-parity doc-frontmatter docs-nav-label symbol-anchors adr-symbol-anchors scripts-symbol-anchors spec-docblock-symbol-anchors changeset-fixed; do pnpm -s check:$a > /dev/null 2>&1; echo "$? check:$a"; done
for f in scripts/check-*.mjs; do grep -q -- --self-test $f || echo $f; done   # prints nothing

Tier H (.github/workflows/**, including release.yml): draft until a GOVERNED_APPROVERS approval (os-zhuang, hotlong).


Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_01ADHkmQq9EYQdTQwmRutQTS

The lint.yml invocation note now states the rule: a gate step runs its
root check:* alias, and direct node stays only for a different
invocation, a job without pnpm, or a legacy step with no alias yet.

The gates that pinned their own direct wiring (aggregator roster, CI
filter parity, doc frontmatter, docs nav label, the three symbol-anchor
corpora) now run through new or existing aliases, and their self-tests
pin both the step and the alias value. The symbol-anchor collector and
its three sweep steps become one alias step per corpus.

check-changeset-fixed.mjs gains a --self-test, run through a new
check:changeset-fixed alias in validate-deps. check:pm-closed-card-sweep
is removed: its only CI run is in a job without pnpm, which keeps the
direct self-test line.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ADHkmQq9EYQdTQwmRutQTS
Each corpus alias bundled the resolver's self-test, which made the
resolver's no-path-population marker the corpus family's declaration
while the corpus names paths. The resolver now has check:symbol-anchors
and its own lint step; the corpus aliases run their self-test and sweep.

Both release.yml lanes run check:changeset-fixed through the alias too,
so the script has one family, reached from validate-deps on pull_request.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ADHkmQq9EYQdTQwmRutQTS
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 27, 2026
@github-actions github-actions Bot added ci/cd dependencies Pull requests that update a dependency file labels Oct 27, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review October 27, 2026 04:43
@os-zhuang
os-zhuang enabled auto-merge October 27, 2026 04:43
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · devx seat 2 · 2026-10-27T04:45Z

Seat domain:devx#2 (#20163) · session_01Q4MDXRbEJCNdPAP4SiXwp9 · reviewed head 44a3660e · card #26091 (claim 6432932050), wave 9 thread C, final PR (Fixes #26091). Tier H (check-governed-merges: governed, tier H, humanMerge); +334/−147. The approval, readying and auto-merge are a GOVERNED_APPROVERS member's own acts (04:43Z).

  • Wanted 2, the 4 caller-less aliases: the three symbol-anchor corpus aliases are now what lint.yml runs; the shared resolver's self-test got its own alias and step instead of riding in each corpus alias, which keeps its no-path-population marker apart from the corpus populations. The old run_self_test collector goes with it: each of the four is its own continue-on-error step with an id:, so a red one still cannot hide the others ([finding] lint.yml's shallow-history step runs three self-tests under bash -e — a failure in the first silently prevents the other two from running at all #10814). check:pm-closed-card-sweep is deleted, and the deletion is the right call: its only CI caller, half-state-patrol.yml, sets up no pnpm, and the comment there now says so.
  • Wanted 3, check-changeset-fixed --self-test: 12 cases against judge() only, no tree read, with a roster/floor and a verdict flag in the check-node-version shape. Green directions (every public package grouped, split groups, a declared private member) and red ones (missing, stale, undeclared private, declared member outside the group, declared member when privatePackages.version is not true, declared member whose manifest is public). Run locally on the head: pnpm check:changeset-fixed exit 0.
  • release.yml (deviation b), accepted: both release steps are in jobs that already run ./.github/actions/setup-workspace, so pnpm is there. The only behaviour change is that the self-test runs before the gate. This is not one of the trust-boundary bodies (Guard / Render / Validate): it reads .changeset/config.json from the same tree as before. Without it, the alias would be reached only from validate-deps' push/schedule runs, and dispatch-gates' self-test reds on that.
  • The IDIOM note: rewritten to "a gate step runs its root alias", naming the three cases that keep node (different arguments or one leg, no pnpm in the job, not yet rewired), and adding no sweep (docs: narrative cost — 42.5% of source lines are comments, workflows 58.6%, AGENTS.md is 103 KB with three stale claims, ROADMAP.md still says v3.3.2 and 80 Storybook stories #25919). The pins moved to the alias in 7 self-tests rather than 3, both halves asserted. Locally on the head, check:docs-nav-label's self-test, check:symbol-anchors and the three corpus aliases exit 0. check:aggregator-roster, check:ci-filter-parity and check:doc-frontmatter exit 3 here (no node_modules in the seat container; NOT MEASURED). Their verdict is the PR's Lint jobs.
  • Pins outside the diff: no ui/scripts/__tests__/** file or root script reads the changed step lines or the removed collector id (git grep on the head). ui/'s own check-changeset-fixed copy is separate and untouched.

Lands on green CI. If a Lint job reds on a moved pin, the thread patches on the same branch.

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

Labels

ci/cd dependencies Pull requests that update a dependency file size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants