Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

Commit 3bfcf44

Browse files
huangyiireneclaude
andauthored
chore(claude): deny the enqueue class and update_pull_request in .claude/settings.json (objectui#9586) (#10214)
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.json` 的 `permissions.deny` 从 14 条加到 17 条,新增点名三个 MCP GitHub 工具 —— `update_pull_request`、`enable_pr_auto_merge`、`disable_pr_auto_merge`。改完之后,本仓的 deny 数组与 objectstack 的**逐元素相同、顺序也相同**。`allow` 与 `hooks` 两块字节未动。另附一个空 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 ready`、`gh pr merge --auto` 与 REST 的 ccr 路径都不受影响。 - ⚠️ 已知代价,需要你知道:AGENTS.md 里有两处**配方文本**仍然点名这两个工具(自行合并那条的「MCP 等价物」一行,以及受管面段「发现自己已经挂上了怎么办」那条补救 —— 后者写的是「只调 `disable_pr_auto_merge` …… 两个都要做」)。本卡的文件面只有 `.claude/settings.json`,且 AGENTS.md 当前被两个在途 PR 持有(#10212 / #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.mjs` 与 `check-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 OK`。`settings.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-residue` 的 `SCAN_ROOTS` 是 `AGENTS.md`、`CLAUDE.md`、`skills`、`.claude/skills`、`content/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.project` 或 `projectService`)⇒ 本 diff 移不动任何它没触碰的文件的判定。同一读数覆盖 `type-check`:tsc 的输入字节未变。 仓级 `pnpm lint` / `pnpm type-check` 农场是 CI 的运行。⚠️ 并且它**会**在本 PR 上跑满:`lint.yml` 的相关性判断只排除 `**/*.md`、`content/**`、`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 **不在本卡的文件面内**,且当前被 #10212 / #10213 两个在途 PR 持有。⇒ 交席位另立卡,本 PR 不碰。方向安全:失效的配方会被响亮拒绝,不会静默做错。 - **`.changeset/issue-9418-write-identity-locks-port.md` 里有一句已经过时的话。** 那份(空 frontmatter、永不消费的)历史声明写着四个「状态形」工具 —— 含 `update_pull_request` 与 `enable_pr_auto_merge` —— 「stay allowed」。本 PR 之后其中两个不再 allowed。它是一份历史记录而非规程,读者为零,⛔ 不改、也不建议为它单独立卡;记在这里只是为了下一个读到那句话的人不被它误导。 - **`check-governed-queue-guard.mjs` 的拒绝文案仍写着「a human merge IS the review record」**,而 AGENTS.md 已按裁决 C 改成「获授权批准后由认领席落地」。这条在 objectui PR #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](https://claude.ai/code/session_01Wnstp2kTth7sGXfr8fXypc)_ --- _Generated by [Claude Code](https://claude.ai/code)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 8d7915e commit 3bfcf44

2 files changed

Lines changed: 25 additions & 1 deletion

File tree

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
---
3+
4+
`.claude/settings.json` only — no released package source and no published contract field is
5+
touched, so this declares **no release** with an empty frontmatter rather than a bump. The
6+
declaration is written anyway because this repo's rule asks for a declaration once, not for a
7+
release; `node scripts/check-changeset-presence.mjs` agrees on its own terms.
8+
9+
objectui#9586 mirrors the enqueue-class lock objectstack landed in objectstack#18317, and the
10+
one earlier entry this repo had also never received (objectstack#18276):
11+
12+
- `permissions.deny` gains `mcp__github__enable_pr_auto_merge` and
13+
`mcp__github__disable_pr_auto_merge` (the enqueue class, ruled closed on objectstack#18282)
14+
plus `mcp__github__update_pull_request`, which arms a pull request's landing as a hidden side
15+
effect of an unrelated field — the mechanism behind objectui#6183, where a governed draft
16+
entered the merge queue and landed with no human approval.
17+
- Nothing is lost by denying them: the only enqueue route either board actually uses is the
18+
REST proxy's `PUT .../pulls/{n}/ccr/auto_merge`, which is already an `allow` entry in this
19+
same file, and `gh pr ready` / `gh pr merge --auto` are untouched.
20+
- The result is that this repo's deny array is now element-for-element identical to
21+
objectstack's, order included — declared = enforced on both boards from one reading.

‎.claude/settings.json‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"deny": [
1818
"mcp__github__issue_write",
1919
"mcp__github__create_pull_request",
20+
"mcp__github__update_pull_request",
2021
"mcp__github__add_issue_comment",
2122
"mcp__github__add_comment_to_pending_review",
2223
"mcp__github__add_reply_to_pull_request_comment",
@@ -28,7 +29,9 @@
2829
"mcp__github__sub_issue_write",
2930
"mcp__github__merge_pull_request",
3031
"mcp__github__create_repository",
31-
"mcp__github__fork_repository"
32+
"mcp__github__fork_repository",
33+
"mcp__github__enable_pr_auto_merge",
34+
"mcp__github__disable_pr_auto_merge"
3235
]
3336
},
3437
"hooks": {

0 commit comments

Comments
 (0)