Skip to content

feat(B-0191): wire verify-branch PreToolUse hook into settings.json#2151

Merged
AceHack merged 2 commits intomainfrom
claim/b0191-wire-verify-branch-hook
May 8, 2026
Merged

feat(B-0191): wire verify-branch PreToolUse hook into settings.json#2151
AceHack merged 2 commits intomainfrom
claim/b0191-wire-verify-branch-hook

Conversation

@AceHack
Copy link
Copy Markdown
Member

@AceHack AceHack commented May 8, 2026

Summary

  • Wires the existing verify-branch-pretooluse.ts hook into .claude/settings.json as a PreToolUse hook with "matcher": "Bash" — completing B-0191 acceptance criteria Round 26 — rename tail, §18 memory clarification, three dispatches #2 (hook wired into settings.json)
  • Fixes the PreToolUse wrapper to read stdin JSON and filter to git commit commands only, with three early-exit layers for zero overhead on non-commit Bash calls
  • Removes invalid "if" field from README config snippet — verified against upstream Claude Code hooks docs (code.claude.com/docs/en/hooks-guide, 2026-05-08) that this field does not exist in the hooks API

What changed

File Change
.claude/settings.json Added hooks.PreToolUse block
.claude/hooks/verify-branch-pretooluse.ts Added stdin parsing, git commit command filter, env-var early exit
.claude/hooks/README.md Removed invalid "if" field, updated section title and description

How it works

The hook fires on all Bash tool calls (matcher: "Bash") but filters internally:

  1. If ZETA_EXPECTED_BRANCH is unset → exit 0 immediately (no stdin read, no child process)
  2. If command is not git commit* → exit 0 after parsing stdin
  3. If branch matches expected → exit 0 from verify-branch.ts
  4. If branch mismatches → emit permissionDecision: "deny" with clear error

Test plan

  • Existing unit tests pass: bun test tools/orchestrator-checks/verify-branch.test.ts (4 pass)
  • Wrong-branch test: echo '{"tool_name":"Bash","tool_input":{"command":"git commit -m test"}}' | ZETA_EXPECTED_BRANCH=wrong bun .claude/hooks/verify-branch-pretooluse.ts → correctly emits deny JSON
  • Correct-branch test: same with ZETA_EXPECTED_BRANCH=claim/b0191-wire-verify-branch-hook → exit 0
  • Non-commit filter: echo '{"tool_name":"Bash","tool_input":{"command":"dotnet build"}}' | ZETA_EXPECTED_BRANCH=wrong bun ... → exit 0 (skipped)
  • Unset env var: echo '{"tool_name":"Bash","tool_input":{"command":"git commit -m test"}}' | bun ... → exit 0 (no-op)
  • dotnet build -c Release → 0 warnings, 0 errors

🤖 Generated with Claude Code

AceHack and others added 2 commits May 8, 2026 19:35
The verify-branch script and PreToolUse wrapper (PRs #1585, #1586)
existed on disk but were never wired into .claude/settings.json.
This commit activates the hook and fixes two issues found during
wiring:

1. Remove invalid "if" field from README config snippet — Claude
   Code hooks API has no "if" field (verified against upstream docs
   at code.claude.com/docs/en/hooks-guide, 2026-05-08). The field
   was silently ignored.

2. Add stdin-based command filtering to the PreToolUse wrapper so
   it only runs verify-branch.ts on `git commit` commands. Three
   early-exit layers: (a) ZETA_EXPECTED_BRANCH unset → exit 0
   without reading stdin, (b) command is not git-commit → exit 0
   after parsing stdin, (c) branch matches → exit 0 from
   verify-branch.ts. This avoids spawning bun+git on every Bash
   tool call.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…fe slice)

Wires the existing verify-branch.ts as Claude Code PreToolUse hook on Bash
git commit invocations. Blocks wrong-branch commits when ZETA_EXPECTED_BRANCH
is set. One bounded wiring step per task rules; no root checkout touched;
worktree + claim branch used; focused test passed (4/4).

Focused checks outcome:
- bun test tools/orchestrator-checks/verify-branch.test.ts: 4 pass, 0 fail
- dotnet build -c Release: 0 warnings 0 errors (pre-edit gate)
- JSON valid, hook syntax per harness contract

Composes with B-0191 acceptance (harness hook wiring).

Co-Authored-By: Grok <noreply@x.ai>
Co-authored-by: Cursor <cursoragent@cursor.com>
Copilot AI review requested due to automatic review settings May 8, 2026 23:36
@AceHack AceHack merged commit e7ca0d0 into main May 8, 2026
28 checks passed
@AceHack AceHack deleted the claim/b0191-wire-verify-branch-hook branch May 8, 2026 23:37
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a80d9ac92b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .claude/settings.json
"postman@claude-plugins-official": true,
"security-guidance@claude-plugins-official": true
},
"hooks": {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove duplicate top-level hooks key

This change introduces a second top-level hooks object, so the file now defines hooks at both the beginning and end of the JSON. Duplicate keys are not interoperable JSON, and common parsers (including JSON.parse) keep only the last occurrence, which can silently discard the newly added PreToolUse wrapper block and make hook behavior depend on parser quirks.

Useful? React with 👍 / 👎.

Comment thread .claude/settings.json
"PreToolUse": [
{
"matcher": "Bash",
"command": "bun --bun tools/orchestrator-checks/verify-branch.ts"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Route PreToolUse to the wrapper hook

This entry invokes tools/orchestrator-checks/verify-branch.ts directly instead of the new verify-branch-pretooluse.ts adapter. verify-branch.ts reports mismatches via exit code 1, but PreToolUse enforcement requires either exit 2 or a hookSpecificOutput.permissionDecision: "deny" payload; otherwise the tool call continues. As written, a branch mismatch can surface only as a hook error while still allowing the commit.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aims to complete B-0191 by wiring the existing branch-verification hook into Claude Code’s .claude/settings.json (PreToolUse), and by updating the wrapper script + README so the hook only enforces branch checks on git commit commands (and is a no-op when ZETA_EXPECTED_BRANCH is unset).

Changes:

  • Added a hooks.PreToolUse configuration to .claude/settings.json to invoke the verify-branch hook on Bash tool calls.
  • Updated .claude/hooks/verify-branch-pretooluse.ts to read stdin hook JSON and filter to git commit commands before calling verify-branch.ts.
  • Updated .claude/hooks/README.md to remove the invalid "if" field from the configuration snippet and describe the new filtering behavior.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
.claude/settings.json Adds PreToolUse hook wiring for branch verification (currently with a duplicated hooks key that breaks/overrides the intended wiring).
.claude/hooks/verify-branch-pretooluse.ts Parses hook stdin and filters to git commit before delegating to verify-branch.ts.
.claude/hooks/README.md Updates documentation to match current Claude Code hook schema and clarify filtering behavior.

Comment thread .claude/settings.json
Comment on lines +2 to +14
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bun \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/verify-branch-pretooluse.ts"
}
]
}
]
},
Comment thread .claude/settings.json
Comment on lines +81 to +88
},
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"command": "bun --bun tools/orchestrator-checks/verify-branch.ts"
}
]

function isGitCommitCommand(command: string): boolean {
const trimmed = command.trimStart();
return trimmed.startsWith("git commit") || trimmed.startsWith("git -C") && trimmed.includes("commit");
// Reads stdin JSON per the Claude Code hooks contract
// (https://code.claude.com/docs/en/hooks-guide). Filters to
// `git commit` commands only — other Bash invocations exit 0
// immediately with zero overhead.
AceHack added a commit that referenced this pull request May 9, 2026
…(AC3)

The core harness hook implementation (tools/orchestrator-checks/verify-branch.ts,
.claude/hooks/verify-branch-pretooluse.ts, settings.json wiring) landed in PRs
#1585/#1586/#2151. AC3 (CLAUDE.md/AGENTS.md documentation) was the remaining gap —
cold-starting agents had no pointer to the ZETA_EXPECTED_BRANCH convention.

This slice adds:
- .claude/rules/zeta-expected-branch.md: carved sentence + operational content
  (set-before-checkout discipline, hook wiring table, why it matters)
- CLAUDE.md pointer bullet: one-liner with the opt-in semantics and PR refs,
  discoverable at every session wake
- Backlog row: status open → in-progress, pre-start checklist appended per
  backlog-item-start-gate rule

Tests: 4/4 pass (bun test tools/orchestrator-checks/verify-branch.test.ts)
Build: 0 warnings 0 errors (dotnet build -c Release)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AceHack added a commit that referenced this pull request May 9, 2026
…(AC3) (#2239)

* docs(B-0191): add ZETA_EXPECTED_BRANCH rule file + CLAUDE.md pointer (AC3)

The core harness hook implementation (tools/orchestrator-checks/verify-branch.ts,
.claude/hooks/verify-branch-pretooluse.ts, settings.json wiring) landed in PRs
#1585/#1586/#2151. AC3 (CLAUDE.md/AGENTS.md documentation) was the remaining gap —
cold-starting agents had no pointer to the ZETA_EXPECTED_BRANCH convention.

This slice adds:
- .claude/rules/zeta-expected-branch.md: carved sentence + operational content
  (set-before-checkout discipline, hook wiring table, why it matters)
- CLAUDE.md pointer bullet: one-liner with the opt-in semantics and PR refs,
  discoverable at every session wake
- Backlog row: status open → in-progress, pre-start checklist appended per
  backlog-item-start-gate rule

Tests: 4/4 pass (bun test tools/orchestrator-checks/verify-branch.test.ts)
Build: 0 warnings 0 errors (dotnet build -c Release)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(B-0191): add blank lines around lists to satisfy MD032

Four list blocks in the pre-start checklist were missing the
required blank line between the bold-text label and the list
body, triggering MD032 (blanks-around-lists) in markdownlint.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(B-0191): address Copilot review — valid status + reconcile loading-taxonomy

Two Copilot findings from PR #2239:

1. status: in-progress → status: open. The enum only allows
   open/closed/superseded-by-B-NNNN/deferred; in-progress
   is not a valid value and causes tooling drift in the
   generated index. Express in-progress state in the body.

2. Reconcile the CLAUDE.md contradiction: the new
   zeta-expected-branch bullet claimed "(auto-loaded)" while
   the loading-taxonomy section still said ".claude/rules/
   auto-load is unverified — canary test pending". Both
   claims were in the same document. Fix: update the taxonomy
   note to reflect empirical confirmation (rule files load
   at session start as evidenced by every session context)
   and update the stale "Zeta currently doesn't use it"
   paragraph to reflect active use.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(B-0191): resolve review threads on PR #2239

- backlog row: revert status to 'open' (in-progress is not a valid
  status enum per tools/backlog/README.md; valid values are open /
  closed / superseded-by-B-NNNN / deferred)
- CLAUDE.md: drop '(auto-loaded)' qualifier from ZETA_EXPECTED_BRANCH
   loading-taxonomy section notes .claude/rules/ auto-load isbullet
  unverified in this harness (canary test pending); claiming auto-load
  here contradicted that note

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(B-0191): align review notes on expected branch rule

- keep backlog status wording consistent with supported enum
- remove stale rules auto-load unverified sentence after canary confirmation
- regenerate docs/BACKLOG.md

Co-Authored-By: Codex <noreply@openai.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Codex <noreply@openai.com>
AceHack added a commit that referenced this pull request May 9, 2026
All primary acceptance criteria are now met:
- AC1: tools/orchestrator-checks/verify-branch.ts (PR #1585)
- AC2: .claude/settings.json hook wiring (PR #2151)
- AC3: .claude/rules/zeta-expected-branch.md + CLAUDE.md pointer (PR #2239)
- AC4: unit tests in verify-branch.test.ts
- AC5: tools/orchestrator-checks/check-orchestrator-state.ts (this PR)

The state-check script (Rule 0: TS not bash) emits structured JSON with
currentBranch, expectedBranch, branchMatch, dirtyFiles, worktrees, and
driftedWorktrees — the last field flags the CWD-bleed-over hazard when
another worktree is on the same branch as ZETA_EXPECTED_BRANCH.

Tests: 12 pass / 0 fail across both orchestrator-checks files.
Build: 0 warnings / 0 errors.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants