fix(roborev): skip agent hooks outside branch checkouts
Summary
roborev-agent.shexits 0 without contacting the daemon when the hook's cwd is not on a branch (detached HEAD or not a git repo).
Why
On matic the RoboRev daemon was pinned at its 250% CPU quota (100/100 periods throttled) and every agent-hook run PostToolUse call failed after 5s with context deadline exceeded.
The daemon's goroutine dump showed the cause. On a detached HEAD, findOpenFailedReviewIDs lists every open failed review in the repo (no branch filter). It then calls git merge-base --is-ancestor once per review, with no context cancellation, on every PostToolUse and Stop event. The monorepo has 2604 open failed reviews, so each event from a detached worktree started thousands of git processes, and about 64 ran at any moment.
With turn_threshold = 0 and commit_threshold = 0, failed-review prompting is the only active agent-hook feature. In detached worktrees it never returned within the client timeout, so skipping those worktrees drops no working behavior.
decided: gate in the shared hook script on git symbolic-ref -q HEAD - one cheap git call; scoping by branch keeps reminders working in branch worktrees
decided: no change to the daemon cgroup quota - the quota was hiding the storm, not causing it
Test plan
-
shellspec spec/roborev_hooks_spec.sh(branch calls the daemon; detached HEAD skips it) -
shellcheckon the changed files
Upstream follow-up: in internal/agenthook, the detached-HEAD path should batch the reachability check and honor request cancellation.
🤖 Generated with Claude Code
Summary by cubic
Skips RoboRev agent hooks outside branch checkouts, so hooks stop timing out in large repositories.
- On a detached HEAD, the daemon matches every open failed review in the repo with a
git merge-base --is-ancestorcall per tool event and keeps going after the client gives up. - With about 2600 open failed reviews, events from detached worktrees pinned the daemon at its CPU quota and every agent hook timed out after 5s.
- The hook now exits 0 before contacting the daemon unless it is inside a git work tree with a symbolic HEAD. Branch checkouts are unaffected; bare repositories and non-repositories are skipped too.