Fallout surfaced by #215 (issue #214). The scope fix there was correct; it exposed a pre-existing assumption in the per-repo predicate that is wrong for the clones add_repo produces.
Symptom
At Stop, in a session that used add_repo:
/workspace/verbspec: Branch 'claude/next-jybxja' has 1 unpushed commit(s) and no remote branch.
Please push these changes to the remote repository.
The branch was pushed. Its PR was reviewed and squash-merged. The remote branch still exists.
Mechanism
add_repo instructs git clone --depth 1, which writes a single-branch fetch refspec. Measured in /workspace/verbspec while the message above was firing:
remote.origin.fetch = +refs/heads/main:refs/remotes/origin/main
branch.<branch>.merge = refs/heads/<branch> (push -u DID set tracking)
refs/remotes/ = origin/HEAD, origin/main (and nothing else)
git ls-remote --heads origin <branch> -> the branch EXISTS on the remote
The hook decides "is this pushed?" with git rev-parse origin/$current_branch — a purely local remote-tracking ref. Under a single-branch refspec that ref is never created, no matter how many successful pushes happen. So the hook falls through to upstream=origin/HEAD, reports "no remote branch", and counts the commit as unpushed.
Why it matters now
Before #215, /workspace checkouts were never examined, so this could not fire. Now every session that adds a repo mid-session and pushes a branch will hit it — permanently, including immediately after a clean push, and forever after the PR merges.
That is the cries-wolf mode this repo already names as worse than no hook: "A hook that cries wolf on every merge is worse than no hook: it teaches you to ignore the one time it is right." The existing infra#112 exclusion does not help — it covers a local branch reset onto the merged default branch, not a branch whose remote-tracking ref was never fetched.
Proposed fix
Narrow, and only on the path that is already failing. Where origin/$branch does not resolve but branch.$name.merge is configured, ask the remote before concluding anything:
git ls-remote --heads origin "$branch"
and compare the returned sha against HEAD / containment. A network call lands only in the single-branch-clone case, not on every Stop.
Worth deciding alongside: whether the discovery in #215 should instead fetch the branch ref for /workspace clones so the local ref exists in the first place. The ls-remote route is cheaper and does not mutate the checkout.
Acceptance
Fallout surfaced by #215 (issue #214). The scope fix there was correct; it exposed a pre-existing assumption in the per-repo predicate that is wrong for the clones
add_repoproduces.Symptom
At Stop, in a session that used
add_repo:The branch was pushed. Its PR was reviewed and squash-merged. The remote branch still exists.
Mechanism
add_repoinstructsgit clone --depth 1, which writes a single-branch fetch refspec. Measured in/workspace/verbspecwhile the message above was firing:The hook decides "is this pushed?" with
git rev-parse origin/$current_branch— a purely local remote-tracking ref. Under a single-branch refspec that ref is never created, no matter how many successful pushes happen. So the hook falls through toupstream=origin/HEAD, reports "no remote branch", and counts the commit as unpushed.Why it matters now
Before #215,
/workspacecheckouts were never examined, so this could not fire. Now every session that adds a repo mid-session and pushes a branch will hit it — permanently, including immediately after a clean push, and forever after the PR merges.That is the cries-wolf mode this repo already names as worse than no hook: "A hook that cries wolf on every merge is worse than no hook: it teaches you to ignore the one time it is right." The existing
infra#112exclusion does not help — it covers a local branch reset onto the merged default branch, not a branch whose remote-tracking ref was never fetched.Proposed fix
Narrow, and only on the path that is already failing. Where
origin/$branchdoes not resolve butbranch.$name.mergeis configured, ask the remote before concluding anything:git ls-remote --heads origin "$branch"and compare the returned sha against
HEAD/ containment. A network call lands only in the single-branch-clone case, not on every Stop.Worth deciding alongside: whether the discovery in #215 should instead fetch the branch ref for
/workspaceclones so the local ref exists in the first place. Thels-remoteroute is cheaper and does not mutate the checkout.Acceptance
--depth 1single-branch clone does not report as unpushed.