Branch-state gates

Updated Jul 21, 2026

After quality gates pass, the hook evaluates branch-state gates in this order:

  1. Uncommitted changes.
  2. Unpushed commits.
  3. Pull request base branch needs rebasing.

The uncommitted gate blocks when the working tree has uncommitted, staged, or untracked changes, unless the current local branch name is listed in protected_branches. That prevents the hook from asking an agent to commit directly onto a shared protected branch.

The unpushed gate blocks when HEAD is ahead of the branch's upstream ref. If the branch has no upstream, the unpushed gate is skipped. If either the current local branch name or the tracked upstream branch name is listed in protected_branches, the unpushed gate is also skipped so the hook does not ask the agent to push a shared branch directly. The default protected branches are trunk, main, release, and master. When the tracked remote name contains a slash, such as team/fork, the hook strips the longest matching configured remote name before comparing the upstream branch name. Protected branch skips keep stdout quiet like other successful checks and are recorded as structured log records for operators who collect hook logs.

The PR-rebase gate is best effort. It runs only when the hook can identify a primary remote, obtain a GitHub token from GITHUB_TOKEN or gh auth token, find an open pull request for the current branch, and compare the PR base with the local merge-base. Missing remote information, missing tokens, network errors, lookup timeouts, or no open pull request skip this gate rather than blocking the stop.

Disable individual gates in configuration when a repository needs a looser policy:

gate_uncommitted_changes = false
gate_unpushed_commits = false
gate_pr_rebase = false
protected_branches = ["main", "stable"]