chore(hooks): remove the client-side enqueue guard hook — the merge_group queue guard is the line - #18478
Conversation
Delete .claude/hooks/guard-governed-enqueue.sh and its self-test, drop both settings.json registrations that named it, and re-key the three PM gate headers that described it as a live control. The deny roster and every other hook registration are byte-identical; the merge_group queue guard (check-governed-queue-guard.mjs) remains the line. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wj1HUjzyeiBQ8atRf1ZhaL
…move-enqueue-guard-hook
维护者速读(终稿)总监席, 改了什么 删掉客户端入队守卫钩子 为什么改 你的裁决 B(「b」):真正拦住未批准受管 PR 入队的是 merge_group 里的 CI 守卫(09-01 实证守住)+ 你的批准点击;这个钩子只省一个队列周期,而且它盯的三种拼法合规席位一种都不用、今天被返工三遍。 风险与代价(含回滚) 风险:席位违规把未批准的受管 PR 挂 auto-merge 时,多烧一个队列周期 + 一条红条目,合并仍被拦。代价零。回滚 = revert 一个 squash 提交。 席位意见 建议批准。deny 表逐字节核过相同,五个保留钩子顺序不变,五份钩子自测全绿。 你要做的 一个动作:在本 PR 上 Approve。与 #18476 互不依赖,先批哪个先落哪个。 Generated by Claude Code |
|
Landing provenance (ruling C) — director seat, Governed surface ( Generated by Claude Code |
… list, report-only (ruling F) (objectstack-ai#18497) Fixes objectstack-ai#18472 `skip-changeset`: the diff is one file under `docs/audits/`, which no released package's `files[]` ships. ## What this is The gate census the maintainer's ruling **F** asked for, verbatim 「F」 on 「现在有各种门禁的开发在浪费我的各种时间,我们的项目是不是门禁用的太重了。有这个必要吗?」 — **one table, report-only**. No gate is removed, disabled or edited by this PR; the retirement batch is its own card(s) after the maintainer's one-word 「认可」 on the table. `docs/audits/gate-census-2026-09.md`: - **Inventories**: ① 182 check files (`scripts/**/check-*.mjs` 178 + `packages/*/scripts/check-*.mjs` 4) · ② 26 root `check:*` scripts that are not a thin alias (of 164; 138 collapse onto their file row) · ③ 10 hooks (5 PreToolUse guards + 5 self-tests; the base tree had 12 — objectstack-ai#18478 removed the enqueue guard on `main` after the base) · ④ 7 required contexts (read from `GET /repos/objectstack-ai/objectstack/rules/branches/main`) → **union = 225 rows**, the table has 225 rows. - **Verdicts**: **retire 59** (58 rule-derived + `retire (ruled)` for `check-reference-carrier-shape.mjs`, objectstack#18095 E) · **report-only 16** · **keep 150** (133 plain, 2 ruled — the governed queue guard and its context —, 5 too new to judge, 5 session hooks + 5 hook self-tests). - **Classes**: process 115 · contract 60 · correctness 50. - **Column 5 (real catches)**: sampled over the last 30 days of failed PR CI (2026-08-17..09-16): 1,230 failed runs in the eight PR-triggered gate workflows, 1,287 failed jobs read step-by-step, 1,287 annotation reads, 9 sampled job logs, attribution by step name, split by annotation tag where a step hosts several gates, counted as distinct PRs, PRs that edited the gate's own file excluded. Every zero is a measured zero; every `unmeasured` names why; no count is guessed. - **Three sections**: retirement list (per row: files to delete, root scripts to drop, workflow `file:line`, count of other references to fix; a nine-row CI-integrity sub-group presented as one decision), report-only list, method + limits (incl. what is outside the four inventories: 70 per-package `check:*`, 31 workflow-direct non-`check-*` steps, two inline workflow gates — the inline `Require a changeset` step alone reddened 107 PRs). - Zod validation inside `packages/spec` / the runtime is the product, out of scope, said in one line. Standing verdicts recorded as given: `check-governed-queue-guard.mjs` = `keep (ruled)`; `check-reference-carrier-shape.mjs` = `retire (ruled)`. ## Acceptance notes - Dispatch-derived gates (`node scripts/pm/dispatch-gates.mjs --commands docs/audits/gate-census-2026-09.md`, 17 families) all exit 0 on the merged tree except `pnpm check:cross-package-test-inputs` (exit 1, at the base and again on the merged tree): its finding names `packages/cli/test/init-created-files-summary.e2e.test.ts` walking `packages/spec/dist/` with no declared glob — not a path this diff touches (one docs file). Reported to the seat in the dev report for filing; not fixed here (scope rule). - `pnpm --filter @objectstack/lint run check:doc-formula-expressions` first answered exit 3 (PREREQUISITE NOT MET: `@objectstack/formula` and `@objectstack/lint` not built) — built both through the verify lock and re-ran: exit 0. - noted, not filed: the four inventories the card defines leave 31 workflow-direct gate steps and two inline workflow gates without a row; the census names them in Method so the union stays honest. 承接者: the director's retirement batch (it decides whether a ⑤ is worth a follow-up census). - noted, not filed: two of the 25 `Docs anchors resolve to real headings` step failures could not be attributed between `check-doc-anchors.mjs` and `check-system-context-census.mjs` (no tag in the annotation); recorded as `unmeasured` on the doc-anchors row. 承接者: none. - Drift: `origin/main` moved seven commits before this PR opened; the branch merged `bf61f0a1` and the census was re-derived on the merged tree (Drift paragraph in the file). objectstack-ai#18481 (the protocol flip: a census-marked retirement becomes class-1) landed in that range and is the other half of ruling F. - Local verification scope: no package is touched, so no build/test closure is owed; `packages/spec`, `@objectstack/formula` and `@objectstack/lint` were built only as prerequisites of the derived gates. Repo-wide scans (`pnpm lint`) are CI's. ## 维护者速读(草稿) **改了什么**:新增一份审计文档 `docs/audits/gate-census-2026-09.md`,把仓库里全部门禁放到一张表上:182 个 `check-*.mjs`、26 个非别名的根 `check:*` 脚本、10 个 `.claude/hooks` 文件、7 个 main 分支必过的 CI 上下文,合计 225 行。每行六列:名字、类别(contract / correctness / process)、守的是什么契约、是否必过与波及面、落地以来真实抓到过几次(近 30 天 1,230 次失败的 PR 运行逐步归因,去掉门禁自己的 PR)、结论。⛔ 本 PR 不删、不停、不改任何门禁。 **为什么改**:您在直连通道的裁决 **F**(「门禁是不是用的太重了」→ 先普查、再按表退役)。这张表就是普查结果:**59 行建议退役**(其中 1 行 `check-reference-carrier-shape` 是 objectstack-ai#18095 已裁 E),**16 行只是报告不拦合并**,**150 行保留**(含您已裁定保留的治理队列守卫)。波及面最大的三处:`check-changeset-no-major`(Clause-② 等级规则,30 天红了 **74 个 PR**)、`check-partof-closing-keyword`(**31 个 PR**,但它只在非必过的工作流里跑)、`check-system-context-census`(必过、全仓,**21 个 PR**)。表外还有一个最大的红源:`pr-automation.yml` 里的内联「Require a changeset」步骤,30 天红了 107 个 PR。 **风险与代价(含回滚)**:本 PR 只加一份文档,零运行时改动,回滚即删除该文件。真正的代价在下一步:退役清单里有 9 行是「CI 自身接线」的守卫(必过上下文名字钉、聚合器 needs 花名册、Node/pnpm 获取、self-test 接线等),按规则它们 30 天零抓获所以列为退役,但它们的价值是威慑而非抓获——表里把这 9 行单独成组,供您一次决定。抽样只有 30 天:一季度才响一次的门禁会读成零。 **席位意见**:(留空,席位定稿) **你要做的**:读表,认可则回一个字「认可」;若对某行有异议(尤其 CI 接线 9 行组、以及 5 个会话 hook 的保留),点名该行即可,director 会把退役批次按您的意见立卡。 --- _Generated by [Claude Code](https://claude.ai/code/session_01Wj1HUjzyeiBQ8atRf1ZhaL)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Fixes #18470
Executes maintainer ruling B on PR #18447 — reply verbatim 「b」, then 「这个卡你也直接派发」: remove the client-side enqueue guard hook entirely. The server-side line stays:
scripts/pm/check-governed-queue-guard.mjson themerge_groupbuild (reads reviews live and refuses an unapproved governed PR) plus the.claude/settings.jsondenyroster for the MCP enqueue class, plus the approver's click.What changed
.claude/hooks/guard-governed-enqueue.sh.claude/hooks/guard-governed-enqueue.selftest.sh.claude/settings.jsonenable_pr_auto_merge/merge_pull_request) removed — this hook was its only command; the oneguard-governed-enqueue.shcommand block removed from theBashmatcher. 13 lines, all removals.permissions(incl. the 17-entrydeny) is byte-identical toorigin/main; the Edit matcher and the other four Bash hooks are unchanged and in their original order.scripts/pm/check-governed-merges.mjs--teststderr note no longer names the hook as the stdout consumer (stdout stays a machine-read--jsonsurface for any caller). No code change.scripts/pm/check-governed-queue-guard.mjsauthorizedApprovalVerdictdocblock re-keyed: the importer note now records that the seat-side hook was removed by ruling and that the queue leg is this function's only consumer; the rename fence stays as "grep for importers in the same diff". Refusal logic untouched — 238 self-test cases pass unchanged.scripts/pm/check-settings-deny-roster.mjsCONTENT_WRITE_TOOLSuntouched — 36 self-test cases pass, 17 declared = 17 enforced.Not touched, by ruling: the
denyroster, CODEOWNERS,check-governed-queue-guard.mjs's refusal logic; and #18468's surface (.claude/skills/pm-dispatch/SKILL.md:92,scripts/pm/check-harness-current.mjs).Premise check against origin/main (BASE
8fe5cb8e)git grep -l guard-governed-enqueue origin/main→ exactly six files: the two hook files,.claude/settings.json,check-governed-merges.mjs,check-governed-queue-guard.mjs,check-settings-deny-roster.mjs. Neitherscripts/pm/dispatch-gates.mjsnorpackage.jsonnames the hook or its self-test: no family runs it, andlint.yml's hook self-test step discovers*.selftest.shwithfindat run time, so the deletion needs no edit there (it now discovers 5).Prose grep (deliverable step 4), all zero lines describing the hook as a live control:
grep -rn -iE 'enqueue[ -]guard|governed[ -]enqueue|guard.governed.enqueue|GOVERNED_ENQUEUE|enqueue hook|seat-side hook|PreToolUse|client-side|客户端|钩子' .claude/skills .claude/agents AGENTS.md CLAUDE.md docs/adr. The PreToolUse mentions inAGENTS.md(lines 221, 323) and.claude/agents/os-dev.md(line 27) are the worktree and stash guards;platform-readings.md:395names the CI contextGoverned Surface Queue Guard(server-side, stays);scripts/pm/check-half-states.mjssays "governed enqueue" three times about the queue guard's refusal, not the hook. So no SKILL.md, AGENTS.md or ADR line changes in this PR.Gates green by design — no exemption for a missing file, no weakened assertion
Acceptance:
git ls-files .claude/hooks/guard-governed-enqueue*→ 0;grep -rn guard-governed-enqueue .claude scripts AGENTS.md CLAUDE.md docs .github package.json→ 0 hits.node scripts/pm/dispatch-gates.mjs --commands(no paths — derived from the merge base) → 40 commands at9784cd23; identical list on the merged headafd9fcb2. Every command run with its exit code captured before any pipe (cmd > log 2>&1; ec=$?):pnpm --filter @objectstack/lint run check:doc-formula-expressionsexited 3 = PREREQUISITE NOT MET (@objectstack/formulaand@objectstack/lintunbuilt — nothing measured, not a finding); afterturbo run build --filter=@objectstack/formula --filter=@objectstack/lintunderos-verify-lock.sh(VERDICT command-exit 0, turbo cache hit) it re-ran at exit 0.dispatch-gates.mjs --ranonafd9fcb2: 40 derived, 40 run, 0 NOT-MEASURED, 0 UNRUN — "a DERIVED zero — all 40 recorded an exit code and none of them is 3".check:pm-settings-deny-roster— 36 self-test cases, 17 content-write tools declared = enforced;check:pm-governed-merges— 328 assertions;check-governed-queue-guard.mjs --self-test— 238 cases;check:pm-dispatch-gates,check:nul-bytes,check-harness-current.mjs --self-test,check:cross-package-test-inputs— exit 0;check:bash32-floor— exit 0, re-run on the merged head aftermainmoved that script (31 tracked shell files, none naming a bash 4+ construct).lint.ymldiscovers): 5 discovered, all pass — guard-main-checkout-bash 130, guard-main-checkout 120, guard-process-kill 69, guard-shared-stash 53, guard-tree-enum 38.origin/main(85c6d76e: two docs commits —check-bash32-floor.mjs+41, two SKILL.md lines):check:skill-frame-sync,check-skills-token-ratchet.mjs(and its--self-test),check:pm-dispatch-gatesre-run at exit 0 onafd9fcb2.check:pm-skill-ratchet/check:pm-skill-id-lint— no.claude/skills/**line changes in this diff (the SKILL.md ratchet stays 812/812).Lint, proven narrowing at
afd9fcb2: ① population read fromeslint.config.mjs— the base block lints**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}minusNEVER_LINTED;.claude/settings.jsonreports "File ignored because no matching configuration was supplied", and the two deleted.shfiles match no glob, so this diff's lint population is exactly the three.mjsfiles; ②--format jsoncount: 3 files, 0 errors, 0 warnings; ③ invariance: the config "never enables type-aware linting (noparserOptions.project, no typed@typescript-eslintrules) for ANY file" (its own line 327), so no untouched file's verdict can move with this diff. The repo-widepnpm lintis CI's run.Changeset
skip-changeset— route 2 incheck-empty-changeset.mjs: a PR touching only.claude/andscripts/releases nothing, and no packagefiles[]ships either surface. Label applied by RESTPOST /issues/NUMBER/labelsand read back.Acceptance notes
scripts/pm/check-half-states.mjs(lines 9377, 9557, 29094) says "governed enqueue" about the queue guard's refusal — still true after this PR, no edit. 承接者:无.9784cd23carries the model-free trailer pair;afd9fcb2is the merge oforigin/main..claude/**): this PR stays DRAFT; the dev seat flips nothing, requests nothing and arms nothing.needs:contract-reviewis the seat's.维护者速读(草稿)
改了什么
删掉客户端入队守卫钩子
guard-governed-enqueue.sh及其自检脚本,.claude/settings.json里的两处注册一并去掉;三个 PM 门禁脚本里把它描述为"在场控制"的注释改写成现状。deny名单、CODEOWNERS、check-governed-queue-guard.mjs的拒绝逻辑一字未动。为什么改
执行 PR #18447 上的裁决 B(维护者原话「b」,随后「这个卡你也直接派发」)。真正的硬线是
check-governed-queue-guard.mjs在merge_group构建上的拒绝,加上维护者的批准点击;这个钩子只省一个队列周期,而实装版本盯着三种合规席位根本不用的拼法,今天已经耗掉三个受管 PR。删除优于继续维护。风险与代价(含回滚)
风险:失去"批准前就在本地拦住"的便宜防线——违规入队会在队列里红一次,而不是在本地被拦;服务端正确性不变。代价:一次受管落地即一次 harness 重载(#18468 已把"重开"处方去掉)。回滚 = revert 本 PR 的 squash 提交,钩子与两处注册整体回来。
席位意见
(留空,席位定稿)
你要做的
一个动作:在本 PR 上 Approve;落地由拥有席位按受管流程执行。
Generated by Claude Code