ci: the merge queue's Test Core runs the affected package set; selection moves into a script with a self-test - #16471
Conversation
…uns the affected set The "Compute this shard's package set" step becomes a thin call that exports the event's fields; scripts/ci/select-shard-packages.sh reads plain environment variables, so every branch of every event runs locally and under its self-test (scripts/ci/select-shard-packages.selftest.sh, wired as check:select-shard-packages). pull_request: moved verbatim -- the same comments, warnings and fallbacks. merge_group: the affected set against the group's base_sha, unioned with the same cross-package scans; every failure to resolve or fetch the base, or to run the scans, falls back to the FULL list with a warning naming the reason. An empty selection on a docs-only group flows into the existing "No packages on this shard" path and attests as judged. push on main: unchanged, the full list. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox
…dead one check:declared-population-live read the fixture's fake package paths as a declared population reaching nothing. The self-test now spells the script it drives as a quoted repo-relative literal (its real population, so a card touching the script schedules this family) and keeps every fixture path slash-free. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox
…d into the selection script check-cross-package-test-inputs.mjs classified as value-bearing only through ci.yml's inline package-selection step; that shell now lives in scripts/ci/select-shard-packages.sh, which no workflow-text scan reaches, and the script's only workflow invocation is the all-literal pnpm alias. The typed roster rotted the way its own comment predicts; the entry moves out with the reason recorded beside it. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox
Verdict: ACCEPT (contract tier — Clause-② yes, maintainer-directed)Implemented-by: os-dev flight V (fable), branch What was checked
Rulings
LandingNot governed, ordinary queue landing: the seat flips the draft to ready and arms auto-merge (SQUASH). The PR's own merge-group run is the live proof: the seat reads its selected set (this PR touches Generated by Claude Code |
Fixes #16453
The merge queue's
Test Coreruns the affected package set instead of the full list. The "Compute this shard's package set" step inci.ymlbecomes a thin call into a new script that reads plain environment variables, so every branch of every event runs locally and under a self-test — which is how themerge_groupbranch, which cannot run before a PR is already in the queue, is proved here before it lands.pushonmainis unchanged: the full list.Maintainer-directed (part A of the test-cost programme; the authority is quoted on the card). Not a governed surface:
check-governed-merges --teston this PR's six paths printedgoverned-surface predicate: 0 of 6 path(s) hit the register (5 surfaces, repo-agnostic). NOT governed — ordinary queue landing applies to a PR with exactly this file list.(exit 0).What changed
scripts/ci/select-shard-packages.sh(new)OS_SHARD_EVENT_NAME,OS_SHARD_PR_BASE_REF,OS_SHARD_PR_PINNED_BASE_SHA,OS_SHARD_MERGE_GROUP_BASE_SHA,RUNNER_TEMP),$RUNNER_TEMP/turbo-ls.jsonout; exit 0 on every decided outcome, 2 on a usage error, turbo's own status whenturbo lsfails (thebash -ebehaviour the inline step had)scripts/ci/select-shard-packages.selftest.sh(new)pnpm/turbopair on PATH that records every call, a stub union script that records its argv; pins whichturbo lscalls were made and with whatTURBO_SCM_BASE, the union's changed-file list, every::warning::fallback text, and the ABSENCE of the ones that must not fire.github/workflows/ci.ymlpartition-test-shards.mjspins its spelling); the header comment states the per-event posture and the empty-set rule.github/workflows/lint.ymlTest Core package selection self-test, runspnpm check:select-shard-packages(wiring the self-test into CI is whatcheck:self-test-wiredexists for)package.jsoncheck:select-shard-packagesscripts/pm/dispatch-gates.mjsthe card's named specimens all classify as VARIABLE from the workflow textlistedscripts/check-cross-package-test-inputs.mjs, whose only value-bearing invocation was the inline step's--union-into … --changed …line that this PR moved into the script; with it gone from the workflow textcheck:pm-dispatch-gatesfailed1 of 1534on this branch (the rot its own block comment predicts for a typed list). Measured before the edit with the tool's exportedextractCheckInvocationsover the live workflows: the other five specimens still classify VARIABLE,check-cross-package-test-inputs.mjsno longer does anywhere. After the edit:✓ dispatch-gates self-test: 1534 cases pass.File-surface increment against the claim:
lint.ymlandpackage.jsoncarry the self-test wiring only (one step, one script line);scripts/pm/dispatch-gates.mjsis the one-entry repair above, a red this PR itself causes. Flight X (#16455) edits a differentci.ymlstep and none of these files;git merge-tree --write-tree HEAD d2fab7ab77against its pushed head exits 0 both plain and with-c merge.os-regen.driver=false(final head: both write tree4c84c99cf3).Per event
pull_request— moved VERBATIM: the affected set against merge-base(origin/BASE_REF, HEAD), unioned with the cross-package scans; fallbacks ci.yml 的TURBO_SCM_BASE吃同一个冻结 base.sha —— turbo --affected 在 merge ref 上把 main 漂移算成本 PR 改动(方向保守:多跑,不会少跑) #6195 and [finding] Nothing distinguishes "zero packages affected" from "the affected-set computation silently produced nothing" — an emptychanged-files.txtis never checked #10057 with their exact warning texts.merge_group—SCM_BASEisgithub.event.merge_group.base_sha; if the checkout cannot resolve it the script fetches it by sha (mirroring the PR branch's fetch oforigin/BASE_REF), thenTURBO_SCM_BASE=base_sha pnpm exec turbo ls --affected --output=jsonand the union overgit diff --name-only base_sha HEAD, through the same code path the PR branch runs. Any failure to resolve the base, to fetch it, or to run the scans falls back to the FULL list with a::warning::naming the reason. An EMPTY selection is legitimate (a docs-only group) and is not the [finding] Nothing distinguishes "zero packages affected" from "the affected-set computation silently produced nothing" — an emptychanged-files.txtis never checked #10057 case: that guard is now an explicitpull_requesttest, and the comment beside it says why the two cannot be told apart anywhere else.push— unchanged, the full list (the step comment says so in one line).Byte-for-byte equivalence of the
pull_requestbranchThe old
run:block (121 lines at6eba38f5a3, extracted and dedented; the round-trip re-indents to the identical bytes) was spliced into the script by line range, never retyped.diff -b old-inline new-script-bodyremoves exactly 15 lines and adds 45; the 15 removed are, in full:Every other old line survives verbatim: 106 of 121 are present byte-for-byte in the script (indentation aside — the six drift/echo lines sit one level deeper under the new
pull_requestguard, with amerge_groupelseprintingAffected-set diff base: SHA (the merge group's base_sha)). The added lines are the merge_group resolution block, the two event guards, the merge_group fallback warning, and the plumbing header.Behavioural proof — the OLD step's
run:block (with${{ github.event_name }}=pull_request,${{ matrix.shard }}= 1) and the NEW step'srun:block were both executed underbash -ein a scratch clone against the same tree and the same event variables, real turbo, real union script, real partitioner:86f09bcdd964e1ac2440598fnopeAnd live: this PR's own
pull_requestrun of the new step on a runner —Test Core (1/6)…(6/6)and the aggregateTest Coreallsuccesson05cb57b43c.The four scenario runs (real commits in a scratch clone,
git diffagainst the synthetic base T =e6fe28e94e; never pushed)rawisturbo ls --affectedalone at the same HEAD;selectedis what the step wrote after the union.pull_request, leaf (packages/drivers/driver-sql/src/index.ts)merge_group, same leaf changemerge_group, TRUE leaf,.ts(packages/plugins/knowledge-ragflow/src/index.ts; zero dependents)merge_group, TRUE leaf,.mdonly (packages/plugins/knowledge-ragflow/CHANGELOG.md)merge_group,packages/spec/src/index.tsmerge_group, docs-only (.claude/agents/os-dev.md, the 01:18Z shape)merge_group, docs-only (rootREADME.md)merge_group, docs-only undercontent/**(content/docs/concepts/north-star.mdx)(d): no warning, exit 0,
shard-packages.txtis 0 bytes on every shard, so each shard takes the existingNo packages on this shard — nothing to testexit. Every merge_group run printed zero::warning::lines. The (d) reading is the docs-only group the card measured at 15–20 minutes of shard time; it now costs six ~1-minute shards.Seat addendum — does
turbo ls --affectedread the$TURBO_ROOT$inputs?No. Measured on turbo 2.10.10:
--affectedreads package directories plus the dependency graph only; the$TURBO_ROOT$inputs on the heavy test tasks feed the task hash and never widen the affected verdict. What pulls those packages into the queue run is the cross-package union (check-cross-package-test-inputs.mjs --union-into), by declared glob.$TURBO_ROOT$inputs that match itturbo ls --affected.ts(b2)spec#test,core#test,types#test,runtime#test,objectql#testall listpackages/**/*.tspackages/**/*.ts).md(b3)spec#test(packages/**/*.md)packages/**/*.md)content/**(d3)spec#test,rest#test,create-objectstack#testlistcontent/**content/**).claude/agents/*.md/ rootREADME.md(d, d2)So the floor for a leaf
.tsmerge is six packages — the leaf plus the five whose declared radius is every.tsunderpackages/; spec's suite (~500s) dominates that shard. That radius is a declaration inscripts/cross-package-test-inputs.mjs, not a turbo.json input, and nothing here moves it (no turbo.json input was changed).The attestation reading (empty roster on every shard)
check-shard-attestation.mjs --emitcarries no package field at all (payload: attestation, job, shard, total, run_id, run_attempt, sha, workflow_job, attested_at); the emit step has noif:, and on an empty shard the test step exits 0 before creating a log while the completeness guard exits 0 onNo test log. Dry run: six--emitcalls over six emptyshard-packages.txtfiles, then--verify --gate 'Test Core' --dir ... --filter-result success --leg test/6:success:Control, one credential removed:
- test-4-of-6 MISSING/::error::Test Core: 1 of 6 declared shard(s) of test published no positive attestation (test-4-of-6)(exit 1). Its--self-test:144 assertionsgreen. Nothing in that script needed to change.Gates (final head
05cb57b43c, every exit captured before any pipe)node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(path-less, derived from the merge base): 58 commands (the threescripts/pm/families joined at the last commit:check-declaration-mirrors, its self-test,check:ratchet-remedy-authority, all exit 0). 55 ran to exit 0, includingcheck:select-shard-packages(all 21 cases passed (97 checks)),check:pm-dispatch-gates(✓ dispatch-gates self-test: 1534 cases pass., run detached as its header requires),check:shard-attestation,check:cross-package-test-inputs,check:workflow-status-functions,check:required-contexts,check:aggregator-roster,check:ci-filter-parity,check:self-test-wired,check:self-test-workflow-commands,check:step-collectors,check:declared-population-live(224 of 270 famil(ies) declare a path population, and every one of them reaches this tree's 8087 tracked file(s)),check:entry-guard,check:parse-guard,check:scripts-symbol-anchors,check:bash32-floor(29 tracked shell file(s) ... name no bash 4+ construct),check:nul-bytes(OK (scanned 8080 text file(s) ...)).PREREQUISITE NOT METexit 3 on an unbuilt closure:check:dts-closure,check:dual-build-cjs-loads,check:type-check-debt. Declared narrowing: this diff touches no package source, tsconfig or build input (workflows, two shell scripts, one root script line, one self-test roster entry), so none of the three properties can move; CI ran them on a built closure (Type Check · debt ledger,TypeScript Type Check: success).--ranreconciliation:✓ dispatch-gates --ran: 58 derived famil(ies) accounted for — 55 run, 3 NOT-MEASURED.(exit 0; the three carry their stated reason).pnpm lintthroughscripts/pm/os-verify-lock.sh(slotissue-16453):VERDICT command-exit 0 · held the lock 138s (2m18s) · waited 0s.check-governed-merges --teston the six paths: exit 0, NOT governed (quoted above).05cb57b43c: 33 check runs completed, 31success, 2skipped(Check Changesetunderskip-changeset;Packed-tarball smoke (opt-in)), 0 failures —Lint & Repo Gates,TypeScript Type Check,Test Core,Dogfood Regression Gate,Build Core,Temporal Conformance (live PG + MySQL)allsuccess.skip-changeset: nothing here publishes from any package.Noted, not filed (below the #16351 threshold): two prose readings are now stale for
merge_group— the header ofscripts/check-cross-package-test-inputs.mjs("the merge-queue and push builds ... partition the FULL package list") and AGENTS.md multi-agent §7 ("the queue runs the full suite; the PR ran affected-only"); the second is a governed surface and both are outside this card's file surface.Authored in Claude Code session
session_019RfFHiRCSs3JXLK4cwcfox(os-steve).🤖 Generated with Claude Code
https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox