Repository navigation
[Decision] A commit-trailer red is unclearable by any permitted act, and the gate's own repair requires a MANUAL merge the lane forbids — which rule yields? #16502
Description
State: pm:blocked → pm:awaiting-maintainer, 2026-09-11T12:50Z (domain:cli execution seat, #6024). The ruling below (C + D, decision batch #73) is recorded and half of it is done: #16516 closed completed 2026-09-09T04:09:09Z and both of its edits are verified present on origin/main. What remains is D alone, and D is a repository-settings act no seat can perform — so this card leaves pm:blocked, whose only machine exit is a blocker closing.
Maintainer-action: change the repository setting squash_merge_commit_message from COMMIT_MESSAGES to PR_BODY (Settings → General → Pull Requests → default squash commit message) — done when GET /repos/objectstack-ai/objectstack reports squash_merge_commit_message as PR_BODY; read 2026-09-11T12:44Z it is still COMMIT_MESSAGES.
⛔ Nothing is blocked on D. PR #16470 landed under C and needed neither condition. D closes the class, so the next instance of this red needs no decision.
维护者速读
事情:一个 dev 把 Fixes #14895 写进了 commit message(应该只写在 PR 正文里)。门禁 Part-of PR must not also close its card 因此变红,PR #16470 卡住。
麻烦在于这个红清不掉:门禁检查 PR 上的每一条 commit,所以再推新提交只会增加问题、不会减少(已用门禁自己的判定函数跑了四种反事实证明)。唯一能去掉它的两种手段,一是改写历史 force-push(AGENTS.md 三处无条件禁止),二是重开一个 PR。
而且门禁自己说它就该是红的:它的文件头写着「已推送分支上的红,是在正文里、在合并那一刻修的」。但合并那一刻的修法是人工的 —— 仓库设置 squash_merge_commit_message 是 COMMIT_MESSAGES,所以要人在合并按钮那里手动把 squash 正文换成 PR 正文。自动合并不会做这一步。
于是两条成文规则撞上了:车道规定「每一个 check 全绿才能入队」且「队列是唯一被认可的落地路径,永不队列外合并」;门禁规定的补救却要求一次人工合并。两条都遵守是不可能的。
本 PR 的实际风险是零(已测):正文和 commit 指向同一张卡、同一种关系,#14895 无论如何都会正确关闭。所以这是先例问题,不是本 PR 的安全问题。
你要做的:选 A / B / C / D(可多选,C+D 是我的推荐)。
Governing text:
.claude/skills/pm-dispatch/SKILL.md→ 入队与落地:「入队资格 = PR 上每一个 check 全绿,⛔ 不是 required 子集」「队列是唯一被认可的落地路径,⛔ 永不队列外合并」scripts/check-partof-closing-keyword.mjsRULE 2 及其文件头:108-112AGENTS.md:464、AGENTS.md:474、.claude/agents/os-dev.md:63(禁止改写已推送历史)
测得的事实(⛔ 全部实测,非推断)
| 事实 | 读数 |
|---|---|
| 该门禁是否必查 | 否。main 的 required contexts 恰七个,不含它;mergeable_state = unstable,不是 blocked |
| 队列会不会被它挡 | 不会。该 workflow 无 merge_group 触发器,按设计无法在队列构建上报告 |
| 推新提交能否清红 | 不能。门禁自身判定函数四例反事实:真实2提交=1、追加任意干净提交=1、去掉该提交=0、单条干净提交=0 |
| 合并那一刻的补救是否自动 | 否。squash_merge_commit_message = COMMIT_MESSAGES |
| 本 PR 落地的残余风险 | 零。双载体同卡同关系 |
| PR 其余健康度 | 33 个 check,27 success / 5 skipped / 1 failure;门禁联合 57/57 绿,Artifact rosters 37/39 |
四棱
① 实际业务需求 — #16470 是一个真缺陷的修复:os i18n extract --check 打印的"重新生成"命令会丢标志位,照它跑会写出不同的 bundle,下一次 --check 再失败并再打印同一条错命令 —— 一个本可一步自愈的失败被变成了死循环。修复已完成、已契约复审、门禁全绿(除这一条)。它应该落地,问题只是从哪条路落。
② 项目长远合理性 — 这是类问题,不是本 PR 的偶发。只要有 dev 把卡片 trailer 写进 commit,就会重现,而且每次都不可清除。选项 D 是唯一触及根因的:让门禁自己规定的补救变成自动发生,而不是依赖每次合并时有人记得手动改。A 和 C 都是本次绕过,不改变下一次。
③ 防 AI 写代码犯错 — RULE 2 存在的理由正是「AI 逐条诚实写的 commit message 被 squash 连成一条自相矛盾的落地文本,关掉了没人想关的卡」。
④ 创业阶段不扩散 — 倾向最小动作。B(重开 PR)每次都要一整轮返工、新 PR 号、重跑全部 CI,为一个已测零风险的红付这个价,是最贵的。⛔ 不推荐把 B 变成常规通道。
选项
A — 人工合并 #16470,在合并按钮处手动把 squash 正文换成 PR 正文(门禁文件头写下的正是这条)。
· 代价:破「永不队列外合并」。该规则是为合并串行化安全存在的,⛔ 不宜为一个装饰性的红破例。
B — 授权重开 PR(相同 diff、干净 commit message),关闭 #16470。
· 代价:一整轮返工。⭐ 澄清一点:契约复审记录不会丢 —— 裁决锚在卡 #14895(评论 5565241724 与 5564848115),不在 PR 上;要重做的只是 27 个绿 check 和载体配对。
C — 允许本 PR 带这一个红入队(门禁非必查、队列不受其阻)。
· 代价:破「每一个 check 全绿才入队」。
D — 把 squash_merge_commit_message 改成 PR_BODY(仓库设置)。
· 让门禁自己规定的补救自动发生,此后这一类红不再需要任何人工步骤。⛔ 这是维护者的设置决定,我无权改。
推荐:C + D
C 落本 PR:门禁按设计而红、非必查、队列不受阻、残余风险实测为零,而 A 要破的那条规则(队列唯一)保护的是真实的合并安全,比 C 要破的那条更硬。
D 修类问题:它把「记得手动改 squash 正文」从一条无人强制的纪律,变成一个设置。
⇒ 若同意 C,我会另开一张 skills 车道卡,给入队规则补上「按设计而红」这第三种情形,并修正该门禁自相矛盾的失败输出(把文件头的化解写进输出里)。⛔ 这两处都是治理面文本,不由本席位自行改写。
Generated by Claude Code
Activity
os-zhuang commentedon Sep 7, 2026
Ruling recorded — options C + D (director seat, decision batch #73, 2026-09-07)
Maintainer reply, verbatim: 「同意」 — presented with C + D recommended.
Ruling:
i18n extract --check's regenerate hint the invocation it was given #16470 enters the queue carrying this one red. The gate is not a required context, has nomerge_grouptrigger, its own header names the red as by-design on a pushed branch, and the residual risk of landing was measured at zero. ⛔ Option A (manual merge outside the queue) is refused: 「队列是唯一被认可的落地路径」 protects merge serialisation and is not broken for a decorative red. ⛔ Option B (re-open the PR) is refused as the most expensive path for a red with zero risk. Cleared on the PR: 5565946407. The PM lane un-drafts and queues it.squash_merge_commit_messagemoves fromCOMMIT_MESSAGEStoPR_BODY. This is the maintainer's own change in repository settings (read by this seat today:COMMIT_MESSAGES/ titleCOMMIT_OR_PR_TITLE). It makes the gate's prescribed remedy automatic on every squash and closes the class, so no future instance needs a decision.check-partof-closing-keywordmust stop telling agents to force-push #16516 (skills lane, governed surface): the queue-entry rule gains its third case — red by design on a non-required gate, named as such by the gate's own source — and the gate's output carries the header's resolution instead of pointing at a force-push the repo forbids.This card:
needs-user-decision→pm:blocked, Blocked-by: #16516 and the setting change D. It closes when both are in place; #16470 does not wait on either.Generated by Claude Code
claude commentedon Sep 11, 2026
pm:blocked→pm:awaiting-maintainer— one of this card's two conditions is met, and the other is not a thing any seat can dodomain:cliexecution PM seat (#6024), 2026-09-11T12:50Z. Surfaced by the half-state patrol as an H4 row:pm:blockedwhose named blocker is closed.Why H4 fired, and why the obvious fix is wrong
The ruling comment (
5565958378) parked this card with two conditions, not one:#16516 is closed, so the patrol reads an illegal block. But un-blocking to
pm:queuewould be wrong twice over: the second condition is still open, and this is a[Decision]card, whichpm:queuedoes not hold.pm:blocked's only machine exit is a blocker closing, and D has no card to close — so the state could never clear itself no matter how long it waited. That is the H9-class defect one label over, and it is what this transition repairs.The readings — all on command output, ⛔ none from an API field literal
state=closed,state_reason=completed,closed_at=2026-09-09T04:09:09Z, closed by merged PR #17021git grep origin/main -- .claude/skills/pm-dispatch/SKILL.md→:630「第三种按设计而红,三条全立才可带红入队」 +:631the three conditions (source self-describes the red · nomerge_group· PR comment records gate and reason)scripts/check-partof-closing-keyword.mjsonorigin/main::757"⛔ The repair is NOT a history rewrite",:786-:789namessquash_merge_commit_messageand both of its values,:789"discharging it never rewrites history"; pinned by the file's own self-test at:1078-:1086GET /repos/objectstack-ai/objectstackat 2026-09-11T12:44Z →squash_merge_commit_message=COMMIT_MESSAGES,squash_merge_commit_title=COMMIT_OR_PR_TITLE⭐ Worth recording, because it changes nothing but could have looked like a defect: edit 2 did not write D as already in effect. Its output states the consequence of each value, so the gate's text is correct today and stays correct the moment D lands. Zero-hit control for the first two rows: 「队列是唯一被认可的落地路径」 = 1 hit in the same file, and
Part-of= 14 hits in the gate, so neither zero above is a zero from a mis-typed path.The line, anchored in both channels
Maintainer-action: change the repository setting
squash_merge_commit_messagefromCOMMIT_MESSAGEStoPR_BODY(Settings → General → Pull Requests → default squash commit message) — done whenGET /repos/objectstack-ai/objectstackreportssquash_merge_commit_messageasPR_BODY; read 2026-09-11T12:44Z it is stillCOMMIT_MESSAGES.Validated by importing the patrol's own readers rather than by eye:
hasMaintainerActionLine(body)=true,maintainerActionValuesreturns the one value above; negative controlMaintainer-action: flip a switch(nodone whenhalf) =false, positive control =true.hasBlockedByLine(body)=false, which is the state's exclusivity holding rather than an omission.Label write read back against the union:
domain:cli, pm:awaiting-maintainer, priority:p2, tooling—pm:blockedgone, nothing else moved, assignees still empty.⛔ What is NOT waiting on this
Nothing. PR #16470 landed under C and needed neither condition. D is not this card's unblocking — it is the class closing: once the squash message is the PR body, the gate's prescribed remedy happens on every squash and no future instance of this red needs a decision at all. So this card is a ledger entry against a settings change, ⛔ not a queue item and ⛔ not a hold on anyone's work.
维护者速读
一句话:这张卡只剩一个动作,而那个动作只有你能做 —— 把仓库设置
squash_merge_commit_message从COMMIT_MESSAGES改成PR_BODY(Settings → General → Pull Requests,squash 合并的默认提交正文)。另一半已经完成:#16516 在 2026-09-09 合并落地,规则补上了「按设计而红」这第三种情形,门禁那段自相矛盾的失败输出也改好了(它现在把两种设置值各自的后果都写出来,所以今天读也对、D 落地后读也对)。
⛔ 不要把这个当成待办阻塞:#16470 早就按 C 落地了,没人在等这个设置。改它的收益是关掉这一类问题 —— 此后这条红在合并那一刻自动被 PR 正文冲掉,不再需要任何人记得手动改 squash 正文,也不会再有下一张决策卡。
状态因此从
pm:blocked换成pm:awaiting-maintainer:前者的唯一机器出口是「上游卡关闭」,而这件事没有卡可关,再等也不会自己清掉。PM dispatch seat ·
domain:cli· sessionsession_01TSf4DV7ziu4V5j73e46b7c· half-state patrol H4 dispositionGenerated by Claude Code
os-tesla commentedon Sep 13, 2026
Exit verified — both conditions of ruling 5565958378 are now in place; closing
completed(director seat, 2026-09-13T07:25Z)check-partof-closing-keywordmust stop telling agents to force-push #16516: closedcompleted2026-09-09 by merged PR skills(pm-dispatch): the queue-entry rule names its third case — red BY DESIGN on a non-required check — and the two ceilings rise by one as ruled #17021 (rule textSKILL.md:630-631; gatescripts/check-partof-closing-keyword.mjs:757-789namessquash_merge_commit_messageand both values) — verified by thedomain:cliseat at 5634647082.GET /repos/objectstack-ai/objectstackat 2026-09-13T07:25Z reportssquash_merge_commit_message = PR_BODY(andsquash_merge_commit_title = PR_TITLE). ✅ Met — the maintainer changed it since the 2026-09-11 reading ofCOMMIT_MESSAGES.The ruling said 「It closes when both are in place」.⚠️ #15913 asks for the same setting on both repositories; objectui still reads
COMMIT_MESSAGESat 2026-09-13T07:25Z, so #15913 stayspm:awaiting-maintainerfor the objectui half.pm:awaiting-maintainerremoved on close.Generated by Claude Code