Repository navigation
fix(ci): a pm:blocked card parked on a PR is released only once that PR merged - #27924
Conversation
…PR merged
The full-run card refresh resolved every `Blocked-by:` target through
issues.get and kept `{state, closed_at}`. The issues API answers a PR
number `closed` whether the PR merged or was closed unmerged, so an
abandoned PR released the card as though its fix had landed on main.
The card step now carries `pull_request.merged_at`, and the verdict
holds a closed PR with no `merged_at` and names it on the summary row.
The self-tests pin merged (released, body and comment channel),
closed unmerged (held) and open (held), plus the card step's
projection.
triage-duties.md names the line triage writes when it parks a card:
a line-initial `Blocked-by: #N` in the card body, an issue or PR
number, beside the label; the full-run card takes it in a comment
because its body is rewritten on every red. The line is paid in the
same file by folding the two reporting-discipline bullets and
dropping their restated clause.
Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WV4cxBhj4jCJgyxHY42p5
|
domain:skills#2 · os-elon-musk · session_016WV4d1QkPVVzNz3FJqb2iS Contract-review-claim: session_016WV4d1QkPVVzNz3FJqb2iS · head 928dcbc · 2026-10-30T04:05Z |
|
domain:skills#2 · os-elon-musk · session_016WV4d1QkPVVzNz3FJqb2iS Contract reviewServed-tier: ① Derived judgments
② Semver levelNo published package is touched ( ③ Boundary flags
④ Design judgment
Implemented-by: VERDICT: PASS Must-fix: none. Should-fix:
|
|
domain:skills#2 · os-elon-musk · session_016WV4d1QkPVVzNz3FJqb2iS 维护者速读
席位意见:ACCEPT。契约复核 PASS,没有 must-fix;三条 should-fix(state-machine.md 的目标写法、H19 的 merged_at、正文与评论通道的说法不一)都转到跟进卡 #27925,由分诊在 A/B 两种写法里定。CI 全绿。 席位核对
|
Requested by Elon · project thread
domain:skills#2 · os-elon-musk · session_016WV4cxBhj4jCJgyxHY42p5
Fixes #27846
Clause-②: no
Metadata-protocol: none
维护者速读
triage-duties.md加一行:分诊把卡挂pm:blocked时,同一笔在卡正文写行首Blocked-by: #N(issue 或 PR 号;整点全量红卡写在评论里),散文提及不算。整点全量红卡刷新时的放回判断,目标是 PR 的只认「已合并」:关闭未合并的 PR 不再把卡放回队列。scripts/ci/下的工具,不发布任何包。放回只会更严(多一种「不放」),不会多放。回滚即 revert。scripts/ci/scheduled-full-run.mjs的judgeCardBlock与staleBlockVerdict;scripts/ci/scheduled-full-run-card.mjs的getIssue;triage-duties.md第 36 行。node scripts/ci/scheduled-full-run.mjs --self-test && node scripts/ci/scheduled-full-run-card.mjs --self-test,预期两行都报 OK(后者 8 个用例)。node scripts/pm/check-governed-merges.mjs --pr本 PR 号)。What changes
The release reader tells a merged PR from an abandoned one. When the hourly full-run card is refreshed while it carries
pm:blocked, everyBlocked-by:target is resolved throughissues.get, and the card step kept only{ state, closed_at }. The issues API answers a PR numberclosedwhether it merged or was closed unmerged, so a closed-unmerged PR released the card, against this file's own rationale ("whose closure means its fix landed on thismain"). Measured before the fix by drivingjudgeCardBlockwith that projection: merged →expired, closed unmerged →expired, open →held.scripts/ci/scheduled-full-run-card.mjs:getIssuenow carriespull_request.merged_atwhen the answer is a PR (no extra call:issues.getalready returns it).scripts/ci/scheduled-full-run.mjs:judgeCardBlockmarks a closed PR with nomerged_atasunmerged: true(a missingmerged_atreads the same, so the unknown case holds).staleBlockVerdictholds on it, and the summary row names it (#N closed unmerged (a PR that never landed)). THE RULE table gains the row.Triage names its park line.
.claude/skills/pm-dispatch/references/triage-duties.mdgains: 「停pm:blocked同笔在卡正文写行首Blocked-by: #N(issue/PR 号;full-run 卡写评论),散文不算。」 This is triage's option 1 as ruled in 6513844891. The line is paid in the same file: the two reporting-discipline bullets fold into one, dropping 「⛔ 不弹给维护者」 (already implied by 「只进座位贴和触发器」) and 「运维」. The file stays at 102 lines, its ceiling row is untouched, and no line exceeds 120 bytes.Why
Triage parked cards with
pm:blocked on PR #Nin comment prose and no line-initialBlocked-by:. No unlock path reads prose, so 14domain:servercards whose PR had merged sat blocked while seats reported nothing claimable.Tests
scheduled-full-run.mjs --self-test, battery "card refresh: a stale pm:blocked": a park on a PR, body channel and comment channel, MERGED → released topm:queue; the same PR closed UNMERGED → held, no label write, no note, the summary names the abandoned PR; the same PR OPEN → held.scheduled-full-run-card.mjs --self-test: new case "a pm:blocked card whose blocker is a PR closed unmerged is held":issues.getanswers the PR shape (pull_request.merged_at: null), the step makes noaddLabels/removeLabelcall and reportsblock: held -- #601 closed unmerged (a PR that never landed). The battery roster gains the row and its floor moves 7 → 8.Acceptance notes
Out of this card's surface. The seat files one follow-up card for triage:
scheduled-full-runjudges only its own CI card, and only on a new red. It never reads any otherpm:blockedcard, so it would not have released the 14 stranded cards even with a body line. Ordinary cards are still released only by the seat and triage unlock scans.dispatch-runbook.md〈分诊两级盘点细则〉 item 2 intersects astate=CLOSED+sincelisting with theBlocked-by:index. Through MCPlist_issues, that listing holds no PRs: measured 2026-10-30,since02:30Z, 15 rows and none a PR, while fix(rest): a refused xlsx export answers the 400 envelope, not a 200 workbook stream #27888, feat(studio): the organization-flows publish toast names the flows that went live #27882 and feat(studio): Add view in the Interfaces list-view panel creates a kanban, calendar or other list view #27870 merged inside the window. A PR target is found only by the forward scan.state-machine.md〈pm:blocked〉 points a PR wait at a card. It says a card waiting on a PR merge names 「一张在该刻关闭的卡」 (the card the PR fixes). Option 1 allows the PR number itself. A target that is the fixed card avoids both the reverse-scan gap and the closed-unmerged question.scripts/pm/check-half-states.mjsresolveBlockerTargetreads/issues/Nstateonly, so it reports a block on a closed-unmerged PR as outlived. It is report-only, and the refresh is now the stricter of the two, in the direction that cannot release a card.Also noted, not filed:
Blocked-by:is in a comment, whileplatform-readings.mdsays the seat unlock scan greps the body only. A comment-borne line passes H4 and stays invisible to that scan.lanes/devx.mdsays this repo deliberately keepsBlocked-by:in comments, which disagrees with the body-only rule.state-machine.md's sentence on this refresh (「双通道目标全为本仓已关卡…」) does not yet say that a PR target must have merged.claude/project-thread-ucikconames no issue; claim 6514262092 (amended) is the identity record.Verification
scheduled-full-run.mjs: withunmergedforced tofalsethe self-test fails withexpected a refresh judged 'held', got refreshed 'expired' (expired -- #24305 closed); restored byte-identical (cmp).scheduled-full-run-card.mjs: with the projection back to{ state, closed_at }the new case fails (github.rest.issues.addLabelsis not modelled by this case); restored byte-identical.928dcbc5(merge base2a3dec2d):node scripts/pm/dispatch-gates.mjs --commandsderived 40 families (re-derived after the commit: identical). Afterpnpm install --frozen-lockfile --ignore-scripts, each ran with its exit code recorded.check:doc-formula-expressionsfirst exited 3 (prerequisite) and exited 0 afterpnpm exec turbo run build --filter=@objectstack/formula --filter=@objectstack/lint; the other 39 exited 0 on the first run.--ran: 40 derived, 40 run, 0 NOT-MEASURED, 0 UNRUN. This includescheck:pm-skill-ratchet(triage-duties.md 102/102) andcheck:pm-skill-id-lint.check-half-states.mjs --format=markdown --provenance="$PROVENANCE"(takes a workflow value), thePlan Test Core shardsstep, and the type-check lanes. This diff touches no TypeScript.dispatch-gates --tier: no path-derived mandate. 121 changed lines, under the 3000 threshold.Human Minimum
packages/specholds no runtime logic. (not touched)--no-inline-config) on the two changed scripts exits 0, and CI'spnpm lintis cited from the check-runs.main, one pull request.skip-changeset)packages/spectouched: its generated artifacts are regenerated and committed. (not touched)content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_016WV4cxBhj4jCJgyxHY42p5