Skip to content

chore(claude): deny the enqueue class and update_pull_request in .claude/settings.json (objectui#9586) - #10214

Merged
huangyiirene merged 1 commit into
mainfrom
claude/issue-9586-settings-deny-auto-merge-mirror
Sep 23, 2026
Merged

huangyiirene merged 1 commit into
mainfrom
claude/issue-9586-settings-deny-auto-merge-mirror

Conversation

@huangyiirene

@huangyiirene huangyiirene commented Sep 22, 2026

Copy link
Copy Markdown
Collaborator

Fixes #9586

GOVERNED — this pull request is a DRAFT and stays one. The changed instruction-tree path is governed surface in this repository. Asked of the guard itself, in this worktree:

$ node scripts/check-governed-queue-guard.mjs --test .claude/settings.json .changeset/issue-9586-enqueue-class-deny-mirror.md
⛔ GOVERNED — 1 of 2 path(s) are on a governed surface:
   .claude/** x1 — the agent instruction tree (skills, hooks, settings)
     - .claude/settings.json

   One governed path governs the WHOLE pull request — proportion is not a question.
   ⛔ Do not flip it ready, enqueue it, or arm auto-merge. Park it as a DRAFT and leave the merge
      to the maintainer; a human merge IS the review record for a governed surface.
   The merge-queue run of "Governed Surface Queue Guard" refuses this diff unless an APPROVED review by an
   authorized approver (GOVERNED_APPROVERS: os-zhuang, hotlong) is on the pull request — on
   whichever commit it was left (maintainer ruling 2026-09-04).
(exit 3)

⇒ ⛔ never flipped ready, ⛔ never enqueued, ⛔ never armed with auto-merge, ⛔ never approved by a seat under any account. Under ruling C the claiming seat lands it only once an authorized APPROVED review exists.

维护者速读(草稿)

改了什么

一个文件、三行:.claude/settings.jsonpermissions.deny 从 14 条加到 17 条,新增点名三个 MCP GitHub 工具 —— update_pull_requestenable_pr_auto_mergedisable_pr_auto_merge。改完之后,本仓的 deny 数组与 objectstack 的逐元素相同、顺序也相同allowhooks 两块字节未动。另附一个空 frontmatter 的 changeset(声明「不发版」,本仓惯例)。

为什么改

这是 objectstack 已裁决条款的跨仓镜像,不是本仓的新主张。objectstack#18282 裁决 A(批次 #135 追认)把「声明即强制」扩到入队类:席位在两个板上真正用的入队通路只有 REST 的 PUT .../pulls/{n}/ccr/auto_merge(它就在同一个文件的 allow 里),所以把 MCP 的那两个入队工具关掉,不损失任何能力,只是把一条没人走的旁路封死。

第三条 update_pull_request 是本仓自己的事故教训:objectui#6183 里,一次只传 reviewers 的 MCP 调用顺带把 PR 的 draft: false 设了,于是一个受管草稿进了合并队列、零人工批准就落了地。本仓当时只镜像了 14 条内容写入类,这一条(objectstack 在 PR #18276 落地)和入队那两条(PR #18317)都没跟上 —— 本次一并补齐。

风险与代价(含回滚)

  • 代价:任何还想用 MCP 去翻 ready、挂/摘 auto-merge 的流程会被运行时拒绝。这是有意的。gh pr readygh pr merge --auto 与 REST 的 ccr 路径都不受影响。
  • ⚠️ 已知代价,需要你知道:AGENTS.md 里有两处配方文本仍然点名这两个工具(自行合并那条的「MCP 等价物」一行,以及受管面段「发现自己已经挂上了怎么办」那条补救 —— 后者写的是「只调 disable_pr_auto_merge …… 两个都要做」)。本卡的文件面只有 .claude/settings.json,且 AGENTS.md 当前被两个在途 PR 持有(docs(agents): state the attribution-footer append as conditional on absence #10212 / docs(agents): 一次派发跑出来的 PR 停在 draft —— 给自行合并条款加第二条例外 #10213),所以本 PR ⛔ 不碰它,改由席位另立卡。方向是安全的一侧:配方失效会响亮地被拒,不会静默做错事。
  • 回滚:git revert 单个 commit 即可,无数据迁移、无构建产物、无已发布包字节变化。

席位意见

(留空,交席位定稿)

你要做的

确认「入队类 MCP 工具一并关掉」这个方向是你要的(objectstack 那边已经这么定了),然后在本 PR 上留一条 APPROVED review。批准之后由认领席翻 ready、入队落地,你不需要自己合并。上面那条 ⚠️ 只需要你知道,不需要你在这张卡里处理。

前提核验(动手前,在 origin/main 0cf2d6644 上实测)

# 前提 读数 结论
P1 卡片说本仓 deny 有 14 条 mcp__github__*,且三条都不在 实测 deny 总数 14,全部是 mcp__github__*;三条各命中 0 次,正控 issue_write 命中 1 次 成立
P2 本仓有没有 deny 名册门禁(objectstack 的 scripts/pm/check-settings-deny-roster.mjs) 不存在;scripts/pm/ 全部内容是 board-snapshot.mjscheck-half-states.mjs;全仓 grep settings.json,scripts/.github/ 内零个读取者 无 ⇒ ⛔ 不移植该门禁
P3 本仓有没有通道对照表(objectstack 的 references/rest-channel.md) .claude/ 下不存在任何 references 目录;.claude/ 全部文件共 14 个(4 个 hook + 4 个 selftest + launch.json + settings.json + 2 个 skill 的 4 个 md) 无 ⇒ 那一行事实无处可加
P4 .claude/settings.json 是不是被 pin 的上游移植文件(改它会红 check:upstream-port-parity) 不是。scripts/upstream-port-pin.json 13 条,.claude/ 下只 pin 了 8 个 hook/selftest 文件 可以改;该门禁仍已跑,绿
P5 并发:有没有别的在途 PR 碰这个文件 7 个 open PR 的文件清单逐一读过,零个碰 .claude/settings.json;#10212 / #10213 只有 AGENTS.md(hunk 分别在 468 与 323 行区) 无冲突

执行判据(卡片的那一条)

推送之后,GET /repos/objectstack-ai/objectui/contents/.claude/settings.json?ref=claude/issue-9586-settings-deny-auto-merge-mirror 解码后的 permissions.deny(HTTP 200,blob sha 30c3367037de7ba000673e4da8caa2913a9c704c,size 2255):

 1 mcp__github__issue_write
 2 mcp__github__create_pull_request
 3 mcp__github__update_pull_request          ← 新增(objectstack PR #18276 的镜像)
 4 mcp__github__add_issue_comment
 5 mcp__github__add_comment_to_pending_review
 6 mcp__github__add_reply_to_pull_request_comment
 7 mcp__github__pull_request_review_write
 8 mcp__github__push_files
 9 mcp__github__create_or_update_file
10 mcp__github__delete_file
11 mcp__github__create_branch
12 mcp__github__sub_issue_write
13 mcp__github__merge_pull_request
14 mcp__github__create_repository
15 mcp__github__fork_repository
16 mcp__github__enable_pr_auto_merge         ← 新增(本卡的判据)
17 mcp__github__disable_pr_auto_merge        ← 新增(本卡的判据)

两条判据条目都在 ⇒ 判据满足。

改动的形状,实测而非声称

⛔ 没有手打重排。两处锚点文本替换,锚点各自唯一,其余字节不动:

deny  base: 14  branch: 17  objectstack: 17
branch deny === objectstack deny (order included): true
allow identical to origin/main: true   sha 46560076ce2be885 = 46560076ce2be885
hooks identical to origin/main: true   sha 10e6c3a3ae991607 = 10e6c3a3ae991607
top-level keys / permissions keys: 与 origin/main 相同,无增删
added:   ["mcp__github__update_pull_request","mcp__github__enable_pr_auto_merge","mcp__github__disable_pr_auto_merge"]
removed: []
duplicate entries: 0
git diff --numstat: 4 1 .claude/settings.json

顺序惯例是按类分组、不是字母序(实测:objectstack 把 update_pull_request 紧挨 create_pull_request、把入队对追加在末尾),本 PR 照同样的位置插入,结果两仓数组完全一致。

两个读者都能解析:node JSON.parse OK · python json.load OKsettings.json 里 wire 的 4 个 hook 命令全部落在磁盘上(4/4 OK)—— 这个文件正是点名它们的那一个。

门禁

门禁族由本仓自己推导:objectui 没有 scripts/pm/dispatch-gates.mjs(不存在),所以逐个读 .github/workflows/*.yml 的触发条件,取「会判到这两个路径」的全集。

门禁 exit 判定行
JSON 有效性 0 node JSON.parse OK · python json.load OK
check-control-bytes(commit 后重跑) 0 ✅ check-control-bytes: OK (scanned 8217 tracked text file(s); skipped 85 binary).
check-governed-queue-guard --self-test 0 OK check-governed-queue-guard self-test: 185 cases pass
check-governed-queue-guard --test 3 本文顶部那段(受管,exit 3 是本 PR 的健康读数)
check-changeset-presence 0 ✅ No source or published contract of a released package changed in this range, so no changeset is owed.
check-changeset-no-major 0 ✅ No changeset declares a major bump.
check-changeset-overwrite 0 ✅ No pre-existing changeset was modified or deleted.
check-lint-coverage 0 ✅ lint coverage: 46/46 packages linted, 0 with outstanding errors (0 total).
check-entry-guard --self-test / 全跑 0 / 0 63 cases pass · 108 scripts/ file(s) — no entry guard outside the baseline
check-upstream-port-parity --self-test / 全跑 0 / 0 66 cases pass · 13 ported file(s) match objectstack-ai/objectstack modulo their declared divergences
check-bash32-floor --self-test / 全跑 0 / 0 160 cases pass · 13 tracked shell file(s) … name no bash 4+ construct
check-skills-paths 0 ✅ check-skills-paths: OK (88/89 stated path(s) resolve across 20 guide file(s); 1 baselined).
check-new-cross-file-line-citations 0 VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0

退出码一律在任何管道之前捕获(先重定向到文件,再读 $?)。

不欠的两族,连同不欠的理由: hook-selftests.yml 的 paths 是 .claude/hooks/** 与它自己 —— 本 diff 不碰 hook 文件(hook 命令仍被 settings.json 点名,所以单独验了 4/4 落盘)。check-shell-escape-residueSCAN_ROOTSAGENTS.mdCLAUDE.mdskills.claude/skillscontent/docs 五条,.changeset/** 不在其中 ⇒ 新增的 changeset 不在它的总体里。

lint 与 type-check:一次申报过的收窄,不是一次漏跑。 三条读数里的两条来自仪器,第二条被替换,如实写明:

  1. 总体读自 eslint 自己的配置:eslint.config.js 里每一个 files 块都限定在 TS/TSX(**/*.{ts,tsx}scripts/**/*.{ts,tsx}**/*.test.{ts,tsx}**/__tests__/**/*.{ts,tsx}**/examples/**/*.{ts,tsx}**/*.tsx 及三个具名单文件),没有 JSON 块,也没有 markdown 块。
  2. 文件数:⚠️ 本次不是读自 --format json,而是读自 diff 的扩展名普查 —— 本分支 git diff --name-only 两个文件,.json.md 各一,.ts/.tsx/.js/.mjs/.cjs 命中 0 ⇒ 落在 eslint 总体里的文件数必然为 0。这个容器里 objectui 没有 node_modules(共享检出与本 worktree 均无),而为一个 JSON + markdown 的 diff 做一次全仓 pnpm install 是这台共享机器上最贵的动作、且买不到任何判定。⇒ 这一格的仪器读数记 NOT MEASURED,理由如上;推翻它只需要一个 .ts 文件,而本 diff 没有。
  3. 对未触碰文件的不变性:type-aware linting 全仓未启用(eslint.config.js 里没有任何块设 parserOptions.projectprojectService)⇒ 本 diff 移不动任何它没触碰的文件的判定。同一读数覆盖 type-check:tsc 的输入字节未变。

仓级 pnpm lint / pnpm type-check 农场是 CI 的运行。⚠️ 并且它在本 PR 上跑满:lint.yml 的相关性判断只排除 **/*.mdcontent/**docs/**.changeset/**,而 .claude/settings.json 不在排除列里 ⇒ should_run=true,安装之后的那半边门禁由 CI 如常判定。

上述读数取自本分支最终 commit 45f22049

Acceptance notes

顺路看到、⛔ 本 PR 一律没动的:

  • AGENTS.md 有两处配方点名了本 PR 刚关掉的工具。 一处在「CI 全绿即自行合并」那条的代码块里(# MCP 等价物:pull request update(draft: false) + enable_pr_auto_merge),一处在受管面段的补救条(「只调 disable_pr_auto_merge 会摘掉 auto-merge 但不取消队列成员资格,两个都要做」)。本 PR 落地之后,照这两处文本行事的席位会撞上运行时拒绝。objectstack 那边的对应修复是改它的通道对照表那一行(「auto-merge 备用 MCP 未拒」→「auto-merge MCP 锁 1 同拒」);本仓的等价文本恰好落在 AGENTS.md 里,而 AGENTS.md 不在本卡的文件面内,且当前被 docs(agents): state the attribution-footer append as conditional on absence #10212 / docs(agents): 一次派发跑出来的 PR 停在 draft —— 给自行合并条款加第二条例外 #10213 两个在途 PR 持有。⇒ 交席位另立卡,本 PR 不碰。方向安全:失效的配方会被响亮拒绝,不会静默做错。
  • .changeset/issue-9418-write-identity-locks-port.md 里有一句已经过时的话。 那份(空 frontmatter、永不消费的)历史声明写着四个「状态形」工具 —— 含 update_pull_requestenable_pr_auto_merge —— 「stay allowed」。本 PR 之后其中两个不再 allowed。它是一份历史记录而非规程,读者为零,⛔ 不改、也不建议为它单独立卡;记在这里只是为了下一个读到那句话的人不被它误导。
  • check-governed-queue-guard.mjs 的拒绝文案仍写着「a human merge IS the review record」,而 AGENTS.md 已按裁决 C 改成「获授权批准后由认领席落地」。这条在 objectui PR docs(agents,settings): port the write-identity locks — deny MCP content writes, §9 states ruling C (objectui#9418) #9448 的 Acceptance notes 里已经报过一次,至今仍在;本 PR 只是再次撞见,不重复立卡。

Clause-②: no

本次实现所在的会话,写成散文以便在正文被改写后仍可追溯:session_01Wnstp2kTth7sGXfr8fXypc

席位意见(domain:skills seat 1,session session_01Wnstp2kTth7sGXfr8fXypc)

建议:批准并由席位落地。 一个文件三行,把 objectui 的 permissions.deny 补成与 objectstack 逐元素、同顺序相同(席位在复核工作树上解析两边数组比对为真);allow / hooks 未动;三条都在卡的裁决范围内(objectstack#18282 裁决 A、批次 #135 追认;update_pull_request 是卡正文点名的「本仓一并缺失」项)。不损失能力:两个板上席位真正用的入队通路只有 REST PUT …/ccr/auto_merge,就在同一文件的 allow 里。卡留给取卡者的两个可选项 —— 移植 deny 计数门禁、频道表加一行 —— 本树都不存在对应物,均未做(新增门禁默认否)。

你需要知道的一件事(不阻塞本 PR): 落地后 objectui AGENTS.md :331 与 :531 两处配方文字仍写着 enable_pr_auto_merge / disable_pr_auto_merge 作为 MCP 等价物,届时会被响亮拒绝而非静默执行错;两个在途 AGENTS.md PR(#10212 :468、#10213 :323)不在那两段,不能顺带。席位身份过不了立卡门,已记到席位帖的维护者事项里,由你决定是否立卡。

复核记录:PR 评论 5778587633(PASS,在席按服务档渲染);ACCEPT 在卡 objectui#9586。CI 读数:42 latest-per-name check runs — 39 success, 3 skipped, 0 in progress, 0 other。


Generated by Claude Code


Generated by Claude Code

…ui#9586)

Mirror of objectstack PR #18317 (ruling A on objectstack#18282) plus the one
earlier entry this repo never received, objectstack PR #18276:

- mcp__github__update_pull_request  — arms a landing as a hidden side effect
- mcp__github__enable_pr_auto_merge  — the enqueue class
- mcp__github__disable_pr_auto_merge — the enqueue class

The deny array is now element-for-element identical to objectstack's, order
included. `allow` and `hooks` are byte-identical to origin/main. No capability
is lost: the only enqueue route either board uses is the REST proxy's
`PUT .../pulls/{n}/ccr/auto_merge`, already an `allow` entry in this file.

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

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 45f22049e02b3485e34c9475f1a3b29258700623

① Derived judgments

Rendered in-seat by the skills seat 1 session at the served tier the maintainer named on this date (the fable tier word at get_sessionexternal_metadata.last_served_model, re-read for this record; ruling record objectstack#7623 comment 5771798588; the constant on objectstack origin/main names it since PR #19684). The contract is objectui#9586's one executable criterion — the cross-repo mirror of objectstack#18282 ruling A (batch #135 追认: 「declared = enforced」 extended to the enqueue class), whose objectstack landing is PR #18317 — read against this repo's own tree, ⛔ not against the dev's narrative.

  • (a) The diff — measured by this seat on a review worktree at the head. One commit, single parent, origin/main 0cf2d6644 an ancestor. .claude/settings.json +4 / −1: permissions.deny 14 → 17 — mcp__github__update_pull_request inserted beside create_pull_request (index 2), mcp__github__enable_pr_auto_merge and mcp__github__disable_pr_auto_merge appended; nothing removed, no duplicate; the array after the change equals objectstack's permissions.deny on 106d4c8dd element for element, order included (this seat compared the two parsed arrays); allow, hooks, the top-level key set and the permissions key set unchanged; the file parses. One new changeset, .changeset/issue-9586-enqueue-class-deny-mirror.md, empty frontmatter — declares no release.
  • (b) The premise held on the base — measured. All three entries absent on 0cf2d6644 (14 mcp__github__* entries), as the card's filing read said; the seat's claim had assumed update_pull_request present — the claim was wrong on that detail, the card's text right, and the card names the three as the missing set, so the third entry is inside the card, ⛔ not widening.
  • (c) Nothing is lost by denying them. The seat's only enqueue route on either board is the REST PUT …/pulls/{n}/ccr/auto_merge, an allow entry in this same file; the two state-shaped tools the ruling did NOT close (actions_run_trigger, resolve_review_thread) stay out — negative controls read 0.
  • (d) The two optional halves the card left to the taker — both absent on this tree, both verified by this seat. No deny-roster gate: scripts/pm/ holds board-snapshot.mjs and check-half-states.mjs only, and no script under scripts/ or .github/ reads .claude/settings.json; the gate was ⛔ not ported (新增门禁默认否 — the dispatch said no, the card left it open). No channel table: objectui's .claude/ carries no references/ directory, so objectstack's rest-channel.md :53 fact line has no home here; nothing added.
  • (e) Gates — re-run by this seat on the review worktree at the head. JSON parse 0; check-governed-queue-guard.mjs --self-test 185 cases 0; --test on the two paths exit 3 GOVERNED (1 of 2 on .claude/** — the healthy reading); check-control-bytes 0 (8217 tracked files); check-changeset-presence 0 (no changeset owed; one declared anyway); check-changeset-no-major 0; check-changeset-overwrite 0; check-upstream-port-parity 0 (13 ported files; .claude/settings.json is not a pinned port); check-entry-guard 0; check-skills-paths 0; check-bash32-floor 0; check-lint-coverage 0; check-new-cross-file-line-citations 0. The four hook commands the file wires resolve on disk. pnpm lint / pnpm type-check were not run locally by the dev or the seat (no node_modules for objectui on this container) — the diff carries no TS / JS file, the eslint population is TS / TSX scoped, and CI runs both on this head. CI at the seat's read: 41 latest-per-name check runs — 29 success, 3 skipped, 9 in progress (Spec Main Shape Gate and the eight Test shards); the Vercel status success. The 四件套 waits for green.

② Semver level

None — no release. .claude/** is in no package's files[]; the changeset declares no release by an empty frontmatter, the convention this repo used on PR #9448 (the earlier mirror on the same file, which carried no skip-changeset label either — that label is read by nothing on this board).

③ Boundary flags

  • GOVERNED under objectui's register (.claude/**): draft, never flipped ready, never enqueued, never approved by a seat. The 四件套 is this seat's on green — ACCEPT on the card, needs-user-decision on the PR, 席位意见 in the body, review requested from GOVERNED_APPROVERS os-zhuang / hotlong; the seat lands it only on an authorized APPROVED review (ruling C).
  • Report 5778518389 on the card parses (os-dev-report first line); mcp_calls 0; two REST writes plus two pushes; no label write, no body PATCH by the dev; no pace refusal.
  • Dispatch bookkeeping: the dispatch file's stated size was a code-point count (the dev's 8680 is bytes) — the md5 matched, the binding check; the claim's 「beside the existing update_pull_request entry」 was false on the tree — recorded above at (b).
  • Out of scope, reported by the dev, ⛔ not fixed here (class b — a seat filing owed): objectui AGENTS.md :331 (the 自行合并 recipe's comment naming enable_pr_auto_merge as the MCP equivalent) and :531 (the remedy bullet naming disable_pr_auto_merge) will name denied tools once this lands — a stale recipe refused loudly, not executed wrongly; the two in-flight AGENTS.md PRs docs(agents): state the attribution-footer append as conditional on absence #10212 (:468) and docs(agents): 一次派发跑出来的 PR 停在 draft —— 给自行合并条款加第二条例外 #10213 (:323) sit on other regions, so neither is a carrier. The filing door bars this seat's User-type identity from filing; it goes to the maintainer's items on the seat post. Two acceptance notes, recorded not filed: the stale sentence in .changeset/issue-9418-write-identity-locks-port.md (four state-shaped tools 「stay allowed」 — two no longer do; a never-consumed declaration with zero readers), and the queue guard's refusal text still reading 「a human merge IS the review record」 while AGENTS.md moved to ruling C (already reported on PR docs(agents,settings): port the write-identity locks — deny MCP content writes, §9 states ruling C (objectui#9418) #9448).

Implemented-by: claude/issue-9586-settings-deny-auto-merge-mirror
Reviewed-by: session_01Wnstp2kTth7sGXfr8fXypc

VERDICT: PASS


Generated by Claude Code

@huangyiirene
huangyiirene marked this pull request as ready for review September 23, 2026 00:14
@huangyiirene
huangyiirene added this pull request to the merge queue Sep 23, 2026
Merged via the queue into main with commit 3bfcf44 Sep 23, 2026
44 checks passed
@huangyiirene
huangyiirene deleted the claude/issue-9586-settings-deny-auto-merge-mirror branch September 23, 2026 00:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

3 participants