Skip to content

ci(lint): tooling self-tests leave the product PR path — pm_dispatch_gates and the other script self-tests run on a PR only when their own inputs change (ruling #208 on #19491) #19498

Description

@os-project-manager

Path: none | tooling | maintainer-directed (ruling #208 on #19491) — dispatched at filing, never queued

Filed and dispatched by the director seat, summon #25 (session_012GcsUbuqFGBibkEDMRC1eE), under the maintainer's verbatim direct-dispatch instruction: Ruling #208 on #19491 — maintainer 「19491 接受你的建议,并立刻派发处理相关任务。」 (2026-09-21, chat, on the director seat's 档 2 package R1–R7). Direct-dispatch channel (SKILL.md 〈多仓协调〉规则 4 「维护者直派通道」): the audit quote above is the authorization; ⛔ not a triage grading, ⛔ never queued. The full analysis and measured table are the ruling record on #19491.

Why (measured on PR #19314's head 7d67e1ee41, run 35506407130, job 「Lint & Repo Gates」)

A product PR (diff under packages/spec) paid 27.4 minutes of wall clock in 「Lint & Repo Gates」 — the longest job in its CI, above the longest test shard (19.7 min) — over 184 steps. 56 of those steps are self-tests, ratchets, corpora and censuses of the tooling, 18.6 minutes (68%); the single step 「PM dispatch-gates self-test」 (pnpm check:pm-dispatch-gates) took 11.8 minutes on a PR that changes no PM tool. The selector scripts/ci/select-gate-families.sh already scopes five families by read-set (FAMILIES='pm_dispatch_gates query_options_erasure slot_lookup verify_lock comment_mask_corpus') on pull_request and merge_group, and its own header says the full battery runs on every push to main and on nightly — so moving a self-test off the PR path loses nothing post-merge. Gate weakening is a maintainer floor; the sentence that authorizes it is the ruling quote above, and the director seat's analysis on #19491 named this item (R4) explicitly.

The change (scripts/ci/** + .github/workflows/lint.yml — neither on the governed register: Tier S)

  1. Find why pm_dispatch_gates ran on spec: declaresCollection reads a pipe's authorable side, so a preprocess-wrapped collection key cannot silently leave the merge refusal set (#19150) #19314. Reproduce with OS_GATE_EVENT_NAME=pull_request OS_GATE_PR_BASE_REF=main RUNNER_TEMP=/tmp/rt bash scripts/ci/select-gate-families.sh against that PR's changed-file list (REST pulls/19314/files); read the family's read-set (the header, lines ~54–74: 「that are neither a manifest nor a script skip it」). Narrow the read-set to the tool's own inputs — scripts/pm/**, .github/workflows/**, .github/actions/**, turbo.json, root package.json, and whatever dispatch-gates.mjs itself parses (read its population declaration) — so a diff confined to packages/** source and tests skips it. Keep the self-test's own refusal semantics (an unreadable population still refuses).
  2. Census the remaining unconditional self-test steps in lint.yml: every step whose subject is a script's own --self-test / selftest / corpus agreement (e.g. 「scripts/ shared-module self-tests」, 「PM bare-root worklist self-test」, 「Self-test workflow-command gate」, 「Comment mask agrees with a real parser over the whole corpus」, the changeset-family gate self-tests) — list them with the measured minutes from the job above. ⛔ Product ratchets and censuses stay unconditional (query_options_erasure, slot_lookup, the tenancy / tenant-audit censuses guard product code). For each self-test step, add it to the selector as a family with a read-set of its own inputs (its script, the files it reads), and the matching if: steps.gate-families.outputs.<id> != 'skip' in lint.yml; extend scripts/ci/select-gate-families.selftest.sh (it pins the workflow's if: set against FAMILIES) so the pin covers the new ids. If a step's cost is under 0.3 minutes, leave it unconditional and say so — the list, not the principle, decides.
  3. Nothing changes for push to main or nightly: prove it by running the selector with OS_GATE_EVENT_NAME=push — every family run.

Acceptance

  • Selector dry runs quoted in the PR body: (a) event pull_request, changed files = spec: declaresCollection reads a pipe's authorable side, so a preprocess-wrapped collection key cannot silently leave the merge refusal set (#19150) #19314's list → pm_dispatch_gates skip and each new family skip; (b) same event, changed file scripts/pm/dispatch-gates.mjspm_dispatch_gates run; (c) event push → every family run; (d) bash scripts/ci/select-gate-families.selftest.sh exit 0.
  • Expected: 「Lint & Repo Gates」 on a product PR ≤ 16 minutes (the director seat measures it on the first product PR after landing and records the number on this card). State the expected number and the steps moved, with their minutes, in the PR body.
  • Every family node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derives for this diff (workflow lint, self-test-wired, gate-population, ci-filter parity), exit codes captured before any pipe; skip-changeset.

Out of scope

Deleting or rewriting any self-test; touching scripts/pm/**; the patrol workflow (sibling card); the charter (sibling card).


Generated by Claude Code

Activity

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

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions