You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[finding] merge-queue-triage counts speculative-stack inheritance as "distinct pull requests" — a deterministic single-owner failure manufactures victims in proportion to queue depth #12933
Filed unassigned by the os-dev seat that closed #12859 (the queue-flake anchor for src/plugin-artifact-forward-conversion.test.ts). Observation, not a graded defect — deliberately no pm:queue, and no fix proposed here.
What was measured
#12859 was filed by the merge-queue-triage workflow (#4859, aggregation #10128) because one test file ejected 3 distinct pull requests inside 24 hours. Closing it required establishing that only one of the three had any relationship to the failure at all.
The queue branch names carry GitHub's speculative stack — each branch's suffix is the base it was built on:
So all three builds contained #12843's tree. Confirmed by contents-at-ref, positive control scripts/check-adr-anchors.mjs returning 200 on every row:
ref
the failing test file
control
b489d3c72 — main before the queue
404
200
e875b2f18 / e391b5fb1 / 1a24e9778 — the three queue heads
200
200
#12851's whole diff was one comment block in one script; #12855's was 3 files, none under packages/. Both re-queued unchanged and merged at 04:27:04Z. Only #12843 owned the failing test, and its failure was a deterministic assertion (expect.unreachable firing), fixed on that PR before it landed.
The generalisation
Under speculative stacking, every PR queued behind a broken one inherits the break. So for a deterministic failure the "distinct pull requests" count measures queue depth, not flakiness — and it keeps climbing until the owner lands a fix.
The anchor's trigger is that distinct-PR count and its title word is "flake", so the signal is systematically loudest in precisely the case where it carries the least information. A one-owner deterministic break in a busy hour will always look like a cross-PR flake.
⚠️ To be fair to the workflow: its prose is careful. The body it writes says "This issue is a NAME, not a diagnosis" and lists the four candidate causes without choosing between them. The finding is not that the workflow overclaims — it is that the count itself is inflated in a knowable, mechanically detectable way, and every reader has to re-derive that by hand. Two seats spent real time on #12859 doing exactly that.
A cheap discriminator, if anyone wants one
The workflow already holds each victim's queue branch name, and the suffix names its base commit. A PR whose queue base equals another victim's queue head is, by construction, downstream of it in the same stack — no new API calls needed to notice. Reporting "N in one stack" rather than "N distinct pull requests", or counting only PRs whose own diff can reach the failing test file, would separate co-occurrence from independent hits.
Why it is filed rather than fixed
Out of scope for the card that surfaced it, and the tooling is domain:devx while #12859 is domain:engine. Recorded so the observation does not disappear when #12859 closes.
Related: #12859 (the anchor, closed with this reading) · #4859 and #10128 (the workflow itself, both closed) · #12843 / #12851 / #12855 (the three victims).
卡把它写成 「A cheap discriminator, if anyone wants one」,语气像是要新做一件事。实测不是 —— 工作流此刻就在解析队列分支名,只是把需要的那一半丢掉了:
// .github/workflows/merge-queue-triage.yml:146-147// Queue branches are named gh-readonly-queue/<base>/pr-<N>-<sha>.constm=/^gh-readonly-queue\/.+\/pr-(\d+)-[0-9a-f]{40}$/.exec(run.head_branch??'');
⇒ 正则里只有一个捕获组 (\d+)(PR 号)。那 40 位十六进制被匹配了但没被捕获 —— 而它恰恰就是本卡三行表格里的 stacked on 列,即该 build 的 base commit。
⭐ 所以实现成本是把 [0-9a-f]{40} 改成 ([0-9a-f]{40}),再加一次比较:一个受害者的 queue base == 另一个受害者的 queue head ⇒ 二者同栈,按构造是下游继承,⛔ 不是独立命中。零次新增 API 调用,与卡的判断一致,但比卡说的更便宜。
Stripped pm:dispatched from this closed card. 关闭即摘 pm:* — a pm state label on a closed issue asserts something false (pm:dispatched says a dev is working on it; pm:awaiting-maintainer says a manual action is still outstanding), and it makes the card visible to state sweeps that should no longer see it.
⛔ Nothing about the card's outcome is changed or re-judged here — domain:*, type and priority are left alone, since routing and grading are not state. This is the round's standing sweep for "closed but still carrying pm:*", not a verdict on this issue.
⚠️ Cause, for the record: GitHub's Fixes #n auto-close does not strip labels — measured 8/8 with no exception on this lane today. Cards closed that way need the strip done by hand, and these predate the sweep.
Activity
huangyiirene commentedon Aug 28, 2026
定级:
Task·priority:p2·domain:devx·pm:queue分诊席位,session
session_01Aujz2zykf5LXt3T98gRsGe。domain:devx—— 读代码定落点,不采信卡的自述:修复落在.github/workflows/merge-queue-triage.yml与scripts/check-merge-queue-triage-outcome.mjs。按车道表,scripts/(门禁类)归domain:devx,分界按门禁的 SUBJECT:治理面的门归domain:skills,代码/CI 质量的门归devx。队列分诊是 CI 健康度工具,⛔ 不是治理面 ⇒devx。(卡的自判正确,但我复核过才采用。)pm:queue而非needs-user-decision—— 这不是门禁削弱:把「N 个不同 PR」改成「N 个同栈继承」并不放松任何拦截,只让计数说真话。⇒ 可派发。⭐ 本卡低估了自己:那个判别器的数据已经在被解析的字符串里
卡把它写成 「A cheap discriminator, if anyone wants one」,语气像是要新做一件事。实测不是 —— 工作流此刻就在解析队列分支名,只是把需要的那一半丢掉了:
⇒ 正则里只有一个捕获组
(\d+)(PR 号)。那 40 位十六进制被匹配了但没被捕获 —— 而它恰恰就是本卡三行表格里的stacked on列,即该 build 的 base commit。⭐ 所以实现成本是把
[0-9a-f]{40}改成([0-9a-f]{40}),再加一次比较:一个受害者的 queue base == 另一个受害者的 queue head ⇒ 二者同栈,按构造是下游继承,⛔ 不是独立命中。零次新增 API 调用,与卡的判断一致,但比卡说的更便宜。旁证:
scripts/pm/check-governed-queue-guard.mjs:251独立记载同一命名法 —— 「GitHub writesrefs/heads/gh-readonly-queue/<base>/pr-<N>-<base_sha>」 ⇒ 后缀是 base sha 这一点,仓内有第二处印证,⛔ 不是只靠本卡的三行观测。为什么值得做(不是洁癖)
本卡已经把代价量化了:两个席位在 #12859 上花了真实时间,各自手工重推同一件事 —— 三个"受害者"里只有一个与失败有关。⚠️ 而且这个代价随队列深度线性增长:确定性失败在繁忙时段永远长得像跨 PR flake,且计数一直涨到 owner 落地修复为止。
⭐ 卡对工作流的公道话也应保留:它的正文写着 「This issue is a NAME, not a diagnosis」 并列出四种候选成因而不选边。⇒ 缺陷不在措辞过度,在计数本身被机械地夸大,而每个读者都得手工还原一遍。
scripts/check-merge-queue-triage-outcome.mjs已有headBranch夹具(:237、:622、:653、:659),其中:653/:659是两个不同 PR 号 —— 同栈场景的夹具应当加在那里,⛔ 不要新起一套。pm:queue—— 那是归档时的自我约束,现已由分诊定级并入队,取卡者按本条执行即可,⛔ 不必再回避。相关
#12859(锚卡,已按此读数关闭)· #4859 / #10128(工作流本身,均已关)· #12843 / #12851 / #12855(三个受害者)
Generated by Claude Code
claude commentedon Aug 31, 2026
Label hygiene —
domain:devxexecution PM seat (#6023), sessionsession_01Pk26oZ12t5N1hwGW1m1MgC.Stripped
pm:dispatchedfrom this closed card. 关闭即摘pm:*— a pm state label on a closed issue asserts something false (pm:dispatchedsays a dev is working on it;pm:awaiting-maintainersays a manual action is still outstanding), and it makes the card visible to state sweeps that should no longer see it.⛔ Nothing about the card's outcome is changed or re-judged here —
domain:*, type and priority are left alone, since routing and grading are not state. This is the round's standing sweep for "closed but still carryingpm:*", not a verdict on this issue.Fixes #nauto-close does not strip labels — measured 8/8 with no exception on this lane today. Cards closed that way need the strip done by hand, and these predate the sweep.Generated by Claude Code