Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 115 additions & 11 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,45 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile

# ── Gate-family selection (#16496) ───────────────────────────────────
# After #16453 this job is the merge queue's critical path (median 1468 s
# over the ten merge-group runs measured for #16496; the PM dispatch-gates
# self-test alone 597 s), and none of its expensive steps read a
# merge group's file surface: a docs-only group paid the full battery.
# So on `merge_group` and `pull_request` the FIVE scoped families below
# -- each step carrying `if: steps.gate-families.outputs.<id> != 'skip'`
# -- run only when the changed paths touch the files that family reads.
# `push` on main and the scheduled full run keep the whole battery: the
# script runs everything for any event it does not scope.
#
# ⭐ The invariant is FAIL-OPEN, and it holds at both layers. The script
# runs every family when the base cannot be resolved, the diff fails or
# is empty, a path is one it does not classify (a new top-level
# directory, an unlisted root file), or any change is a deletion, rename
# or type change; a family is skipped ONLY when every changed path is
# positively classified into a class that family provably never reads.
# The `!= 'skip'` spelling means an ABSENT output -- the selector never
# ran, or wrote nothing -- also runs the step. Both halves are pinned by
# `scripts/ci/select-gate-families.selftest.sh` (`check:select-gate-
# families`, further down this job), which also reads THIS file and
# refuses any other `if:` spelling and any family the script does not
# decide. The per-family read-sets, and how each was measured, are in
# the script's header.
#
# The selection itself lives in scripts/ci/select-gate-families.sh:
# environment in, `$RUNNER_TEMP/gate-families.txt` + `$GITHUB_OUTPUT`
# out, and a ran/skipped table in the job summary (the seat reads it).
# This step only exports the event's fields, so the same script -- every
# branch of it -- runs locally and under its self-test.
- name: Select the gate families this run pays for
id: gate-families
env:
OS_GATE_EVENT_NAME: ${{ github.event_name }}
OS_GATE_PR_BASE_REF: ${{ github.event.pull_request.base.ref }}
OS_GATE_MERGE_GROUP_BASE_SHA: ${{ github.event.merge_group.base_sha }}
run: |
bash scripts/ci/select-gate-families.sh

# #7484 docs anchors: `lychee.toml` sets `include_fragments = "none"`, so
# the `Check Documentation Links` job resolves a link's FILE and never its
# `#anchor` — measured with the pinned lychee 0.24.2 under the CI argv, a
Expand Down Expand Up @@ -251,7 +290,12 @@ jobs:
# file is ignored COMPLETELY, so new erasures added to one ride the old
# entry in silence. This re-measures those files with the grandfathering
# lifted and holds them to a per-file count, so the list can only shrink.
#
# Scoped (#16496): reads `packages/**/*.{ts,tsx,mts,cts}`, its baseline,
# `eslint.config.mjs` and the top-level `scripts/*.mjs` it imports; a
# group touching none of those skips it (selection step above).
- name: Slot-lookup ratchet
if: steps.gate-families.outputs.slot_lookup != 'skip'
run: pnpm check:slot-lookup

# Engine query-options erasure ratchet (#4918). The slot-lookup rule above
Expand All @@ -269,7 +313,11 @@ jobs:
# slot-lookup for the non-test residual, plus one aggregate decrease-only
# number for test code (a test whose subject IS off-contract engine input
# must be able to build it). Runs its own --self-test first.
#
# Scoped (#16496): the same population and inputs as the slot-lookup
# ratchet above, with `scripts/query-options-erasure-baseline.json`.
- name: Engine query-options erasure ratchet
if: steps.gate-families.outputs.query_options_erasure != 'skip'
run: pnpm check:query-options-erasure

# @objectstack/verify stand-in erasure guard (#6399). The third and
Expand Down Expand Up @@ -620,7 +668,13 @@ jobs:
# Cost, measured on a contended 4-vCPU container: 4,741 files, 72 MB of
# source, 45–48 s, of which the parser is 45 s. The step prints CI's own
# number on every run.
#
# Scoped (#16496): the walk reads every `.ts .tsx .mts .cts .js .mjs
# .cjs .jsx` file in the tree, so a group that changes no file of those
# extensions (and no root configuration) skips it -- selection step at
# the top of this job.
- name: Comment mask agrees with a real parser over the whole corpus
if: steps.gate-families.outputs.comment_mask_corpus != 'skip'
run: node scripts/check-comment-mask-corpus.mjs

# Stack-collection enumerations vs the schema (#6242). `stack.zod.ts`
Expand Down Expand Up @@ -745,9 +799,28 @@ jobs:
# green and surfaced later as a prompt naming the wrong gate families —
# output that reads as correct, produced by the one tool whose purpose is
# to stop gate lists being memory-shaped. Same family as the
# `Changeset-family gate self-tests` step above, and like that one this
# step is deliberately UNCONDITIONAL: no `if:`, no label read, no paths
# filter. An exemption is what a self-test must not have, or the gap moves.
# `Changeset-family gate self-tests` step above. Until #16496 this step
# was deliberately UNCONDITIONAL -- no `if:`, no label read, no paths
# filter -- on the argument that an exemption is what a self-test must
# not have, or the gap moves. That argument was about an exemption a
# PATH FILTER or a LABEL would grant: a paths: trigger or a label read
# skips the step on a card that never touched the paths someone once
# listed, with no reader of what the test actually reads. What runs
# here now is different in kind and was the maintainer's call (#16496,
# 「同意你的建议,你负责执行派发所有可行的优化」): the selection step at
# the top of this job classifies every changed path against this
# self-test's MEASURED read-set (every workflow, every gate source under
# `scripts/**` and `packages/*/scripts/**`, every `package.json`,
# `.claude/**`, `skills/**`, `AGENTS.md`, `CLAUDE.md`, `tsconfig.json`,
# every `.gitignore` -- nested ones included -- the CONTENT of every
# JS/TS file in the tree (the compound-anchor census of
# `function ...SelfTest...(` declarations and the exposed-scratch-dir
# sweep of every mkdtempSync/mkdirSync caller both assert over it), and
# the tracked NAME set it sweeps -- so any added, deleted or renamed
# file runs it too), and skips this step only when every path is one
# the test provably never reads: a modified doc, changeset or non-source
# workspace file. Every doubt runs it, the self-test of the selector
# pins that, and `push` on main keeps it unconditional.
#
# The gate runs the SELF-TEST only. The live derivation
# (`node scripts/pm/dispatch-gates.mjs <path>`) answers a question about a
Expand Down Expand Up @@ -775,13 +848,17 @@ jobs:
# counts stand here either, for the same reason — they rot the same way,
# and it is the SHAPE of the work, not its size, that the argument needs.
#
# ⛔ Cost was never what makes this step unconditional. The FIRST
# paragraph above is, and it stands without this half: an exemption is
# what a self-test must not have, or the gap moves. Losing the estimate is
# therefore NOT an argument for an `if:`, a label read or a paths filter —
# weakening this gate is a maintainer call, and no reading of this note
# authorises one.
# ⛔ Cost was never what decided this step's conditionality, and the
# measured medians that motivated #16496 (597 s here, over ten
# merge-group runs) live in that card and its PR, not in this note.
# Losing the estimate is NOT an argument for a label read or a paths
# filter, and the read-set-scoped `if:` below is not one of those: it
# was a maintainer call (the first paragraph names it), and widening the
# skip -- any change to the classes the selector treats as inert for
# this family -- is again a maintainer call, taken in
# scripts/ci/select-gate-families.sh under its self-test.
- name: PM dispatch-gates self-test
if: steps.gate-families.outputs.pm_dispatch_gates != 'skip'
run: pnpm check:pm-dispatch-gates

# Every ROOT_DIR_WATCH_HINTS declaration stays READABLE BY A TEXT SCANNER
Expand Down Expand Up @@ -1318,9 +1395,21 @@ jobs:
#
# It runs entirely on a private lock under a temp dir; it never touches
# /tmp/os-heavy-verify.lock, so a runner and an agent container behave the
# same. Unconditional and un-`if:`-ed, like the self-tests above — an
# exemption is what a self-test must not have. No network, ~40 s.
# same. No network; median 100 s over the ten merge-group runs measured
# for #16496.
#
# Scoped (#16496): the suite reads the script itself (`$SELF`) and a
# private temp dir, and its case (h) runs the real entry point from the
# repo root, which routes through filter_preflight ->
# scripts/pnpm-filter-targets.mjs --preflight and so reads the top-level
# scripts/*.mjs helpers that imports, pnpm-workspace.yaml and every
# workspace package.json. A group touching none of those skips it. That
# is not the exemption the self-tests above refuse (a label or a paths
# filter with no reader behind it): the selection step at the top of
# this job runs this step on every doubt, and `push` on main keeps it
# unconditional.
- name: Verify-lock entry-point self-test
if: steps.gate-families.outputs.verify_lock != 'skip'
run: bash scripts/pm/os-verify-lock.sh --self-test

# ci-failure self-test (#9898). Fifth member of the PM self-test family
Expand Down Expand Up @@ -3299,6 +3388,21 @@ jobs:
- name: Test Core package selection self-test
run: pnpm check:select-shard-packages

# Gate-family selection self-test (#16496). The "Select the gate families
# this run pays for" step at the top of this job is a thin call into
# scripts/ci/select-gate-families.sh, and its `merge_group` branch cannot
# run before a PR is already in the queue, so it is proved here: every
# event, the card's four cases (a scripts/pm change, a docs-only group, a
# workflow change, an unknown path), and every fail-open branch (empty
# diff, unresolvable base, deletion / rename, unspellable path), pinning
# which families ran and skipped, for which reason, and which
# `::warning::` lines fired. It also reads THIS file and pins the YAML
# half: the selector step's id and env, the `!= 'skip'` spelling on every
# scoped step, and that the scoped set equals the decided set. Offline;
# ~3 s.
- name: Gate-family selection self-test
run: pnpm check:select-gate-families

# Aggregator roster gate (#10490). Three required contexts are aggregate
# jobs standing in for a set of real jobs — `Test Core` and `Dogfood
# Regression Gate` in ci.yml, `TypeScript Type Check` in this file — and
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@
"check:pnpm-acquisition": "node scripts/check-pnpm-acquisition.mjs --self-test && node scripts/check-pnpm-acquisition.mjs",
"check:workflow-status-functions": "node scripts/check-workflow-status-functions.mjs --self-test && node scripts/check-workflow-status-functions.mjs",
"check:select-shard-packages": "bash scripts/ci/select-shard-packages.selftest.sh",
"check:select-gate-families": "bash scripts/ci/select-gate-families.selftest.sh",
"check:shard-attestation": "node scripts/check-shard-attestation.mjs --self-test && node scripts/check-shard-attestation.mjs",
"check:required-contexts": "node scripts/check-required-contexts.mjs --self-test && node scripts/check-required-contexts.mjs",
"check:cross-package-test-inputs": "node scripts/check-cross-package-test-inputs.mjs --self-test && node scripts/check-cross-package-test-inputs.mjs",
Expand Down
Loading
Loading