Summary
sign-branch.yml exits successfully without doing anything whenever the branch tip is verified — even when an older commit on the branch is not. default-branch-protection's required_signatures rule applies to every commit in the PR, so in exactly that case the workflow reports success and the PR stays mergeable_state: blocked.
That is the case the workflow exists for. A branch whose tip a session just pushed is verified by the session lane; the unverified commit is almost always an older one, from before the lane worked or from a different committer identity.
The code
.github/workflows/sign-branch.yml:157-168:
tip="$(git rev-parse "origin/$BRANCH")"
base="$(git merge-base "origin/$default" "$tip")"
...
verified="$(gh api "repos/$TARGET_REPO/commits/$tip" --jq .commit.verification.verified)"
if [ "$verified" = "true" ]; then
echo "sign-branch: tip $tip is already verified — nothing to do."
exit 0
fi
One commit is queried. The rule covers base..tip.
Measured, 2026-08-18
Dispatched at claude-box / claude/deps-osv-scan — run 32178526391, conclusion: success:
sign-branch: tip b022cf905f2980e29952976a9759e473344a4aad is already verified — nothing to do.
Accurate about the tip. The branch at that moment:
| commit |
verified |
reason |
committer |
3786d6a |
false |
no_user |
oink_monocle_7d@icloud.com |
79d9c83 |
true |
valid |
noreply@anthropic.com |
b022cf9 (tip) |
true |
valid |
noreply@anthropic.com |
3786d6a was signed, but by a key with no GitHub user for its committer address. claude-box#246 sat at mergeable_state: blocked with all 11 checks green, and the workflow whose job is to clear that had just reported success.
The session could not tell from the run that anything was left undone — the log says "nothing to do", which reads as "the branch is fine". It took querying .commit.verification.verified per commit by hand to find the real blocker. Resolution was a manual force-push collapsing the branch to one verified commit (claude-box#246, a6d76b0).
Why this is the interesting kind of bug
The remediation is already correct — the mutation re-creates the branch as one signed commit, which would have fixed this branch completely. Only the precondition is too narrow. The workflow can do the job; it just decides it does not need to.
It is also the failure direction that costs most: a gate reporting green while leaving its target exactly as blocked as it found it. docs/agentic-code-hygiene.md rule 3 — a gate's own claim about itself is not evidence — with the twist that here the claim is true and still useless, because it is a claim about the wrong thing.
Suggested fix
base is already computed two lines above, so the range is in hand:
unverified=0
for c in $(git rev-list "$base..$tip"); do
[ "$(gh api "repos/$TARGET_REPO/commits/$c" --jq .commit.verification.verified)" = "true" ] || unverified=1
done
if [ "$unverified" = 0 ]; then
echo "sign-branch: all $(git rev-list --count "$base..$tip") commit(s) on $BRANCH are verified — nothing to do."
exit 0
fi
Costs one API call per commit on the branch, which is small for the branches this runs on, and can be bounded (or the loop short-circuited on the first unverified commit) if that matters.
Worth also making the early-exit message name what was checked — "all N commits verified" rather than "tip is already verified" — so a reader of the log can tell the difference between the two claims.
Two related notes, neither necessarily this issue's to fix:
- The header comment at
:15 cites run 31455664654's "tip is already verified — nothing to do" as evidence that the session lane signs. That inference is still sound for the tip; it just should not be read as the branch being clear.
- A branch that reaches this state can only be fixed by rewriting history, which is a force-push. If sign-branch handled it, that force-push would be the workflow's rather than a session's — better, since the workflow already force-pushes (
gh api -X PATCH … force=true) as its normal mode.
Surfaced while landing claude-box#246; filed rather than fixed, and left unclaimed.
Summary
sign-branch.ymlexits successfully without doing anything whenever the branch tip is verified — even when an older commit on the branch is not.default-branch-protection'srequired_signaturesrule applies to every commit in the PR, so in exactly that case the workflow reports success and the PR staysmergeable_state: blocked.That is the case the workflow exists for. A branch whose tip a session just pushed is verified by the session lane; the unverified commit is almost always an older one, from before the lane worked or from a different committer identity.
The code
.github/workflows/sign-branch.yml:157-168:One commit is queried. The rule covers
base..tip.Measured, 2026-08-18
Dispatched at
claude-box/claude/deps-osv-scan— run 32178526391, conclusion: success:Accurate about the tip. The branch at that moment:
3786d6ano_useroink_monocle_7d@icloud.com79d9c83noreply@anthropic.comb022cf9(tip)noreply@anthropic.com3786d6awas signed, but by a key with no GitHub user for its committer address. claude-box#246 sat atmergeable_state: blockedwith all 11 checks green, and the workflow whose job is to clear that had just reported success.The session could not tell from the run that anything was left undone — the log says "nothing to do", which reads as "the branch is fine". It took querying
.commit.verification.verifiedper commit by hand to find the real blocker. Resolution was a manual force-push collapsing the branch to one verified commit (claude-box#246,a6d76b0).Why this is the interesting kind of bug
The remediation is already correct — the mutation re-creates the branch as one signed commit, which would have fixed this branch completely. Only the precondition is too narrow. The workflow can do the job; it just decides it does not need to.
It is also the failure direction that costs most: a gate reporting green while leaving its target exactly as blocked as it found it.
docs/agentic-code-hygiene.mdrule 3 — a gate's own claim about itself is not evidence — with the twist that here the claim is true and still useless, because it is a claim about the wrong thing.Suggested fix
baseis already computed two lines above, so the range is in hand:Costs one API call per commit on the branch, which is small for the branches this runs on, and can be bounded (or the loop short-circuited on the first unverified commit) if that matters.
Worth also making the early-exit message name what was checked — "all N commits verified" rather than "tip is already verified" — so a reader of the log can tell the difference between the two claims.
Two related notes, neither necessarily this issue's to fix:
:15cites run 31455664654's "tip is already verified — nothing to do" as evidence that the session lane signs. That inference is still sound for the tip; it just should not be read as the branch being clear.gh api -X PATCH … force=true) as its normal mode.Surfaced while landing claude-box#246; filed rather than fixed, and left unclaimed.