Skip to content

fix(check-partof-closing-keyword): the already-pushed red says what discharges it — a merge whose squash message is the PR body — beside the rewrite ban - #16831

Merged
hotlong merged 2 commits into
mainfrom
claude/issue-16516-queue-entry-third-case
Sep 8, 2026
Merged

fix(check-partof-closing-keyword): the already-pushed red says what discharges it — a merge whose squash message is the PR body — beside the rewrite ban#16831
hotlong merged 2 commits into
mainfrom
claude/issue-16516-queue-entry-third-case

Conversation

@hotlong

@hotlong hotlong commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Part of #16516

This PR delivers the gate half of the card — item 2 of triage's re-scope (comment 5579821742): the BRANCH ALREADY PUSHED paragraph of scripts/check-partof-closing-keyword.mjs now states, positively, what discharges the red. The other half — the queue-entry rule's third case in .claude/skills/pm-dispatch/SKILL.md and its references/core-rules.md twin — is not in this PR: it stopped under the dispatch's 回翻条款 because it cannot be expressed in place or paid for by a fold in either ratcheted file (measurements below). It needs a ceiling decision that is the maintainer's, not a seat's; the full reading and the ready-to-land text are in the dev report on the card.

What changed — scripts/check-partof-closing-keyword.mjs only (+43 / −0)

Output. In judge(), the already-pushed paragraph — after …and it does not decide whether the pull request merges. — gains one paragraph:

  What DISCHARGES it is the merge, and only a merge whose squash message is the PR BODY: then the
  trailer above never reaches the default branch. Whether that happens by itself is the repository
  setting `squash_merge_commit_message`. At `COMMIT_MESSAGES` (the state fact 2 measured) it takes
  the lander replacing the assembled commit list with the PR body BY HAND at the merge button — a
  queue merge edits nothing, so there the residue lands. At `PR_BODY` every squash does it, queue
  included. Until that merge the red stays on this branch; ⛔ discharging it never rewrites history.

Header. The same statement, as the authority on detail, in the section RULE 2 — what the output may ask for, and when it may ask for nothing, directly under the measured-squash paragraph it conditions.

Self-test. Three pins in the RULE 2 battery (its ledger count is a floor, so no ledger edit): the discharge sentence prints on the already-pushed branch; the setting is named with BOTH of its values and the by-hand clause; and the sentence sits BESIDE ⛔ Do NOT amend, rebase or force-push, never in place of it.

Why this wording, and the setting-D probe

The card set the wording's timing on setting D (squash_merge_commit_message moving from COMMIT_MESSAGES to PR_BODY, the maintainer's own change ruled on #16502). Probed at claim time on origin/main 7862fb711 (re-read on 7f96e1417): the three newest multi-commit squashes — 68fd85a41 (#16812), 30b099078 (#16788), 094b8fd9c (#16775) — each begin their body with a * bullet followed by a commit subject, the COMMIT_MESSAGES shape; a PR_BODY squash would begin with the PR body's first line. D is not in effect. So the sentence takes the before-D form — the merge needs the PR body as its squash message, and today that is a by-hand act — but it is written as a CONDITION on the setting's value rather than as today's value, so it stays true once D is applied and no history is rewritten either way. The existing fact 2 (assembled from the COMMIT messages … 0a61db1f5, pinned) is untouched; the new paragraph names it as the measurement behind the COMMIT_MESSAGES value.

What is deliberately untouched

  • RULE 2's detection — commitRelations(), commitTrailerFindings(), the four counterfactual cases — byte-identical (the diff is 43 insertions, 0 deletions).
  • The three loci triage fenced off (on d4401f75b: the finding sentence at 594, the repair paragraph at 746, the guidance pins at 1017–1018) — each present exactly once, byte-identical, checked by content rather than line number.
  • The six existing guidance pins.

Verification

  • node scripts/check-partof-closing-keyword.mjs --self-test → exit 0, ✓ check-partof-closing-keyword self-test: 95 cases pass. (92 before, 3 new).
  • Ablation (from the committed state b93320823, blob fb822185aacb5f3887c3f1099eec8faf9fb3722f): the output paragraph deleted on disk (anchor What DISCHARGES it is the merge, and only a merge counted 1 → 0; git diff --stat HEAD = 7 deletions) → self-test exit 1, 3 of 95 case(s) failed, exactly the three new pins. Restored with git checkout HEAD -- scripts/check-partof-closing-keyword.mjs; git hash-object on the restored file = the HEAD blob above; git diff HEAD empty, porcelain empty; self-test 95/95 again.
  • Governed predicate: node scripts/pm/check-governed-merges.mjs --test scripts/check-partof-closing-keyword.mjs → exit 0, NOT governed (the diff no longer touches .claude/**; the seat routes the landing).
  • Mergeability, driverless: git merge-tree --write-tree from a bare --shared probe clone against origin/main 7862fb711 and again against 7f96e1417 (fix(pm): dispatch-gates' reconciliation line enumerates every OUTSIDE block, not three of five #16802, scripts/pm/dispatch-gates.mjs only) → exit 0, clean. origin/main 7862fb711 is merged into the branch; 7f96e1417 is not (it is disjoint from this file — §10 scoping).

Gate reconciliation

Derived in the worktree on the merged head 27bea86dc with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths; the change set is read from the merge base): 31 commands, identical before and after the merge. The seat's list (39 on d4401f75b, derived over three paths) carried eight more — check-governed-queue-guard --self-test, check:doc-formula-expressions, check:doc-authoring, check:pm-governed-merges, check:pm-governed-prose, check:pm-skill-id-lint, check:pm-skill-ratchet, check:skill-frame-sync — every one of them placed by the two .claude/skills/** paths this PR does not touch; they belong to the stopped half.

Every command was run from a script that captures the exit by redirect before any pipe (bash -c "$cmd" > log 2>&1; ex=$?), NODE_OPTIONS=--max-old-space-size=4096, no verify lock (no build or test — the diff touches no package):

  • 30 of 31: exit 0, each gate's own verdict line read from its log — among them ✓ check-partof-closing-keyword self-test: 95 cases pass., check-nul-bytes: OK (scanned 8302 text file(s) … no raw ASCII control bytes), ✓ check-self-test-wired: every one of the 191 script(s) …, check-closing-keyword-parity: OK (3 parsers agree …), ✓ comment-mask corpus sweep … 6356 files, 0 disagree, ✓ check-whole-set-label-write --self-test: all cases pass, check-ratchet-remedy-authority: 227 scripts swept ….
  • pnpm check:pm-dispatch-gates (exceeds the container's foreground cap; run detached with output to a file and awaited with tail --pid, restarted once on the merged tree, 09:03Z → 09:15Z): its own verdict line ✓ dispatch-gates self-test: 1552 cases pass., no signal branch printed.
  • node scripts/pm/dispatch-gates.mjs --ran over the run record: exit 0, ✓ dispatch-gates --ran: 31 derived famil(ies) accounted for — 31 run, 0 NOT-MEASURED.

Not in this PR's derived total and named as such by the tool: the always-runs tail, 10 whole-root families, 1 CI-measured family (this very script, read from the workflow event payload — its self-test is the local half and is green above), 3 workflow-valued families, and the artifact-roster families (silent on every card; not clearances).

Mirror reading

skills/objectstack-pm-dispatch/SKILL.md (the published mirror) carries no queue-entry rule — 0 hits for 入队 / 队列, and its queue mentions are the pm:queue backlog label; check:skill-frame-sync compares only the decision frame (COPIES: the internal and the published SKILL.md); check:pm-skill-id-lint scans .claude/skills/pm-dispatch/**, os-dev.md and AGENTS.md. Two references restate the all-green rule as pointers — references/review-checklist.md:43 and references/true-green.md:3 — no gate holds them to SKILL.md's wording; they become the follow-up's concern when the third case lands.

验收备注

  • noted, not filed: review-checklist.md:43 and true-green.md:3 restate 「入队资格 … 全 check 绿」 without the third case; a pointer, not a gated mirror — 承接者: the follow-up PR that lands the two skill loci of this card.
  • noted, not filed: dispatch-gates.mjs --commands printed STALE TREE … 1 file(s) it derives from CHANGED on 27bea86dc immediately after merging 7862fb711; the file was scripts/pm/dispatch-gates.mjs itself, moved by fix(pm): dispatch-gates' reconciliation line enumerates every OUTSIDE block, not three of five #16802 one commit later — correct behaviour, recorded so the seat does not read it as a derivation defect. 承接者: 无.
  • noted, not filed: the gate's fact 2 (assembled from the COMMIT messages, not from the PR body, pinned with 0a61db1f5) reads as a live fact today and as a dated measurement once D lands; the new paragraph conditions it on the setting's value, so no edit is owed now. 承接者: whoever applies setting D reads the paragraph, not the fact.

维护者速读(草稿)

改了什么:check-partof-closing-keyword 门禁在「分支已推送」那段输出里补了一句正面陈述:这个红由合并化解,且只有 squash 提交信息取 PR 正文的合并才化解;仓库设置 squash_merge_commit_messageCOMMIT_MESSAGES 时要人在合并按钮手动换成 PR 正文(队列合并不改信息,残留会落地),在 PR_BODY 时每次 squash 自动完成;两种情况都不改写历史。文件头同步一段,自测加三条钉子。

为什么改:此前输出只说「什么都清不掉、⛔ 不许改写历史」,没说它到底怎么被清掉;读到红的 agent 不知道该做什么、也不知道可以什么都不做。措辞按设置值写成条件句,D 生效前后都成立。

风险与代价(含回滚):只加不删(+43/−0),检测逻辑与四条反事实逐字未动;三处已落地成果逐字未动。回滚 = revert 这一个提交。⚠️ 本 PR 只交付卡的门禁那一半;入队规则第三种情形那一半(SKILL.md + core-rules.md)实测无法在既有行内表达、两文件均无可折并的相邻行(601 对与 121 对实测 0 对 ≤120 字节),需要天花板裁决,已在卡上报告。

席位意见:(留空)

你要做的:一个动作 —— 对卡上报告里的天花板问题作裁决(两文件各 +1 行,还是别的路);设置 D 仍未生效,顺手改了这一半就自动化解。

🤖 Generated with Claude Code

Generated by Claude Code


Generated by Claude Code

…h red, beside the rewrite ban

The already-pushed paragraph said that no author action clears the red
and forbade the rewrite, but never what does clear it, so a reader was
left knowing only what not to do. It now states the discharge as a
CONDITION on the repository setting: a merge whose squash message is the
PR BODY is what keeps the trailer out of the default branch; at
`COMMIT_MESSAGES` that takes the lander replacing the assembled commit
list with the PR body by hand at the merge button (a queue merge edits
nothing, so there the residue lands); at `PR_BODY` every squash does it,
queue included. Worded on the setting's value rather than today's value,
so it stays true after the ruled setting change is applied. The header
carries the same paragraph as the authority on detail.

Detection is untouched: RULE 2 and its four counterfactuals are
byte-identical, as are the finding sentence, the repair paragraph and
the six existing guidance pins. Three new pins hold the discharge
sentence, the setting and both of its values, and that the sentence
sits BESIDE the prohibition rather than in place of it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P58euzUXCVJNwmhuPC9DXY
@github-actions github-actions Bot added the size/s label Sep 8, 2026
@hotlong hotlong added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 8, 2026 — with Claude
@hotlong
hotlong marked this pull request as ready for review September 8, 2026 09:54
@hotlong
hotlong enabled auto-merge September 8, 2026 09:54
@hotlong
hotlong added this pull request to the merge queue Sep 8, 2026
Merged via the queue into main with commit 8b10e8b Sep 8, 2026
38 of 39 checks passed
@hotlong
hotlong deleted the claude/issue-16516-queue-entry-third-case branch September 8, 2026 10:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/s 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