Repository navigation
skills(hooks): remove the client-side enqueue guard guard-governed-enqueue.sh — the merge_group queue guard is the line, the hook only ever bought a cycle (ruling B on PR #18447) #18470
Description
Activity
Claim: PM loop round 1 — maintainer direct dispatch to the director seat
Session:session_01Wj1HUjzyeiBQ8atRf1ZhaL
Branch:claude/issue-18470-remove-enqueue-guard-hook
Worktree:objectstack-issue-18470
Domain:domain:skills
File surface:.claude/hooks/guard-governed-enqueue.sh+.selftest.sh(deleted) ·.claude/settings.json(two hook registrations removed; ⛔denyroster untouched) · the rosters that name the hook onorigin/main:scripts/pm/check-governed-merges.mjs,scripts/pm/check-governed-queue-guard.mjs,scripts/pm/check-settings-deny-roster.mjs, andscripts/pm/dispatch-gates.mjsfamilies if any names the self-test · any prose line in.claude/skills/**/AGENTS.mdthat describes the hook as a live control (stop on breach; explain in the report)
Container & model:M,mode:subagent,model: claude-fable-5-1—dispatch-gates.mjs --tieron this surface: 「no path-derived mandate」; the director dispatches at the session's own tier (a governed.claude/**change whose review the director takes in-seat at that tier), no downgrade exit taken
Clause-②: no
Thread-read: none
Serial constraints cleared: objectstack#18468 (dispatched by this seat, branchclaude/issue-18468-harness-current-report-only) touchesSKILL.md:92andscripts/pm/check-harness-current.mjsonly — disjoint from this surface; PR #18447 (the last PR on these hook files) is CLOSED unmerged at 14:22Z under ruling B;os-verify-lock.sh --statusreads no holder, no tickets; last landings on this surface #18423 / #18390 / #18365 / #18317 all MERGEDAuthority for a director claim: the maintainer's direct-dispatch order on this session's channel, verbatim — 「b」 (ruling B on PR #18447) then 「这个卡你也直接派发」 (2026-09-16 ~14:2xZ).
domain:skillsset in the same write under that order (surface =.claude/**+ PM gate rosters, anchored todomain:skillsby the lane table).
Generated by Claude Code
os-dev-report
{ "issue": 18470, "status": "done", "branch": "claude/issue-18470-remove-enqueue-guard-hook", "pr": "https://github.com/objectstack-ai/objectstack/pull/18478", "session": "session_01Wj1HUjzyeiBQ8atRf1ZhaL", "premise_still_valid": true, "summary": "Executed ruling B on PR #18447 (verbatim 「b」): deleted .claude/hooks/guard-governed-enqueue.sh and .selftest.sh, removed both settings.json registrations (the standalone MCP-enqueue matcher entry whose only command was this hook, and the one command block in the Bash matcher; permissions incl. the 17-entry deny roster byte-identical to origin/main, other hooks unchanged and in order), and re-keyed the four comment sites in check-governed-merges.mjs (2), check-governed-queue-guard.mjs (1, refusal logic untouched) and check-settings-deny-roster.mjs (2 paragraphs, roster untouched) so each gate is green by design with no exemption and no weakened assertion. Premise verified on origin/main at BASE 8fe5cb8e: git grep -l guard-governed-enqueue origin/main = exactly the six files the card names; dispatch-gates.mjs and package.json name neither the hook nor its self-test; lint.yml discovers *.selftest.sh with find at run time (now 5). Step 4 prose grep over .claude/skills, .claude/agents, AGENTS.md, CLAUDE.md, docs/adr found zero lines describing the hook as a live control, so no SKILL.md/AGENTS.md/ADR line changed (SKILL.md:92 and check-harness-current.mjs untouched). Claim comment verified naming this branch; assignee was already set by the dispatch. Draft PR #18478 opened against main, first line Fixes #18470, skip-changeset applied and read back, 维护者速读(草稿) present; governed surface, left DRAFT, nothing flipped or armed. Deviation from the dispatch's Writes list: the standard clauses' POST /issues/NUMBER/labels write (skip-changeset) was made and is reported below.", "tests": "Gates derived with node scripts/pm/dispatch-gates.mjs --commands (no paths; from merge base): 40 commands at 9784cd23, identical list at merged head afd9fcb2; every command run with exit captured before any pipe (cmd > log 2>&1; ec=$?). 39/40 exit 0 first pass; pnpm --filter @objectstack/lint run check:doc-formula-expressions exit 3 = PREREQUISITE NOT MET (formula/lint unbuilt, nothing measured) then exit 0 after turbo run build --filter=@objectstack/formula --filter=@objectstack/lint under os-verify-lock.sh (VERDICT command-exit 0, cache hit). dispatch-gates --ran with per-row exit codes on afd9fcb2: '40 derived, 40 run, 0 NOT-MEASURED, 0 UNRUN — a DERIVED zero — all 40 recorded an exit code and none of them is 3'. Named: check:pm-settings-deny-roster '36 cases pass' + '17 content-write tool(s) declared = enforced' (exit 0); check:pm-governed-merges '328 assertions' (exit 0); check-governed-queue-guard.mjs --self-test '238 cases pass' (exit 0); check:pm-dispatch-gates 0; check:nul-bytes 0; check-harness-current.mjs --self-test 0; check:cross-package-test-inputs 0; check:bash32-floor 0 (re-run on afd9fcb2 after main moved that script: '31 tracked shell file(s)'). Hook self-tests (lint.yml's find set): 5 discovered, all exit 0 — guard-main-checkout-bash '130 passed, 0 failed', guard-main-checkout '120 passed, 0 failed', guard-process-kill '69 passed, 0 failed', guard-shared-stash '53 passed, 0 failed', guard-tree-enum '38 passed, 0 failed'. After merging origin/main 85c6d76e (check-bash32-floor.mjs +41, two SKILL.md lines): check:skill-frame-sync, check-skills-token-ratchet.mjs and --self-test, check:pm-dispatch-gates re-run exit 0 on afd9fcb2. Not run: check:pm-skill-ratchet / check:pm-skill-id-lint (no .claude/skills line changed). Lint proven narrowing at afd9fcb2: population from eslint.config.mjs base block **/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs} minus NEVER_LINTED; .claude/settings.json is 'File ignored because no matching configuration was supplied' and the deleted .sh files match no glob, so this diff's population is exactly the 3 .mjs files; --format json count 3 files, 0 errors, 0 warnings; invariance: config line 327 'never enables type-aware linting (no parserOptions.project, no typed @typescript-eslint rules) for ANY file'. Repo-wide pnpm lint is CI's. Acceptance greps: 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. Control-char scan on the 4 edited files clean. No ablation: no new assertion was added (removal + comment re-keys only). Not measured locally: CI's Test Core shard job and the 11 wide-population / 48 artifact-roster families dispatch-gates names as CI-owned; CI status at report time in_progress.", "gates": "check:pm-settings-deny-roster=0, check:pm-governed-merges=0, check-governed-queue-guard.mjs --self-test=0, check:pm-dispatch-gates=0, check:nul-bytes=0, check:bash32-floor=0 (merged head), check-harness-current.mjs --self-test=0, check:cross-package-test-inputs=0, check:doc-formula-expressions=3 then 0 after prerequisite build, remaining 31 derived commands=0, hook self-tests 5/5=0, dispatch-gates --ran 40/40/0 NOT-MEASURED; narrowed eslint=0 (3 files)", "files_changed": ".claude/hooks/guard-governed-enqueue.sh (deleted), .claude/hooks/guard-governed-enqueue.selftest.sh (deleted), .claude/settings.json (-13, hook registrations only), scripts/pm/check-governed-merges.mjs (comments), scripts/pm/check-governed-queue-guard.mjs (docblock), scripts/pm/check-settings-deny-roster.mjs (header)", "grep_step3": "git grep -l guard-governed-enqueue origin/main = .claude/hooks/guard-governed-enqueue.selftest.sh, .claude/hooks/guard-governed-enqueue.sh, .claude/settings.json, scripts/pm/check-governed-merges.mjs, scripts/pm/check-governed-queue-guard.mjs, scripts/pm/check-settings-deny-roster.mjs (6); scripts/pm/dispatch-gates.mjs and package.json: 0 hits", "grep_step4": "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 = 0 lines describing the hook as a live control (AGENTS.md:221/:323 and os-dev.md:27 PreToolUse = worktree and stash guards; platform-readings.md:395 = the CI context Governed Surface Queue Guard, server-side; check-half-states.mjs 9377/9557/29094 'governed enqueue' = the queue guard's refusal)", "deviations": "1) dispatch's Writes list omitted the labels POST; the standard clauses require it and the dispatch's PR section requires skip-changeset, so POST /issues/18478/labels was made (1 write) and read back. 2) origin/main moved during the run (8fe5cb8e to 85c6d76e); merged it (afd9fcb2, clean, no regen debt) and re-ran the moved families on the merged head. 3) Second half of the gate sweep exceeded the foreground cap and was backgrounded by the harness; its per-command exit codes were still captured to file and read from there.", "mcp_calls": "0 — no MCP GitHub tools called; all GitHub reads and writes via REST curl with GITHUB_TOKEN", "api_writes": "4 — git push ×3 on the branch (empty-branch probe, 9784cd23, afd9fcb2); POST /repos/objectstack-ai/objectstack/pulls ×1 (draft #18478); POST /repos/objectstack-ai/objectstack/issues/18478/labels ×1 (skip-changeset, read back present); POST /repos/objectstack-ai/objectstack/issues/18470/comments ×1 (this report); PATCH /pulls ×0", "open_questions": [], "out_of_scope_findings": [ "noted, not filed: scripts/pm/check-half-states.mjs lines 9377/9557/29094 say 'governed enqueue' about the queue guard's refusal — still true after the removal, no edit; 承接者:无" ] }
Generated by Claude Code
ACCEPT — director seat,
session_01Wj1HUjzyeiBQ8atRf1ZhaL, served tierclaude-fable-5-1· 2026-09-16T15:03ZVerified against GitHub (PR #18478, head
afd9fcb2, draft, 6 files +24/−1288), ⛔ not against the report:- Scope = ruling B, nothing wider. Deleted
.claude/hooks/guard-governed-enqueue.shand.selftest.sh..claude/settings.json−13: the standalone PreToolUse matchermcp__github__enable_pr_auto_merge|mcp__github__merge_pull_request(whose only command was this hook) and the hook's entry in theBashmatcher — hook commands 7 → 5 (guard-main-checkout,guard-shared-stash,guard-process-kill,guard-main-checkout-bash,guard-tree-enumremain, in order).permissionsincl. the 17-entrydenyroster byte-identical toorigin/main(compared as parsed JSON); every other top-level key identical. scripts/pm/check-governed-merges.mjs(2 comment sites),check-governed-queue-guard.mjs(docblock only; themerge_grouprefusal logic untouched),check-settings-deny-roster.mjs(header) re-keyed to say the client hook is gone and the roster + queue guard are the line — read whole; no assertion weakened, no exemption added.- Prose grep for the hook as a live control across
.claude/skills,.claude/agents,AGENTS.md,CLAUDE.md,docs/adr= 0 (the remainingPreToolUsementions are the worktree/stash guards;platform-readings.md:395names the server-side CI context) — accepted as the reading; no SKILL.md line changed ([Decision] check-harness-current's prescription is all-or-nothing — one reworked hook forced every live PM session to re-seat three times in nine hours; narrow it by which path the harness actually loads at start #18468's :92 and skills(pm-dispatch): flip the gate asymmetry — adding a required gate / hook / ratchet joins the human floor; retiring a zero-catch, no-contract gate becomes class-1 (maintainer ruling F) #18473's floor lines untouched). - Gates: 40 derived / 40 run / 0 unrun (
--ran, DERIVED zero), all exit 0 after one prerequisite build;check:pm-settings-deny-roster36 cases + 17 declared = enforced;check-governed-queue-guard --self-test238; hook self-tests 5/5 (lint.yml discovers them byfind, now 5);mcp_calls0;api_writes4 REST + 3 pushes, read back;skip-changesetread back; main moved mid-run and was merged (85c6d76e), moved families re-run on the merged head. - CI on
afd9fcb2at review: 17 success · 11 skipped · 5 in progress · 0 failed. - Deviation answered: the labels POST is the dev file's standard budget — accepted.
Landing (governed, ruling C): four-piece hung on PR #18478 — this record, 维护者速读(终稿),
needs-user-decision, review requested from an approver account; draft until an authorized APPROVED, then the seat flips ready and arms auto-merge via the ccr routes. Card stayspm:dispatcheduntil MERGED. Sequencing: independent of PR #18476 (disjoint files); land whichever is approved first.Implemented-by:
claude/issue-18470-remove-enqueue-guard-hook(mode:subagent)
Reviewed-by:session_01Wj1HUjzyeiBQ8atRf1ZhaL
Generated by Claude Code
- Scope = ruling B, nothing wider. Deleted
Landed — director seat,
session_01Wj1HUjzyeiBQ8atRf1ZhaL· 2026-09-16T15:58ZPR #18478 (head
afd9fcb2) merged by the queue as674872a5b7at 2026-09-16T15:57:59Z (merged_byos-zhuang, the approver who flipped ready and enqueued);git log origin/maincarries(#18478). Verified onorigin/main:.claude/hooks/now lists ten files — the five remaining guards and their self-tests — andguard-governed-enqueue.*is gone; thesettings.jsondeny roster is unchanged. Governed landing by ruling C: APPROVED 5224805455 on that head, in-seat ACCEPT (stamped 2026-09-16T15:03Z), provenance on the PR. Ruling B on PR #18447 is executed in full. Same act:pm:dispatchedremoved and the assignee cleared on this auto-closed card;domain:skillsstays.
Generated by Claude Code
- added a commit that references this issue
on Sep 17, 2026
Filed by the director seat (summon #24,
session_01Wj1HUjzyeiBQ8atRf1ZhaL) executing the maintainer's ruling B on PR #18447 — reply verbatim 「b」, 2026-09-16 ~14:2xZ, on the director's direct channel. ⛔ Nodomain:*set by the filer; the surface is.claude/hooks/**+.claude/settings.json+ PM gate rosters, which the lane table anchors todomain:skills— triage confirms.Dedup terms:
guard-governed-enqueue·enqueue guard removal·PreToolUse hook·ccr/auto_merge route·check-governed-queue-guardRuling being executed (PR #18447, batch #143 item 4)
The hard line for 「受管面 PR 无授权批准时永不入队」 is
scripts/pm/check-governed-queue-guard.mjson themerge_groupbuild (reads reviews live, refuses; measured 2026-09-01 and held) plus the approver's click. Main's ruleset requires no review on its own (required_approving_review_count: 0,require_code_owner_review: false). The client-side hookguard-governed-enqueue.shexisted only to save one queue cycle and one red entry per violation, watches three spellings no compliant seat uses, parses shell text (its own header lists the wrappers that walk past it), and cost three governed PRs today. The maintainer chose removal over completion.Deliverable
.claude/hooks/guard-governed-enqueue.shand.claude/hooks/guard-governed-enqueue.selftest.sh.settings.jsonhook registrations that name it ("$CLAUDE_PROJECT_DIR/.claude/hooks/guard-governed-enqueue.sh", two entries onorigin/main).scripts/pm/dispatch-gates.mjsfamilies,check:pm-dispatch-gates, anycheck-*that enumerates.claude/hooks/*or the settings hook list) so the gates are green by design — ⛔ not by adding an exemption for a missing file.scripts/pm/check-governed-queue-guard.mjs, thesettings.jsondenyroster (MCP enqueue class stays denied), CODEOWNERS.Acceptance
git ls-files .claude/hooks/guard-governed-enqueue*→ 0;grep -rn guard-governed-enqueue .claude scripts AGENTS.md→ 0 outside CHANGELOG /.changeset/.node scripts/pm/dispatch-gates.mjs --commands .claude/settings.jsonlists no family that runs the deleted self-test; every listed command exits 0 on the branch..claude/**): draft PR, 维护者速读(草稿), four-piece by the seat, maintainer's approval lands it.Serialisation
objectstack#18468 (dispatched,
SKILL.md:92+check-harness-current.mjs) is in flight on a disjoint file surface; the two PRs are independent. Land whichever is approved first.Refs PR #18447 · #18468 · the 2026-09-01 incident the hook's header cites
Generated by Claude Code