After quality gates pass, the hook evaluates branch-state gates in this order:
- Uncommitted changes.
- Unpushed commits.
- 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"]