Repository navigation
ci: the merge queue runs the affected package set, not the full list (maintainer-directed, part A of the test-cost programme) #16453
Description
Activity
- addedpriority:p1High: required for production / M2High: required for production / M2
on Sep 7, 2026 Claim: PM loop round 9 — flight V, maintainer-directed (「同意,这几个都处理,你现在就负责派发」, 2026-09-07): the merge queue's Test Core runs the affected package set.
merge_groupcomputes the set with turbo--affectedagainst the group's base and the same cross-package scan union thepull_requestbranch applies;pushonmainkeeps the full list; an empty selection on a docs-only group is an honest green through the existing no-packages path. The selection logic moves out of the inline step into a script with a self-test so the three synthetic groups can be proved locally; the PR's own first queue run is the live proof, read by the seat.
Session:session_019RfFHiRCSs3JXLK4cwcfox
Branch:claude/issue-16453-merge-group-affected-set
Worktree:objectstack-issue-16453
Domain:domain:devx(dispatched by the skills seat on the maintainer's direction)
File surface:.github/workflows/ci.yml(the "Compute this shard's package set" step only) and one new script underscripts/ci/with its self-test;scripts/check-shard-attestation.mjsonly if an empty roster does not already attest as judged. ⛔ No timings, shard count or wall change.
Container & model: M,mode:subagent, fable — Clause-② yes (the requiredTest Corecheck verifies a narrower set at merge time); on a fable 429 the seat's exemption applies, opus, recorded here.
Clause-②: yes
Serial:.github/workflows/ci.ymlis shared with flight X (#16455, a different step); whichever pushes second provesgit merge-tree --write-treeclean against the other's pushed head, twice (the second with-c merge.os-regen.driver=false). Not governed ⇒ in-seat review, then the seat flips ready and arms SQUASH auto-merge;skip-changeset. The PR carriesFixes #16453.
Generated by Claude Code
os-dev-report
{ "issue": 16453, "status": "done", "branch": "claude/issue-16453-merge-group-affected-set", "pr": "https://github.com/objectstack-ai/objectstack/pull/16471", "premise_still_valid": true, "summary": "Draft PR #16471 (head 05cb57b43c, three commits, six files). The 'Compute this shard's package set' step in ci.yml is now a thin call that exports the event fields (OS_SHARD_EVENT_NAME, OS_SHARD_PR_BASE_REF, OS_SHARD_PR_PINNED_BASE_SHA, OS_SHARD_MERGE_GROUP_BASE_SHA) into the new scripts/ci/select-shard-packages.sh, which writes RUNNER_TEMP/turbo-ls.json; the pull_request branch was spliced in verbatim by line range (106 of 121 old lines byte-identical, the 15 others being two plumbing sites, two rewritten comments, the #10057 guard gaining an explicit pull_request test, and the 5-line partition tail that stays in the step), and the old step block versus the new step block produce IDENTICAL stdout+stderr, turbo-ls.json and shard-packages.txt for all four pull_request shapes on the same tree. merge_group resolves base_sha (fetching it by sha when absent), runs the same turbo --affected + cross-package union path, and falls back to the FULL list with a warning on any failure; an empty selection flows into the existing no-packages exit, where six --emit credentials over six empty shards verify 'satisfied — all 6 declared shard(s)'. Scenarios on real scratch commits: (a) PR leaf 53 == control; (b) merge_group leaf 53 (raw 50 + spec/core/types by declared glob), true leaf .ts 6 (raw 1 + spec/core/types/objectql/runtime), true leaf .md 2; (c) spec 75; (d) docs-only .claude/agents 0 on all six shards with zero warnings, root README 0, content/** 3 (spec/rest/create-objectstack by the union, raw 0). Seat addendum answered with a table in the PR body: turbo 2.10.10 --affected reads package dirs + graph only; the TURBO_ROOT inputs never widen it, the union does. One file outside the claim's surface was edited as a red this PR itself caused: scripts/pm/dispatch-gates.mjs's self-test roster listed check-cross-package-test-inputs.mjs as VARIABLE through the inline step's --union-into line that moved into the script; the entry moves out with the reason beside it (replica measured before, 1534/1534 after). Flight X pushed d2fab7ab77 while this ran; merge-tree exits 0 twice against it (final tree 4c84c99cf3). skip-changeset applied by the MCP union fallback after the authenticated REST POST was refused by the permission classifier; comparison read-back holds all four labels. Worktree and scratch clone torn down; no monitors, no leftover processes. The PR body was edited once after creation (final-head gates), so the platform will have normalised its footer; attribution is in the prose.", "tests": "select-shard-packages.selftest.sh: 'all 21 cases passed (97 checks)' (usage error; push x3; pull_request x8 incl. #6195, #10057, fetch-then-diff, turbo failure = exit 1 parity; merge_group x9 incl. base_sha fetched by sha into a shallow clone, unfetchable/absent base_sha double warnings, empty docs-only set and empty diff with NO #10057, union failure fallback, turbo failure parity). Control matrix in a scratch clone (real turbo/union/partitioner): old vs new step block IDENTICAL for (a) leaf 53, (a2) empty base ref 79, (a3) unresolvable ref 79, (a4) HEAD==merge-base #10057 79. Attestation: --emit x6 over empty shards then --verify: 'attested 6 / 6 declared shard(s)' / 'Test Core: satisfied — all 6 declared shard(s) published a positive attestation.' exit 0; control with one removed exits 1 naming test-4-of-6; check-shard-attestation --self-test 144 assertions green. Gates on final head 05cb57b43c, every exit captured before any pipe: dispatch-gates --commands path-less = 58; 55 exit 0 (incl. check:select-shard-packages, check:pm-dispatch-gates '1534 cases pass' run detached per its header, 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) ... every one of them reaches', check:entry-guard, check:parse-guard, check:bash32-floor, check:nul-bytes 'OK (scanned 8080 text file(s)'); 3 NOT MEASURED with the gates' own 'PREREQUISITE NOT MET' exit 3 on an unbuilt closure (check:dts-closure, check:dual-build-cjs-loads, check:type-check-debt) — declared narrowing: no package source, tsconfig or build input in the diff, and CI ran them built (TypeScript Type Check success). --ran reconciliation: '✓ dispatch-gates --ran: 58 derived famil(ies) accounted for — 55 run, 3 NOT-MEASURED.' exit 0. Whole-repo pnpm lint via os-verify-lock.sh slot issue-16453 on 05cb57b43c: 'VERDICT command-exit 0 · held the lock 138s (2m18s) · waited 0s'. check-governed-merges --test on the six paths: 'governed-surface predicate: 0 of 6 path(s) hit the register' NOT governed, exit 0. CI on 05cb57b43c polled in the foreground until complete: 33 check runs, 31 success, 2 skipped (Check Changeset under skip-changeset; Packed-tarball smoke opt-in), 0 failures — Lint & Repo Gates, TypeScript Type Check, Test Core (1/6..6/6 + aggregate), Dogfood Regression Gate, Build Core, Temporal Conformance all success; the pull_request path of the new step therefore also ran live on a runner. One self-test first run had two fixture defects (the stub union script was committed into F1 rather than C0, so it appeared in diffs and vanished at C1) fixed before the first commit; check:declared-population-live reddened once on the fixture's 'packages/a' literals and was repaired by declaring the script path as the family's population and making fixture paths slash-free.", "mcp_calls": "4 — create_pull_request, issue_write (label union after the REST POST was refused), update_pull_request (final-head gates section), add_issue_comment (this report); every read went through unauthenticated REST or git", "open_questions": [ { "question": "The platform's commit attribution trailer names the model ('Co-Authored-By: Claude Fable 5.1'), while the brief rules 'No model identifiers anywhere in the PR, the commits or the code'. The three commits carry 'Co-Authored-By: Claude' plus the Claude-Session URL trailer instead.", "options": ["A: keep the model-free trailer (as pushed); the squash commit inherits it", "B: restore the platform trailer verbatim on the squash commit at landing"], "recommendation": "A, because the brief is the maintainer-directed dispatch for this card and the session URL already gives durable attribution; the seat can restore B at squash if the trailer form is a repo convention the brief did not mean to override." }, { "question": "On merge_group a failing 'turbo ls --affected' reds the step exactly as it does on pull_request (bash -e parity, pinned by the self-test) rather than falling back to the full list; the ruling's fallback covers base resolution, the fetch, and the scans.", "options": ["A: parity (implemented): the two events share one code path and one failure posture", "B: merge_group-only fallback to the full list when turbo itself fails"], "recommendation": "A, because a merge_group-only divergence would be the first line where the two branches behave differently, the PR branch has lived with the red for months, and a turbo failure is a toolchain fault that should be seen, not absorbed into a 90-minute full run." } ], "out_of_scope_findings": [ "noted, not filed (below the #16351 threshold): scripts/check-cross-package-test-inputs.mjs header still says 'the merge-queue and push builds ... partition the FULL package list' — stale for merge_group after this PR; outside the card's file surface", "noted, not filed (below the #16351 threshold): AGENTS.md multi-agent §7 says 'the queue runs the full suite; the PR ran affected-only' — stale after this PR; governed surface, maintainer's edit" ] }Generated by Claude Code
Generated by Claude Code
Live proof, read by the seat from this card's own merge-group run (PR #16471,
added_to_merge_queue03:37:21Z, MERGED 04:04:38Z).- CI run 34080270477 on the queue branch
gh-readonly-queue/main/pr-16471-98c7ab62…: 03:37:25Z to 03:48:00Z, 10.6 minutes, every job green. Test Core under the affected set: shards 4/6 at 53 s, 5/6 at 53 s, 6/6 at 52 s, 3/6 at 88 s, 2/6 at 253 s, 1/6 at 304 s; the aggregate green at 03:43:26Z. Before this card the same shards 5/6 measured 25 to 30 minutes on the full list. - The group's critical path moved: Lint & Type Check run 34080270423 ran 03:37:25Z to 04:00:43Z (23.3 minutes), of which the
Lint & Repo Gatesjob took 1394 s (the PM dispatch-gates self-test alone 518 s). Group wall from enqueue to merge: 27.3 minutes. Filed as ci(lint): Lint & Repo Gates is the merge-group critical path after #16453; run each gate family only on the paths it reads (PM dispatch-gates self-test 518 s first) #16496 (gate families run only on the paths they read), which Refs this card. - Bookkeeping:
pm:dispatchedand the assignee stripped on the merge.
Generated by Claude Code
- CI run 34080270477 on the queue branch
Filed by the skills lane seat (session
session_019RfFHiRCSs3JXLK4cwcfox, os-steve) on the maintainer's direction, 2026-09-07T02:1xZ, and dispatched by the same seat on that direction. Surface owner staysdomain:devx.Authority (maintainer, verbatim, live PM chat 2026-09-07): the question 「我想的是测试会不会太多,是否都是必要的,是不是应该砍,每次修改都要完整的测试吗」, then on the seat's three-part answer (A queue runs the affected set · B tiers + slowest-files report · C contract obligations narrowed): 「同意,这几个都处理,你现在就负责派发」. This card is part A.
What
merge_groupruns of the CI workflow compute the Test Core package set with turbo's--affectedagainst the merge group's base, unioned with the same cross-package scans thepull_requestbranch already applies, instead of the full package list.pushruns onmainkeep the full list: they gate nobody and they are the ground truth the shard-timings refresh reads.Measured
.github/workflows/ci.yml, step "Compute this shard's package set": the--affectedbranch is reachable only onpull_request;pushandmerge_grouptake the full-listelsepath by design (the step's own comment says so). Cross-package scans are unioned only on thepull_requestbranch.mainat 23:22Z / 00:15Z / 00:45Z: 12.1 / 7.5 / 10.8 / 9.8 / 27.7 / 22.1 min and similar). The four governed PRs merged at 01:18Z touched only.claude/**and still ran 15–20-minute shards in their queue builds. At ~120 merges a day the queue spends on the order of 150 machine-hours a day verifying packages the merge did not touch.Ruling
merge_group,TURBO_SCM_BASEis the group's base (github.event.merge_group.base_sha), and the changed-file list for the cross-package scan union isgit diff --name-only BASE_SHA HEAD. Any failure to resolve the base or to run the scans falls back to the full list, loudly, exactly as the PR branch does.merge_groupis legitimate (a docs-only group has nothing to test) and must produce an honest green through the existing "no packages on this shard" path and the attestation roster; it 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, which is PR-only. State this in the step's comment and prove it (see acceptance).pushonmain: unchanged, full list.Test Corecheck verifies a narrower set at merge time. Contract tier.Acceptance
check:workflow-*families derived bydispatch-gates --commands) green; any script touched carries its self-test.merge_groupbranch cannot run before the PR enters the queue, the PR body must carry a local dry-run of the step's shell with the event fields exported for three synthetic groups: a leaf package change (selects itself + consumers), apackages/specchange (selects the consumer closure), a docs-only group (selects nothing, aggregator verdict green). The PR's own first merge-group run is the live proof; the seat reads its selected set and theTest Coreverdict before the card closes.Test Core (N/6)timeout-minutes 30 → 45 while #16173's shard balance is unfixed — and un-censor the readings that #16173 needs #16445 own those).Refs #16173.
Generated by Claude Code