Summary
On any repo cloned with git clone --depth 1, the Stop hook tells the session to push commits that are already on the remote, and calls the branch "no remote branch" while git ls-remote shows it present at exactly the local HEAD.
--depth 1 implies --single-branch, so the clone is configured with a narrowed fetch refspec:
remote.origin.fetch = +refs/heads/main:refs/remotes/origin/main
That mapping covers main only. A session that then creates and pushes a feature branch pushes it successfully, but no refs/remotes/origin/<branch> can ever be created locally — the refspec does not map it. So in the hook, origin/$current_branch fails to resolve, $upstream falls back to origin/HEAD, and git rev-list HEAD --not origin/HEAD --count counts every commit on the branch as unpushed. The "…and no remote branch" message at the tail of that branch is the one that fires.
Measured
bounded-systems/claude-box, cloned per the add_repo instruction (git clone --depth 1 …), branch claude/next-kp573h:
$ git rev-parse HEAD
c69c3dda1cef5b845bdb4c48239c546f6422509b
$ git ls-remote --heads origin claude/next-kp573h
c69c3dda1cef5b845bdb4c48239c546f6422509b refs/heads/claude/next-kp573h # identical
$ git for-each-ref refs/remotes/ # no origin/claude/next-kp573h
… refs/remotes/origin/HEAD
… refs/remotes/origin/main
Hook output at that state: Branch 'claude/next-kp573h' has 1 unpushed commit(s) and no remote branch. Please push these changes to the remote repository. CI had already run green on that exact commit.
The same missing ref caused a second symptom earlier in the session: git push --force-with-lease refused with stale info, because the lease had no remote-tracking ref to compare against. Both are git reasoning from a local ref that the refspec forbids existing, not from the server.
Local repair, for anyone hitting it now:
git config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*'
git fetch origin <branch>
git branch --set-upstream-to=origin/<branch> <branch>
Why it matters
This is a false positive that pushes the session toward the wrong action. With a human reading the transcript it is a nuisance. In an autonomous run it is a loop: the hook demands a push, the push is a no-op because the ref is already there, the hook fires again on the next stop with the same message. Worse, the advice block it prints recommends retrying the push up to five times with backoff — so a session that trusts the hook burns five pushes against an already-correct remote.
Suggested direction
The hook already knows the right check — it prints it as the postcondition the caller should verify:
git ls-remote --exit-code --heads origin '$b'
It just does not take its own advice before asserting. Consulting the remote (or, cheaper, treating "the refspec cannot map this branch" as unknown rather than unpushed) would close it without touching the merge-commit handling that infra#112 and #536-C were about.
Note this hits every session that follows the add_repo clone instruction, which prescribes --depth 1 — so the alternative fix is to have that instruction set the full refspec, and the two are complementary rather than either/or.
Surfaced while working claude-box#236; filed rather than fixed, and left unclaimed.
Summary
On any repo cloned with
git clone --depth 1, the Stop hook tells the session to push commits that are already on the remote, and calls the branch "no remote branch" whilegit ls-remoteshows it present at exactly the local HEAD.--depth 1implies--single-branch, so the clone is configured with a narrowed fetch refspec:That mapping covers
mainonly. A session that then creates and pushes a feature branch pushes it successfully, but norefs/remotes/origin/<branch>can ever be created locally — the refspec does not map it. So in the hook,origin/$current_branchfails to resolve,$upstreamfalls back toorigin/HEAD, andgit rev-list HEAD --not origin/HEAD --countcounts every commit on the branch as unpushed. The"…and no remote branch"message at the tail of that branch is the one that fires.Measured
bounded-systems/claude-box, cloned per theadd_repoinstruction (git clone --depth 1 …), branchclaude/next-kp573h:Hook output at that state:
Branch 'claude/next-kp573h' has 1 unpushed commit(s) and no remote branch. Please push these changes to the remote repository.CI had already run green on that exact commit.The same missing ref caused a second symptom earlier in the session:
git push --force-with-leaserefused withstale info, because the lease had no remote-tracking ref to compare against. Both are git reasoning from a local ref that the refspec forbids existing, not from the server.Local repair, for anyone hitting it now:
Why it matters
This is a false positive that pushes the session toward the wrong action. With a human reading the transcript it is a nuisance. In an autonomous run it is a loop: the hook demands a push, the push is a no-op because the ref is already there, the hook fires again on the next stop with the same message. Worse, the advice block it prints recommends retrying the push up to five times with backoff — so a session that trusts the hook burns five pushes against an already-correct remote.
Suggested direction
The hook already knows the right check — it prints it as the postcondition the caller should verify:
git ls-remote --exit-code --heads origin '$b'It just does not take its own advice before asserting. Consulting the remote (or, cheaper, treating "the refspec cannot map this branch" as unknown rather than unpushed) would close it without touching the merge-commit handling that infra#112 and #536-C were about.
Note this hits every session that follows the
add_repoclone instruction, which prescribes--depth 1— so the alternative fix is to have that instruction set the full refspec, and the two are complementary rather than either/or.Surfaced while working claude-box#236; filed rather than fixed, and left unclaimed.