Commit cf464f6
Fixes #19498
Clause-②: no
Gate weakening is a maintainer floor. The sentence that authorizes this
one, verbatim (ruling #208 on #19491, part R4):
> 19491 接受你的建议,并立刻派发处理相关任务。
## What this changes
`Lint & Repo Gates` set the wall clock of PR #19314's CI — 27.4 minutes
over 184 steps for a three-file `packages/spec` diff, above the longest
test shard — and 18.6 of those minutes were the tooling's own
self-tests, corpora and censuses. The single `PM dispatch-gates
self-test` step was 11.8 of them, on a PR that changes no PM tool: that
family's read-set included the whole-tree censuses its battery runs —
the content of every JS/TS and `.sh` file, the nested `.gitignore`
files, and the tracked NAME set, which made an ADDED path anywhere run
it.
1. **`pm_dispatch_gates` is narrowed to the tool's own inputs**: the
workflow tree and composite actions its discovery reads, every gate
source it resolves under `scripts/` and a workspace package's own
`scripts/`, the `package.json` that names a `check:*` script, the agent
configuration its live cases read (`.claude/**`, `skills/**`,
`AGENTS.md`, `CLAUDE.md`), and root configuration. The self-test's own
refusal semantics are untouched — an unreadable population still
refuses.
2. **Four more tooling steps go behind the selector**, each as a family
with a read-set of its own inputs plus the matching `if:` line in
`lint.yml`.
3. **`push` to main and the hourly scheduled run are unchanged** and
keep the whole battery: the selector runs everything for any event it
does not scope.
## The census: every unconditional step at or above ~0.3 min
Measured on run 35506407130, job `Lint & Repo Gates` (check-run
106066910262) at head `7d67e1ee4136aee8f8e6ea838950c7fb71520be2`, read
step by step from `GET /repos/{owner}/{repo}/actions/jobs/106066910262`.
184 steps, 27.4 min.
| step | min | subject | disposition |
|:--|--:|:--|:--|
| PM dispatch-gates self-test | 11.80 | tooling self-test | already
scoped — **read-set narrowed** |
| Engine query-options erasure ratchet | 2.07 | product ratchet |
unchanged (scoped by its real read-set) |
| ESLint | 1.27 | product lint | stays unconditional (#16496 card ruling
2) |
| Slot-lookup ratchet | 1.07 | product ratchet | unchanged (scoped by
its real read-set) |
| Comment mask agrees with a real parser over the whole corpus | 0.90 |
corpus agreement | unchanged (already a family) |
| scripts/ entry guards go through one predicate | 0.62 | tooling corpus
over `scripts/**` | **moved** → `entry_guard` |
| Engine test-double contract gate | 0.50 | product gate | stays
unconditional |
| Self-test workflow-command gate | 0.48 | tooling self-test | **moved**
→ `self_test_workflow_commands` |
| A declared gate population reaches the tree | 0.40 | tooling gate over
the derivation | **moved** → `declared_population_live` |
| Checkout repository | 0.40 | runner infrastructure | not a gate |
| ADR anchors + number uniqueness | 0.37 | docs/ADR gate | stays
unconditional |
| Tenant-audit census matches the tree | 0.35 | product census | stays
unconditional (named in the card) |
| PM bare-root worklist self-test | 0.32 | tooling self-test | **moved**
→ `bare_root_worklist` |
Below the line, left unconditional because the list decides and not the
principle: `scripts/ shared-module self-tests` 0.27, `Cross-package test
inputs` 0.25, `Declared registry log level` 0.25, `Platform-object
tenancy census` 0.20, `Merge-driver wiring gate` 0.20, `Documented HTTP
status matches the status the runtime emits` 0.17, `ObjectQL double
limit gate` 0.17, `Changeset-family gate self-tests` 0.15. Every other
step in the job measured under 0.17 min.
Product ratchets and censuses stay unconditional throughout:
`query_options_erasure`, `slot_lookup`, the tenancy and tenant-audit
censuses, the engine gates.
## The four families added, and what each reads
| family | step command | read-set |
|:--|:--|:--|
| `entry_guard` | `pnpm check:entry-guard` | `scripts/**` — its own
`ROOT_DIR_WATCH_HINTS`, held against the root it walks by its own
self-test |
| `declared_population_live` | `pnpm check:declared-population-live` |
imports `discoverFamilies` + `trackedFiles`: the workflow tree, every
gate source discovery resolves, the tracked NAME set (only a name that
DISAPPEARS moves its verdict, and deletions already run everything) |
| `bare_root_worklist` | `node scripts/pm/bare-root-worklist.mjs`
(self-test only) | the same derivation, the same read-set |
| `self_test_workflow_commands` | `node
scripts/check-self-test-workflow-commands.mjs` | its declared
`scripts/**` population of `.mjs`, `.mts` and `.sh` files, plus the
workflow tree and `.github/actions` it discovers the runnable self-tests
from |
## What is weaker now, said out loud
A ratchet's skip says: no changed path is one this family reads. A
tooling self-test's skip now says something weaker: no changed path is
one the **tool's own inputs** name, while the battery behind it may
still read that path through a whole-tree census. So a defect these five
would have caught can first appear on `main` instead of on the PR that
wrote it.
Three things bound that, and none of them changed here: every doubt
still runs everything (unresolvable base, empty diff, unclassified path,
any deletion, rename or type change); `push` on main and the hourly
`schedule` run the whole battery; and widening the skip further is again
a maintainer call. The selector's header carries this paragraph beside
the read-sets, and `lint.yml` carries it on the steps themselves —
including the three steps whose prose used to say "unconditional, like
every self-test around it", which this change would otherwise have made
false.
Two previously pinned cases are given up deliberately and are now pinned
in the other direction, so the loss is legible: an ADDED file anywhere
no longer runs `pm_dispatch_gates` (the tracked-NAME sweep), and neither
does a nested `.gitignore` or a workspace `.sh` outside `scripts/` (the
#16769 case).
## Acceptance — the four dry runs, verbatim
**(a) `pull_request`, changed files = PR #19314's list.** Reproduced in
a throwaway detached worktree off `origin/main` (`c9b23cd066`), driven
with this branch's selector; the worktree was removed afterwards and
nothing under `scripts/pm/` is touched by this PR.
```text
-- (a) changed files --
A .changeset/19150-declares-collection-pipe-authorable-side.md
A packages/spec/src/compose-stacks-collection-pipe-arm.test.ts
M packages/spec/src/stack.zod.ts
Gate-family diff base: c9b23cd (merge-base of origin/main and HEAD)
Gate families: 3 run, 6 skipped (event: pull_request; changed paths: 3)
run slot_lookup reads packages/spec/src/compose-stacks-collection-pipe-arm.test.ts (A, workspace)
run query_options_erasure reads packages/spec/src/compose-stacks-collection-pipe-arm.test.ts (A, workspace)
skip entry_guard no changed path is in its read-set
run comment_mask_corpus reads packages/spec/src/compose-stacks-collection-pipe-arm.test.ts (A, workspace)
skip pm_dispatch_gates no changed path is in its read-set
skip declared_population_live no changed path is in its read-set
skip bare_root_worklist no changed path is in its read-set
skip self_test_workflow_commands no changed path is in its read-set
skip verify_lock no changed path is in its read-set
VERDICT command-exit=0
```
**(b) `pull_request`, changed file `scripts/pm/dispatch-gates.mjs`** —
same throwaway worktree:
```text
-- (b) changed files --
M scripts/pm/dispatch-gates.mjs
Gate-family diff base: c9b23cd (merge-base of origin/main and HEAD)
Gate families: 6 run, 3 skipped (event: pull_request; changed paths: 1)
skip slot_lookup no changed path is in its read-set
skip query_options_erasure no changed path is in its read-set
run entry_guard reads scripts/pm/dispatch-gates.mjs (M, scripts)
run comment_mask_corpus reads scripts/pm/dispatch-gates.mjs (M, scripts)
run pm_dispatch_gates reads scripts/pm/dispatch-gates.mjs (M, scripts)
run declared_population_live reads scripts/pm/dispatch-gates.mjs (M, scripts)
run bare_root_worklist reads scripts/pm/dispatch-gates.mjs (M, scripts)
run self_test_workflow_commands reads scripts/pm/dispatch-gates.mjs (M, scripts)
skip verify_lock no changed path is in its read-set
VERDICT command-exit=0
```
**(c) `push` — every family runs:**
```text
Gate families: 9 run, 0 skipped (event: push; changed paths: 0)
run slot_lookup event 'push' is not scoped -- the full battery runs
run query_options_erasure event 'push' is not scoped -- the full battery runs
run entry_guard event 'push' is not scoped -- the full battery runs
run comment_mask_corpus event 'push' is not scoped -- the full battery runs
run pm_dispatch_gates event 'push' is not scoped -- the full battery runs
run declared_population_live event 'push' is not scoped -- the full battery runs
run bare_root_worklist event 'push' is not scoped -- the full battery runs
run self_test_workflow_commands event 'push' is not scoped -- the full battery runs
run verify_lock event 'push' is not scoped -- the full battery runs
VERDICT command-exit=0
```
**(d) the selector's self-test** (`pnpm check:select-gate-families`),
which also pins the YAML half against the real `lint.yml`:
```text
ok the workflow scopes exactly the families the script decides (9)
ok each family gates exactly one step
ok pm_dispatch_gates gates the step running: pnpm check:pm-dispatch-gates
ok query_options_erasure gates the step running: pnpm check:query-options-erasure
ok slot_lookup gates the step running: pnpm check:slot-lookup
ok entry_guard gates the step running: pnpm check:entry-guard
ok declared_population_live gates the step running: pnpm check:declared-population-live
ok bare_root_worklist gates the step running: node scripts/pm/bare-root-worklist.mjs
ok self_test_workflow_commands gates the step running: node scripts/check-self-test-workflow-commands.mjs
ok verify_lock gates the step running: bash scripts/pm/os-verify-lock.sh
ok comment_mask_corpus gates the step running: node scripts/check-comment-mask-corpus.mjs
all 44 cases passed (228 checks)
VERDICT command-exit=0
```
The battery grew from 30 cases / 120 checks at its floor to 44 / 228,
and the floor moves with it (42 / 220). New cases: the nine ids in job
order, an ADDED path inside a read-set, a composite action, the PR
#19314 shape under both `merge_group` and `pull_request`, and one
`pin_step` per new family.
For completeness, this PR's own diff under `pull_request` — a change to
the selector and the workflow runs all five tooling families:
```text
Gate-family diff base: 2cac363 (merge-base of origin/main and HEAD)
Gate families: 5 run, 4 skipped (event: pull_request; changed paths: 3)
skip slot_lookup no changed path is in its read-set
skip query_options_erasure no changed path is in its read-set
run entry_guard reads scripts/ci/select-gate-families.selftest.sh (M, scripts)
skip comment_mask_corpus no changed path is in its read-set
run pm_dispatch_gates reads .github/workflows/lint.yml (M, workflow)
run declared_population_live reads .github/workflows/lint.yml (M, workflow)
run bare_root_worklist reads .github/workflows/lint.yml (M, workflow)
run self_test_workflow_commands reads .github/workflows/lint.yml (M, workflow)
skip verify_lock no changed path is in its read-set
VERDICT command-exit=0
```
## Expected wall clock
The five steps carry 13.62 min of the 27.4-min job (11.80 + 0.62 + 0.48
+ 0.40 + 0.32). On a diff of #19314's shape all five skip and nothing
else changes, so `Lint & Repo Gates` should read about **13.8 min** —
arithmetic on one run's step timings, on that runner with its cache
state, not a prediction of the next run. The card's bar is ≤ 16 min, and
the director seat measures the real number on the first product PR after
this lands.
## Tier S — not on the governed register
```text
$ node scripts/pm/check-governed-merges.mjs --branch claude/issue-19498-self-tests-off-pr-path
derived from `git diff --name-only --no-renames 2cac363 9a1ca2b` (three-dot): 3 path(s).
origin/main = 48c39e0, claude/issue-19498-self-tests-off-pr-path = 9a1ca2b, merge-base = 2cac363.
size: +333 / -111 over 3 file(s) (0 binary, counted 0) — `git diff --numstat --no-renames` on the same range.
governed-surface predicate: 0 of 3 path(s) hit the register (6 surfaces, repo-agnostic).
✅ NOT governed — ordinary queue landing applies to a PR with exactly this file list.
size: 444 changed line(s) (+333 / -111) ≤ 5000 — under the human-merge threshold (generated files included in the count).
VERDICT command-exit=0
```
## Gates
`skip-changeset`: nothing under `packages/**`, nothing published.
Every family `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derives for this diff was run locally, each
exit code captured before any pipe — 51 commands, 50 exit 0, including
`check:self-test-wired`, `check:self-test-workflow-commands`,
`check:step-collectors`, `check:declared-population-live`,
`check:entry-guard`, `check:watch-hint-literal`,
`check:required-contexts`, `check:workflow-status-functions`,
`check:ci-filter-parity`, `check:bash32-floor` and `check:nul-bytes`.
`pnpm check:pm-dispatch-gates` was run detached per its own header and
passed: `✓ dispatch-gates self-test: 1883 cases pass.` / `the battery
took 1056.1s on this box.`
The one command that did not return a verdict: `pnpm
check:type-check-debt` exits **3 = PREREQUISITE NOT MET** in a fresh
worktree (`--re-measure cannot run: 31 workspace dependenc(ies) of the
ledgered packages have no built type entry point on disk`), which its
own remedy text declares is neither a pass nor a finding. This diff
touches no TypeScript, and CI builds the closure before that step.
---
_Generated by [Claude
Code](https://claude.ai/code/session_012GcsUbuqFGBibkEDMRC1eE)_
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 4251c4a commit cf464f6
3 files changed
Lines changed: 333 additions & 111 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
31 | 31 | | |
32 | 32 | | |
33 | 33 | | |
34 | | - | |
35 | | - | |
36 | | - | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
37 | 40 | | |
38 | 41 | | |
39 | 42 | | |
| |||
245 | 248 | | |
246 | 249 | | |
247 | 250 | | |
248 | | - | |
| 251 | + | |
249 | 252 | | |
250 | 253 | | |
251 | 254 | | |
252 | 255 | | |
253 | 256 | | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
254 | 267 | | |
255 | 268 | | |
256 | 269 | | |
257 | 270 | | |
258 | 271 | | |
259 | | - | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
260 | 278 | | |
261 | 279 | | |
262 | 280 | | |
| |||
575 | 593 | | |
576 | 594 | | |
577 | 595 | | |
578 | | - | |
| 596 | + | |
| 597 | + | |
| 598 | + | |
| 599 | + | |
| 600 | + | |
| 601 | + | |
| 602 | + | |
| 603 | + | |
| 604 | + | |
| 605 | + | |
579 | 606 | | |
| 607 | + | |
580 | 608 | | |
581 | 609 | | |
582 | 610 | | |
| |||
911 | 939 | | |
912 | 940 | | |
913 | 941 | | |
914 | | - | |
915 | | - | |
916 | | - | |
917 | | - | |
918 | | - | |
919 | | - | |
920 | | - | |
921 | | - | |
922 | | - | |
923 | | - | |
924 | | - | |
925 | | - | |
926 | | - | |
| 942 | + | |
| 943 | + | |
| 944 | + | |
| 945 | + | |
| 946 | + | |
| 947 | + | |
| 948 | + | |
| 949 | + | |
| 950 | + | |
| 951 | + | |
| 952 | + | |
| 953 | + | |
| 954 | + | |
| 955 | + | |
| 956 | + | |
| 957 | + | |
| 958 | + | |
| 959 | + | |
| 960 | + | |
| 961 | + | |
| 962 | + | |
| 963 | + | |
927 | 964 | | |
928 | 965 | | |
929 | 966 | | |
| |||
997 | 1034 | | |
998 | 1035 | | |
999 | 1036 | | |
1000 | | - | |
| 1037 | + | |
| 1038 | + | |
| 1039 | + | |
| 1040 | + | |
| 1041 | + | |
| 1042 | + | |
| 1043 | + | |
| 1044 | + | |
| 1045 | + | |
| 1046 | + | |
1001 | 1047 | | |
| 1048 | + | |
1002 | 1049 | | |
1003 | 1050 | | |
1004 | 1051 | | |
| |||
1029 | 1076 | | |
1030 | 1077 | | |
1031 | 1078 | | |
1032 | | - | |
1033 | | - | |
1034 | | - | |
| 1079 | + | |
| 1080 | + | |
| 1081 | + | |
| 1082 | + | |
| 1083 | + | |
| 1084 | + | |
| 1085 | + | |
| 1086 | + | |
| 1087 | + | |
| 1088 | + | |
| 1089 | + | |
1035 | 1090 | | |
| 1091 | + | |
1036 | 1092 | | |
1037 | 1093 | | |
1038 | 1094 | | |
1039 | 1095 | | |
1040 | 1096 | | |
1041 | 1097 | | |
1042 | | - | |
1043 | | - | |
1044 | | - | |
1045 | | - | |
1046 | | - | |
| 1098 | + | |
| 1099 | + | |
| 1100 | + | |
| 1101 | + | |
| 1102 | + | |
| 1103 | + | |
| 1104 | + | |
1047 | 1105 | | |
1048 | 1106 | | |
1049 | 1107 | | |
| |||
1727 | 1785 | | |
1728 | 1786 | | |
1729 | 1787 | | |
1730 | | - | |
| 1788 | + | |
| 1789 | + | |
| 1790 | + | |
| 1791 | + | |
| 1792 | + | |
| 1793 | + | |
| 1794 | + | |
| 1795 | + | |
| 1796 | + | |
1731 | 1797 | | |
| 1798 | + | |
1732 | 1799 | | |
1733 | 1800 | | |
1734 | 1801 | | |
| |||
1806 | 1873 | | |
1807 | 1874 | | |
1808 | 1875 | | |
1809 | | - | |
1810 | | - | |
| 1876 | + | |
| 1877 | + | |
| 1878 | + | |
| 1879 | + | |
| 1880 | + | |
1811 | 1881 | | |
1812 | 1882 | | |
1813 | 1883 | | |
| |||
1848 | 1918 | | |
1849 | 1919 | | |
1850 | 1920 | | |
1851 | | - | |
1852 | | - | |
1853 | | - | |
| 1921 | + | |
| 1922 | + | |
| 1923 | + | |
| 1924 | + | |
1854 | 1925 | | |
1855 | 1926 | | |
1856 | 1927 | | |
| |||
0 commit comments