Skip to content

ci(dogfood): the three Dogfood Regression Gate shards run on merge_group - #30639

Merged
os-zhuang merged 4 commits into
mainfrom
claude/issue-30461-dogfood-merge-group
Nov 5, 2026
Merged

os-zhuang merged 4 commits into
mainfrom
claude/issue-30461-dogfood-merge-group

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Requested by 田騏 · project thread

Part of #30461 (the card stays open: the follow-up ruling 6669982756 owes a 24-hour reading of the queue builds after this lands, then the required-set step)
Ruling-ref: 6663883327
Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:合并队列(merge_group)现在也跑三个 Dogfood Regression Gate 分片;PR 上照旧不跑,Dogfood Verify CLI、Temporal Conformance、UI Published Artifacts 仍只在 main 的全量里跑。顺带把说明文字和两个读 if: 的仓内工具改到与新行为一致。
  • 为什么改:按裁决 B 执行。10-22 以来有 9 张 dogfood 引起的 main 红,都是合进去之后才发现;现在 dogfood 红会出现在该条目的队列构建上,比等 main 推送后才发现更早(目前只是提示,不拦合并)。
  • 风险与回滚:每个队列条目多 3 个 runner 作业(push 上实测每片约 11 分钟,与 Test Core 并行,Test Core 在队列里中位约 21 分钟才收尾)。注意:Dogfood Regression Gate 自 skills(ci): two-speed CI — fast set on PR and merge-queue runs, full battery hourly on main and before a release, main-red stops the lane, four required contexts plus the guard, timing gates report-only, test budgets not deletions #22998 起不在必需检查里,所以队列里 dogfood 红不会自动把条目踢出,只是让红提前可见;是否加回必需集合已由后续裁定 6669982756(A,分两步)定下:本 PR 先落地,不动规则集;落地后 24 小时读队列构建的 dogfood 红与抖动,读数干净再由你把它加进规则集(车道先合一行 REQUIRED_CONTEXTS)。回滚只需 revert 本 PR。
  • 先看:① .github/workflows/ci.yml 的 dogfood 作业 if:(只删了 merge_group 一项);② 同文件 THE TWO SPEEDS 注释块;③ scripts/pm/dispatch-gates.mjs 的 jobIfReading。
  • 两分钟验证:node scripts/pm/check-expected-skips.mjs --roster | grep Dogfood,应看到分片一行只排除 pull_request,Dogfood Verify CLI 一行仍排除 pull_request|merge_group。
  • 需要你做的:审批本 PR(.github/workflows/** 与 AGENTS.md 属 Tier H),席位随后落地。

What changes

  • .github/workflows/ci.yml, the dogfood job: its if: drops && github.event_name != 'merge_group' and nothing else. !cancelled(), the core filter term and the pull_request exclusion stay. dogfood-verify, temporal-conformance and ui-published-artifacts keep both exclusions (the ruling's second line). No affected-only filter: on merge_group the core output is 'true' by its existing guard, so the shards run on every queue entry (letter C is not ruled).
  • What the job needs: on the queue: filter only. It runs on every event, and its core output is 'true' on merge_group, so the shards cannot skip silently there. No other needed job is denied on merge_group, so nothing else is widened.
  • Prose brought in line with the new split: THE TWO SPEEDS block, the core output's reader list, the dogfood job and dogfood-gate comments (ci.yml); release.yml's full-battery comment; upgrade-harness-optin.yml's gap paragraph; scripts/ci/scheduled-full-run.mjs HALF 3; the check-required-contexts.mjs tombstone; AGENTS.md's Two speeds line and its detail in docs/agents/multi-agent-discipline.md.
  • Two repo gates read job if: spellings, and the new one (pull_request excluded alone) changed what they said:
    • scripts/pm/check-expected-skips.mjs: the shards stay in the derived roster (a PR head still skips them), but their reason no longer says "full battery only". A pr-event term that excludes only pull_request now reads "skips on every PR head and is answered by the merge-queue build and main's push and scheduled runs". The fixture tree models the live shape, with three new self-test cases.
    • scripts/pm/dispatch-gates.mjs: jobIfReading refused a lone != 'pull_request' clause, so the dogfood shards' path population (the dogfood leads --commands exists to give) would have dropped. It now strips that clause the same way as the full-battery one (exact spelling, last conjunct only) and returns notOnPullRequest beside fullBatteryOnly. The row clause says "does NOT run on this PR … its first run on this change is the merge-queue build". merge_group excluded alone is still refused. Self-test: the near-miss case is re-pointed, and new cases cover the clause, its position, the doubled spelling, both flags, and a LIVE pin on the shards' row with an in-place ablation. The full-battery LIVE pin stays on Dogfood Verify CLI, as the test's own note asks.
  • Read and left alone, from the code: check-shard-attestation.mjs reads no event in any if:. Its verdict already judges a full dogfood matrix beside a skipped CLI leg green (self-test case "each leg is judged on its own roster"), so a queue build counts 3 credentials plus 0 expected. check-required-contexts.mjs pins only registry rows, and Dogfood Regression Gate is not one (prose only, above). check-ci-filter-parity.mjs reads filter globs, not events.

Queue wall time (before)

Window: the 24 hours before the read, runs created at or after 2026-11-04T18:41:25Z (read 2026-11-05T18:41Z, at origin/main 6701c08a44).

gh api -X GET "repos/objectstack-ai/objectstack/actions/workflows/ci.yml/runs?event=merge_group&created=%3E%3D2026-11-04T18:41:25Z&per_page=100&page=N"   # N = 1..4
duration = updated_at - run_started_at, completed runs, run_attempt 1 throughout
ci.yml on merge_group, last 24 h n median p90 max
completed, every conclusion 303 20.5 min 26.0 min 32.8 min
success 285 20.4 min 25.8 min 32.8 min
failure 18 21.9 min 26.7 min 27.8 min

(306 runs listed, 3 still in_progress, none cancelled.)

What finishes last in a queue run, from the jobs of the 60 newest success runs (repos/objectstack-ai/objectstack/actions/runs/RUN_ID/jobs?per_page=100&filter=latest, offset = job completed_at minus the run's first job created_at): Test Core closes 38 of 60 runs at a median of 20.8 min (p90 25.9), then Test UI (11) and Lint UI (10). Build Core closes at a median of 8.6 min.

Dogfood shards for comparison, from main's push runs in the same window (repos/objectstack-ai/objectstack/actions/workflows/ci.yml/runs?event=push&branch=main&created=%3E%3D2026-11-04T18:41:25Z, 135 runs; jobs read for 128 of the 130 completed ones, 2 reads answered 502). success jobs only, completed_at - started_at:

job (push, main) n median p90 max
Dogfood Regression Gate (n/3) shard 282 11.4 min 12.3 min 14.2 min
Dogfood Verify CLI (stays off the queue) 99 5.5 min 8.3 min 8.6 min
Test Core (n/N) shard, for scale 790 12.6 min 18.4 min 26.0 min

A shard (~11.4 min of execution, started after filter) fits inside the queue run's ~20.8 min Test Core close. So queue wall time should move little unless runner pickup slows under the +3 jobs per entry. That is the reading the seat's 24 h "after" measurement checks. It is not assumed here. The card's "~5 min per shard" figure is today's ~11.4 min on push.

Tests

All readings at head 4bb48774cf (this branch with origin/main 63de248a1c merged once), after pnpm install --frozen-lockfile and pnpm build (lock VERDICT command-exit 0).

  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derives 80 commands for this diff (10 paths). Each was run, and its exit code was recorded before any pipe. All 80 exit 0. --ran reconciles: "80 derived, 80 run, 0 NOT-MEASURED, 0 UNRUN" ("a DERIVED zero — all 80 recorded an exit code and none of them is 3").
  • On the pre-merge head 57fd195a35, four dist-reading gates first answered exit 3 (PREREQUISITE NOT MET, no build): check:doc-formula-expressions, check:dts-closure, check:dual-build-cjs-loads and check:lean-entry-closure. After the build all four exit 0. check:pm-skill-ratchet went red on an AGENTS.md line of 123 bytes against the 120-byte budget. Commit c029b25517 rewrapped it (no line added, ceiling 1050 held), and the gate exits 0. The final-head run above includes both.
  • Edited tools' own suites, run beyond the derived list:
    • node scripts/pm/dispatch-gates.mjs --self-test (both tiers): "2240 cases pass" (fast 1951 + slow 289).
    • pnpm check:pm-expected-skips: 209 cases pass, with the new reason and fixture cases.
    • pnpm check:required-contexts, pnpm check:shard-attestation and node scripts/ci/scheduled-full-run.mjs --self-test are in the 80 above.
  • Full dogfood battery: not owed. There is no ! in the title and no (narrowing) arm. The diff changes no test or runtime code, so no ablation either: a workflow, scripts and docs are out of its scope.
  • CI check-runs on this PR: read at hand-off; the seat cites them.

Acceptance notes

  • ⚠ The ruling's "ejects the PR from the queue" holds only when the context is required. Dogfood Regression Gate has been outside the ruleset's required set since skills(ci): two-speed CI — fast set on PR and merge-queue runs, full battery hourly on main and before a release, main-red stops the lane, four required contexts plus the guard, timing gates report-only, test budgets not deletions #22998, and AGENTS.md §7 lists the ten contexts the queue enforces. After this PR a dogfood red shows on the queue build and reds that run, but GitHub's merge queue holds an entry only on required checks, so the entry still merges and main's push run goes red as before. Whether the context rejoins the required set (a ruleset change, plus a REQUIRED_CONTEXTS row in check:required-contexts) was not ruled when this PR opened. The follow-up ruling 6669982756 (maintainer 「同意」, A in two steps) settles it: this PR lands without a ruleset change; after a clean 24-hour reading of the queue builds, a REQUIRED_CONTEXTS row lands first and the maintainer adds the context to the ruleset after it.
  • AGENTS.md and docs/agents/multi-agent-discipline.md were outside the claim's file surface. They are edited because their Two speeds line restates the split this PR changes ("PR and queue runs carry the fast set"). The seat's delta corrected the detail file's stale "10 minutes of wall per queue entry" to the measured median.
  • Carrier: none.

Verification

  • node scripts/pm/check-expected-skips.mjs --roster at this head: Dogfood Regression Gate (${{ matrix.shard }}/3) … gate: job-gate filter core + pr-event pull_request and Dogfood Verify CLI … pr-event pull_request|merge_group.
  • node scripts/pm/dispatch-gates.mjs --commands at this head renders the shards' row as "⛔ does NOT run on this PR: its if: excludes the pull_request event, so its first run on this change is the merge-queue build, after the PR is enqueued", and Dogfood Verify CLI's with the unchanged full-battery sentence.
  • The live proof is this PR's own queue entry, and every entry after it: three Dogfood Regression Gate (n/3) jobs on a merge_group run, and Dogfood Regression Gate counting 3 credentials beside a skipped Dogfood Verify CLI.

Session: session_017ihL2ebbZkr2QAEG1x7UC8 · seat domain:devx#2.

🤖 Generated with Claude Code

Only merge_group leaves the dogfood job's event deny-list; pull_request
keeps the fast set, and dogfood-verify, temporal-conformance and
ui-published-artifacts stay full-battery only. THE TWO SPEEDS and every
comment that restated the old split now say what runs where.

The two gates that read job if: spellings learn the queue-and-full
clause (pull_request excluded alone): check-expected-skips no longer
calls such a job "full battery only", and dispatch-gates keeps the
dogfood path population and tells the dev the PR skips the shards and
the merge-queue build runs them first.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ihL2ebbZkr2QAEG1x7UC8
…-time figure is the measured median

Contract-review should-fixes on the dogfood merge_group change: THE TWO
SPEEDS no longer says a dogfood red shows "before the merge" (the context is
outside the required set, so the queue may merge first); the
check-required-contexts tombstone names the follow-up ruling's two-step path
back into the required set; the discipline doc's "10 minutes of wall per queue
entry" becomes the measured ~20 minute median.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ihL2ebbZkr2QAEG1x7UC8
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:devx#2 · marchtian · session_017ihL2ebbZkr2QAEG1x7UC8

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 263527d3d538f2ef4d39cb94ecfd57f49c4d4857
Local-runs: none
Scope: AGENTS.md, .github/workflows/ci.yml, .github/workflows/release.yml, .github/workflows/upgrade-harness-optin.yml + scripts/pm/dispatch-gates.mjs, scripts/pm/dispatch-gates.self-test.mjs, scripts/pm/check-expected-skips.mjs, scripts/check-required-contexts.mjs, scripts/ci/scheduled-full-run.mjs, docs/agents/multi-agent-discipline.md, scripts/check-shard-attestation.mjs (judge, the dogfood fixtures, the cancel-cause prefixes), scripts/check-ci-filter-parity.mjs, .github/workflows/lint.yml (job-to-gate map), scripts/check-partof-closing-keyword.mjs (RULE 1 and RULE 3, for the Part of line), precedent #25867 (an issue; landed by #25881, 487056b940), precedent #26460 (Test UI, cited by the follow-up ruling), the live ruleset for main

Round 2 of this review. Round 1 gave PASS on 4bb48774cf with three wording should-fixes S1–S3 and one escalation (the required-set question). Since then the maintainer ruled that question (6669982756, A in two steps), the seat pushed one commit 263527d3d5, and the PR body changed Fixes #30461 to Part of #30461. This record supersedes round 1 for this head; the round-1 judgments are restated where they still hold and changed where they do not.

Inputs read: card #30461 body and its five comments (ruling 6663883327, seat note 6664079672, claim 6666075446, dev report 6667658973, follow-up ruling 6669982756); PR #30639 body as of 2026-11-05T21:45:50Z and its file list; the net diff origin/main...263527d3d5 (merge-base 63de248a1c, 10 files, +206/-77) and the round-2 delta 4bb48774cf..263527d3d5 (3 files, +13/-8); the head's check-runs; rules/branches/main. The tree was judged, not the body's account of it.

The round-2 delta (263527d3d5, "docs(ci): the queue-build dogfood red is advisory, and the stale wall-time figure is the measured median") touches three files and no executable text: the ci.yml hunk sits inside the # THE TWO SPEEDS block (hunk 122–130; the block opens at 116 and every line of it starts with #), the check-required-contexts.mjs hunk inside the /** … */ tombstone that closes at line 395 above export const REQUIRED_CONTEXTS at 396 (hunk 384–397; every changed line starts with *), and the discipline-doc hunk is prose. No if:, no filter output, no regex, no fixture and no self-test changed, so every ① derived judgment of round 1 is a judgment of this head as well; they are restated below unchanged.

Check-runs on the head (read 2026-11-05T21:53:37Z, eight minutes after the push at 21:45:44Z; the set is NOT yet complete): 59 check-runs listed — 26 success, 8 skipped, 0 failure/cancelled/timed_out, 25 in_progress. Every gate family this review derives from had completed and is success: Lint · CI, release & engine gates (check:shard-attestation, check:required-contexts, check:ci-filter-parity, scheduled-full-run --self-test and --check-concurrency), Lint · repo & tooling gates (check:pm-expected-skips, check:pm-skill-ratchet), Lint · PM dispatch-gates (check:pm-dispatch-gates), Lint · ESLint, the three Lint · scoped · jobs, Governed Surface Queue Guard, PR-body guards (claim · duplicate · Part-of · single-writer path), Build Docs, Check Documentation Links. Dogfood Regression Gate is success with Dogfood Regression Gate (${{ matrix.shard }}/3) and Dogfood Verify CLI both skipped — the pull_request shape ① predicts, unchanged by a comments-only delta. Check Changeset is skipped on this head (round 1 read it success on 4bb48774cf); the delta touches no package path, so a skip is the filter's answer, not a red. Still in_progress at the read: Build Core, Test Core (1/8), the sixteen Test UI (n/16) shards, Lint UI, Type Check UI, Build & E2E UI, Live E2E UI, Lint · docs & code guards, Type Check · workspace, Type Check · consumer gates — the build/test half of the required set, on a diff that changed one YAML comment block, one JSDoc block and one Markdown paragraph. The seat owes the completed read at merge time; this record's PASS is conditional on none of those going red, which nothing in the delta can cause. Combined commit status is pending with zero statuses set, i.e. no status context exists; not a gate.

Live ruleset (rules/branches/main, re-read 2026-11-05T21:47Z): the same ten required contexts as round 1 — TypeScript Type Check, Test Core, Build Core, Lint & Repo Gates, Governed Surface Queue Guard, Lint UI, Build & E2E UI, Type Check UI, Test UI, Live E2E UI. Dogfood Regression Gate is NOT in the set, which is exactly what the follow-up ruling's step 1 ("⛔ No ruleset change in it") requires of this PR.

The three round-1 should-fixes against the delta:

  • S1 — closed. ci.yml now reads "so a dogfood red shows on the queue build of the entry that carries it rather than first on main's push run after it. It is advisory there: the context is outside the required set (below), so the queue may merge the entry before or despite the shards." The ordering promise ("before the merge") is gone; "rather than first on" is true (the queue build is the first run that carries the shards on that change); "(below)" resolves to the block's own sentence "The queue enforces only the ruleset's required set, and Dogfood Regression Gate is outside it since skills(ci): two-speed CI — fast set on PR and merge-queue runs, full battery hourly on main and before a release, main-red stops the lane, four required contexts plus the guard, timing gates report-only, test budgets not deletions #22998" at line 142, fifteen lines down in the same block; "before or despite the shards" is the correct statement of an advisory context under ALLGREEN on the required set only. The discipline doc now reads "so a dogfood red shows on the queue build, advisory only". Both true against the ruleset read today.
  • S2 — closed. The tombstone's "the instruction files name them correctly as full-battery jobs" became "the instruction files name where each runs", which is true of both names: AGENTS.md:513–514 and the discipline paragraph name the shards on queue runs and Temporal Conformance (live PG + MySQL) in the full battery. The appended paragraph adds "Temporal Conformance stays full-battery only" (true: temporal-conformance keeps both exclusions at head) and states the follow-up path: "the follow-up ruling on [Decision] Merge queue: should the three Dogfood Regression Gate shards run on merge_group, so a dogfood red stops a PR before main instead of after? #30461 (A, in two steps, 6669982756) puts it back only after a clean 24-hour reading of the queue builds, the row here landing first and the ruleset edit after it". Checked clause by clause against 6669982756 items 2–3: the reading is 24 h after landing, the gate is the threshold in item 3, the REQUIRED_CONTEXTS row "merges first, before the ruleset edit". True.
  • S3 — closed on substance, in the lesser of the two forms offered. The figure is updated: "about 20 minutes of wall per queue entry (median, 2026-11-05)" against the PR's own 20.4 min (success) / 20.5 min (all conclusions) median. The first clause still reads "PR and queue runs carry the fast set" rather than the AGENTS.md spelling ("PR runs carry the fast set, queue runs add the dogfood shards"); it is not false — queue runs do carry the fast set, and the paragraph's fourth sentence adds the shards — so the paragraph as a whole is true, and the "10 minutes" contradiction that S3 was about is gone. Two precision notes, not findings: the quantity behind "wall per queue entry" is CI run duration on merge_group (updated_at − run_started_at), not queue residence, and the date stamp correctly marks it as the before-reading, so the 24 h after-reading should refresh it.

Part of #30461. Right, and Fixes would now be wrong: the follow-up ruling says in terms "The card therefore stays open after #30639 merges, until the step-3 report and the REQUIRED_CONTEXTS row land, or step 3 stops at a flake fix", and GitHub closes a Fixes #N target on merge whatever the surrounding prose says (the defect check:partof-closing-keyword exists to block). The body's Part of line carries no closing keyword for #30461 anywhere else (RULE 1), and the parenthetical that follows it reads as not closing, with no keyword bound inside it (RULE 3); PR-body guards (claim · duplicate · Part-of · single-writer path) is success on this head. The parenthetical's account of why ("the follow-up ruling 6669982756 owes a 24-hour reading of the queue builds after this lands, then the required-set step") is true.

① Derived judgments

Unchanged from round 1; the delta is comments and prose only (verified above), so each was re-read against the head rather than re-derived.

  1. Which events run the three shards. dogfood's if: at head is !cancelled() && needs.filter.outputs.core != 'false' && github.event_name != 'pull_request'. So: push to main (when the push diff sets core, as before), schedule, workflow_dispatch, and now merge_group. pull_request stays denied. dogfood-verify, temporal-conformance and ui-published-artifacts keep both exclusions. Right, and exactly letter B: one conjunct removed from one job.
  2. filter.outputs.core on merge_group. core: ${{ github.event_name != 'merge_group' && steps.changes.outputs.core || 'true' }} — on merge_group the guard is false, false && x is false, false || 'true' is 'true'. The filter job has no if:, so it runs on every event. If filter itself fails (checkout flake, timeout; the dorny step is continue-on-error on merge_group), its outputs are empty and '' != 'false' is true, so the shards run (THE FILTER CONTRACT half 2, fail-open). The PR's claim "the shards cannot skip silently there" is right: the only skip path on a queue build is a run-level cancellation.
  3. dogfood-gate (if: always()) on a queue build with the shards run and dogfood-verify skipped: judge() takes --leg dogfood/3:success (roster required, three credentials present) and --leg dogfood-verify/1:skipped with filterResult === 'success' → "satisfied (skipped by the filter — filter job 一旦失败,Test Core / Build Core / Dogfood 会全部 skipped 而分支保护判为通过 —— 隐式 success() 今天已第三次咬人 #4928; expected attestations: 0)"; expected 3, present 3, green. Pinned by the existing self-test "dogfood: each leg is judged on its own roster — a full matrix + a filter-skipped CLI leg ⇒ green". A failing shard reads failure on the leg → red; a shard that never attests → "MISSING" → red. Right. On a PR: both legs skipped, filter success → expected 0, green (observed on this head). On a filter-skipped diff (a push whose diff sets core=false): both legs skipped → expected 0, green. On merge_group a filter-skipped diff cannot arise for core (always 'true'). This is also the third item the follow-up ruling asks the 24 h reading to confirm ("counts 3 credentials and judges Dogfood Verify CLI as skipped"): the code says it will, and the reading is the live proof. The ruling's step-4 fuse ("revert ci(dogfood): the three Dogfood Regression Gate shards run on merge_group #30639 … brings the gate back to zero expected credentials and success") is likewise what the judge does when both legs skip on a queue build.
  4. jobIfReading / notOnPullRequest (dispatch-gates). The queue clause regex /\s*&&\s*github\.event_name\s*!=\s*'pull_request'\s*$/ is applied only when the full-battery clause did not match, as the last conjunct. Traced: A && != 'pull_request' reads refs + notOnPullRequest: true, fullBatteryOnly: false; A && != 'merge_group' && != 'pull_request' (reordered) strips the trailing clause, leaves != 'merge_group', fails the output term → null (refused, as the test says); A && != 'pull_request' && != 'pull_request' → one left over → null; the clause in front of the output → null; (A || B) && != 'pull_request' → reads. The full-battery row now also carries notOnPullRequest: true, and fullBatteryOnlyClause checks fullBatteryOnly first, so Dogfood Verify CLI's sentence is unchanged. Right. Without this change the shards' row would have DROPPED from --commands (the old if: was readable, the new one was the refused "one event alone" case), so the fix was owed by the ci.yml change itself.
  5. Does any OTHER job's reading change? Every job-level if: across .github/workflows/*.yml at head was listed: the only one ending in a lone != 'pull_request' is dogfood's. The other lone exclusions (fleet-patrols, governed-surface-guard, shard-timings-refresh, ui-test-timings-refresh, validate-deps, checklist-status) are STEP-level, which neither jobIfReading (reads extractJobBlocks' job-level if:) nor check-expected-skips' jobGate(job.if) reads. No other job's reading changes. Right.
  6. Expected-skips reason. pr-event terms merge into one events list; "full battery only" is now said only when PR_EVENTS.every(...); the shards keep their derived row (a PR head still skips them, which is what the roster verifies) with the reason "skips on every PR head and is answered by the merge-queue build and main's push and scheduled runs". The fixture tree models the new live shape. Right.
  7. Cited and left alone, verified in the code: check-shard-attestation.mjs reads no event_name anywhere outside fixtures and pins only if: always() on the gates; its cancel-cause classifier is prefix-based ('Canceling since a higher priority waiting request for'), so a merge_group concurrency group is classified the same as a pull_request one. check-required-contexts.mjs pins registry rows only; Dogfood Regression Gate is not one, and the delta edits its tombstone comment, not a row. check-ci-filter-parity.mjs reads filter globs; no event_name in it. release.yml's full-battery and scheduled-full-run.mjs --release-gate read schedule/push runs only, which is still the right source since dogfood-verify and the temporal leg stay off the queue. All right.
  8. Now-live dormant code. The dogfood build step's TURBO_CACHE: … merge_group … 'local:rw,remote:rw' was dead under the old deny-list and is live now; the identical expression appears 8 times in ci.yml, on jobs that already run on merge_group, so it is the established queue policy, and the local turbo seed save stays push-only. Not a finding.
  9. No gate pins the edited prose. Swept scripts/ and .github/ at head for pins on the THE TWO SPEEDS wording ("before the merge", "rather than on main"), the tombstone sentence and the discipline doc's path: dispatch-gates.mjs:1184 and check-expected-skips.mjs:371 name the block, neither quotes its sentences; nothing under scripts/ reads docs/agents/multi-agent-discipline.md; the 120-byte line ratchet (check-skill-line-ratchet.mjs) covers AGENTS.md and the agent files, not docs/agents/, and AGENTS.md is untouched by the delta (its two Two speeds lines stay 117 B / 118 B). So the delta can move no gate; the check-runs confirm it.

② Semver level

Unchanged. No published package, schema or .changeset is touched: a workflow (Tier H), four PM/CI scripts with their self-tests, and prose; the delta adds only comments. skip-changeset is right and Clause-②: no (plain, not (narrowing)) is the right line: nothing a consumer declared is taken away. The face is rules (governed paths .github/workflows/**, AGENTS.md) ⇒ CONTRACT_REVIEW_TIER, which is what this record serves. Semver level: none.

③ Boundary flags

(a) "ejects the PR from the queue" vs the required set — now ruled; the round-1 escalation closes. Round 1 found that ruling 6663883327's binding text rested on the card body's "Facts" calling Dogfood Regression Gate "the required context" (false since #22998), so that this PR alone makes a dogfood red visible on the queue build but does not eject the entry, and asked the seat to raise the required-set question. The follow-up ruling 6669982756 (maintainer 「同意」 2026-11-05T21:25Z) states the same gap in its own words ("PR #30639 alone makes a dogfood red visible on the queue build but does not eject the entry") and binds A in two steps: (1) this PR lands under letter B with no ruleset change; (2) the owed 24 h after-reading gains three items — entries that ran dogfood and whether the shards ran, each red classed (a) own regression / (b) flake / (c) inherited, and the 3-credentials-plus-skipped-CLI confirmation; (3) threshold — at most one (b), no environmental failure, queue median wall-time increase ≤ 2 min — then the maintainer adds the context to the ruleset after a REQUIRED_CONTEXTS row lands first as an XS PR; (4) a fuse — if once required flakes eject ≥ 3 entries in a day the lane may revert #30639. The tree at this head is consistent with every one of those: no ruleset or registry row is touched (step 1); the gate's queue-build judgment matches item 2's third bullet (①3); the fuse's mechanism is what the judge does under the old deny list (①3). The prose that round 1 asked to stop overstating now states the advisory limit in all three places (S1, S2). The two rulings do not conflict: 6663883327 binds the code change (letter B, one conjunct) and 6669982756 binds what follows it; the seat's reading in the PR body's Acceptance notes says exactly this. No escalation remains open from this review. What is owed next is the seat's, not the PR's: the 24 h reading in the ruled shape.

(b) AGENTS.md and docs/agents/multi-agent-discipline.md, outside the claim's file surface. Necessary restatement, not overreach (unchanged from round 1): AGENTS.md's Two speeds line literally said "PR and queue runs carry the fast set", which the tree now contradicts, and the claim's surface already named "every comment that restates the dogfood deny list". Truth of the lines at this head: AGENTS.md:513–514 ("PR runs carry the fast set, queue runs add the dogfood shards; main's scheduled and push runs carry the full battery") — true, 117 B and 118 B inside the 120 B budget. Discipline paragraph: the shards on queue runs, "advisory only", the three legs staying full-battery only, "the context is outside the required set, so the queue does not hold an entry on it", and "about 20 minutes of wall per queue entry (median, 2026-11-05)" — all true against the tree, the ruleset and the PR's own measurement. The round-1 overstatement is gone.

(c) The before-reading of queue wall time, now with a ruled threshold. The before figure (actions/workflows/ci.yml/runs?event=merge_group&created= at-or-after 2026-11-04T18:41:25Z, pages 1–4, duration = updated_at − run_started_at, completed runs, run_attempt 1; n=303, median 20.5 min all conclusions, 20.4 min success) is sound as a like-for-like comparand. The follow-up ruling's item 3 now hangs a decision on it: "a queue median wall-time increase of at most 2 minutes". For that threshold to be meaningful the after-reading must (i) use the same formula, filters (completed, run_attempt 1), event and a 24 h window, and compare the same conclusion class (the body gives both; success-only, n=285, 20.4 min, is the steadier of the two); (ii) be named as CI run duration on merge_group, not queue residence (enqueue → merge, which includes waiting for a build slot and for entries ahead) — run_started_at excludes pre-start pickup wait, so a slower runner pickup under +3 jobs per entry would show as in-run job pickup (captured) rather than run start (not); (iii) carry the discipline doc's "about 20 minutes" forward if it moves. The critical-path reading (60 newest success runs; Test Core closes 38/60 at median 20.8 min) and the push-side shard timing (11.4 min median, 12.3 p90, 14.2 max) remain the right comparands for "should move little". Fine as the before half; the after half is the seat's.

④ Design judgment

Held against ruling 6663883327 (letter B), the follow-up 6669982756 (A in two steps) and the code they cite: merge_group alone leaves the deny list; pull_request stays denied; dogfood-verify, temporal-conformance and ui-published-artifacts keep both exclusions; no affected-only filter (the shards ride core, which is 'true' on every queue entry); no ruleset or registry change. Precedent #25867/#25881 is the same shape one layer down (the filter's ui outputs reading the queue diff) and this PR correctly does NOT copy it onto core (that would be letter C). Precedent #26460 (Test UI, cited by the follow-up ruling) is the same question for another context and was answered the same way — ruling 6435318516, letter A staged: a flake reading on main first (6436579765, "no Test UI flake is live"), then the aggregate enrolled in REQUIRED_CONTEXTS and the ruleset — which is the shape the two-step path copies. Attempts to break it (round 1, re-read at head):

  • filter skipped or failed on a queue build: filter has no if:, so it skips only under run cancellation (!cancelled() then skips the shards; the gate reads filterResult === 'cancelled' → satisfied). A failed filter runs the shards fail-open and reds the gate on the skipped verify leg ("skipped while the filter job did not succeed") — the same red the old tree produced, so no new direction. Advisory either way.
  • !cancelled() semantics: untouched in position and in both readers' stripping.
  • Attestation counting on a queue build: 3 of 3 + 0 expected; a credential from the skipped leg would red the gate (pinned), a missing or failed shard reds it. The --leg dogfood/3 roster, fail-fast: false and the credential steps without if: are unchanged.
  • A later trigger added to on:: the deny-list spelling runs the shards on it, and the dorny allow-list leaves core as 'true' there — the failure direction is a slower run, never a silent skip, as the header says.
  • The revert fuse (follow-up ruling step 4): restoring && github.event_name != 'merge_group' puts both legs back to skipped on a queue build → expected 0 → success, with jobIfReading reading the restored spelling as full-battery (the pre-PR path, still pinned by the LIVE pin on Dogfood Verify CLI). The revert is one conjunct and needs no ruleset step, as the ruling says.
  • Prose vs tree: swept .github, scripts, docs, *.md, skills at head for the old deny-list prose and for "fast set"/"full battery"/"before the merge" restatements; every remaining statement is updated or still true. upgrade-harness-optin.yml's sentence is true: the harness test lives under packages/qa/dogfood/test/, so its first R2 answer is now the queue build.

Findings:

  • S1, S2, S3 (round 1) — closed in 263527d3d5, as judged above.
  • S4 (should-fix, PR body only, not the tree): the body's 维护者速读 is now stale against the follow-up ruling in two places, while the body's own Acceptance notes are current. 「为什么改」 ends "现在 dogfood 红会出现在该条目的队列构建上,合并之前就能看到" — the "before the merge" ordering that S1 removed from the tree; and 「风险与回滚」 ends "要不要把它加回必需集合,裁决没定,席位另行呈报" — the required-set question is now ruled (6669982756, A in two steps), and the seat has already reported. The 速读 is the Tier H approver's first read, so it should say what the Acceptance notes say: 队列里的 dogfood 红是提示性的(不在必需集合,队列可能先合并);加回必需集合已由后续裁决 6669982756 定为两步(先 24 小时读数,再 REQUIRED_CONTEXTS 行,后改 ruleset). Wording only; the squash message is taken from the body, so it is worth fixing before the merge.
  • Notes, not this card's (pre-existing, unchanged by this PR): the dogfood job's timeout-minutes: 30 comment still says "~6× a shard (~5 min)" against the PR's own 11.4 min median / 14.2 max (the 30-minute backstop still holds at about 2× the max). The tombstone's first paragraph still says "their legs run in the FULL BATTERY only — main's push, hourly and dispatch runs": "hourly" predates the 4-hourly cadence, and the present tense is now amended, not replaced, by the "Since [Decision] Merge queue: should the three Dogfood Regression Gate shards run on merge_group, so a dogfood red stops a PR before main instead of after? #30461" paragraph beneath it; both are readable as the skills(ci): two-speed CI — fast set on PR and merge-queue runs, full battery hourly on main and before a release, main-red stops the lane, four required contexts plus the guard, timing gates report-only, test budgets not deletions #22998 account and the second paragraph carries the current state.
  • Must-fix: none. The if: change is the one ruled conjunct, the delta adds no executable text, every derived gate that had completed on the head is green and the pending ones are named above, the two readers that would otherwise have misdescribed the new spelling are corrected with LIVE pins and ablations, the ruled effect's limit is stated in the tree in the follow-up ruling's own terms, and the body's Part of keeps the card open as that ruling requires.

Implemented-by: claude/issue-30461-dogfood-merge-group
Reviewed-by: session_017ihL2ebbZkr2QAEG1x7UC8

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:devx#2 · marchtian · session_017ihL2ebbZkr2QAEG1x7UC8

席位核对 — ACCEPT · domain:devx#2 · Head-sha 263527d3d538f2ef4d39cb94ecfd57f49c4d4857 · 2026-11-05T22:05Z

  • Shape: 10 files, +206/−77, read from the diff and not the report. The behaviour change is a single conjunct: the dogfood job's if: in .github/workflows/ci.yml drops github.event_name != 'merge_group' and keeps !cancelled(), the core filter and != 'pull_request'. dogfood-verify, temporal-conformance and ui-published-artifacts are unchanged. The rest is prose that restates the split (THE TWO SPEEDS, release.yml, upgrade-harness-optin.yml, AGENTS.md, the discipline doc and the required-contexts tombstone) and the two readers of a job's if:. dispatch-gates.mjs gains a notOnPullRequest reading with LIVE pins and an ablation. check-expected-skips.mjs calls a job "full battery only" only when both events are excluded.
  • Rulings: letter B (6663883327) and the follow-up A in two steps (6669982756). The tree matches both. There is no ruleset or REQUIRED_CONTEXTS change, and the text says the queue-build red is advisory until the context rejoins the required set. The PR body says Part of #30461, so the card stays open for the after-reading.
  • Contract review: PASS in two rounds, record 6670580911 on this head. Round 1, on 4bb48774cf, raised three wording should-fixes. The seat closed them in 263527d3d5. The review's S4, the stale 维护者速读 in the PR body, is fixed by a body patch. Must-fix: none.
  • Gates:
    • Head checks: 56 success and 11 skipped, none red, mergeable clean.
    • check-expected-skips: OK, all 11 skips are in main's roster (exit 0).
    • check-governed-merges: GOVERNED, 4 of 10 paths on the register (ci.yml, release.yml, upgrade-harness-optin.yml and AGENTS.md), landing tier H. 283 changed lines, which is ≤ 3000.
    • The seat ran dispatch-gates --commands on this head: 80 of 80 exit 0, and --ran reconciled (80 run, 0 NOT-MEASURED).
  • Before-reading: the PR body records queue wall time on merge_group CI runs over the 24 hours before the change, with the queries: 303 runs, median 20.5 min, p90 26.0 min.
  • Owed after landing: the 24-hour reading on [Decision] Merge queue: should the three Dogfood Regression Gate shards run on merge_group, so a dogfood red stops a PR before main instead of after? #30461 that ruling 6669982756 step 2 lists. Its threshold decides the required-set step.
  • Out of scope: Skills-docs: n/a, Clause-②: no, Metadata-protocol: none.
  • Landing: Tier H. Ready, reviewers os-zhuang and hotlong, needs-user-decision. The seat lands it after an authorized APPROVED review.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 5, 2026 22:06
@objectstack-fleet objectstack-fleet Bot added the needs-user-decision Blocked on a maintainer decision — do not dispatch; decision cards only, never on a PR label Nov 5, 2026
@os-zhuang
os-zhuang added this pull request to the merge queue Nov 5, 2026
Merged via the queue into main with commit 788715e Nov 5, 2026
73 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-30461-dogfood-merge-group branch November 5, 2026 22:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd documentation Improvements or additions to documentation needs-user-decision Blocked on a maintainer decision — do not dispatch; decision cards only, never on a PR size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants