fix(hook): treat a shell-wrapped claude as primary, not nested - #135
Open
paskal wants to merge 1 commit into
Open
fix(hook): treat a shell-wrapped claude as primary, not nested#135paskal wants to merge 1 commit into
paskal wants to merge 1 commit into
Conversation
_is_nested_session decided a claude was primary only when its PID equalled the foreground process group id on the pane's tty. That holds when a shell execs claude directly, but not when a launcher starts the agent as `bash -lc "... && claude ..."`: the shell stays group leader, so the primary claude is its child and its PID never equals the foreground PGID. Every hook such a session fires is then dropped as a nested observer, silently, so session_map.json is never written and Telegram forwarding never starts. Decide nesting by asking whether another claude sits above this one in the process tree instead. That covers both primary shapes and is what the pgid comparison was approximating anyway, since a nested claude inherits the same pgid rather than getting its own (as the original comment noted). The fast path for a group-leading primary is kept, and every fails-open branch is untouched. Verified against a real agent-deck session, which launches claude through a login shell: hooks were dropped before the change and bind correctly after it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Hi there, thanks a lot for ccgram, the tmux-side-rather-than-SDK-side approach is exactly what I wanted.## Problem
_is_nested_sessiontreats a claude as primary only when its PID equals the foreground process group id on the pane's tty:python owner = _closest_claude_ancestor(snapshot, os.getpid()) if owner is None: return False return owner != fg_pgidThat holds when a shell execs claude directly, but not when a launcher starts the agent asbash -lc "... && claude ...". The shell stays group leader, so the primary claude is its child and its PID never equals the foreground PGID. On such a pane:fg pgid = 14053 /bin/bash claude = 14058 (pgid 14053)Every hook that session fires is then classified as a nested observer and dropped. It fails silently, sosession_map.jsonis never written and Telegram forwarding never starts for that window.ccgram doctorreports all hooks installed and the topic binds fine, which makes it look like a transcript problem rather than a discarded hook.This is how agent-deck launches sessions, so ccgram adopts its windows as topics and then stays silent in all of them.## ChangeDecide nesting by asking whether another claude sits above this one in the process tree, rather than whether it happens to lead the process group. The fast path for a group-leading primary is kept, and every fails-open branch is untouched.This preserves #62, which the guard was written for. It is arguably a closer test of that issue's intent than the pgid comparison, since a nested claude inherits the parent's pgid rather than getting its own, as the original comment notes, so PID vs PGID cannot reliably separate the two shapes on its own.Two cases added toTestNestedSessionDetection: a shell-wrapped primary is not nested, and an observer under a shell-wrapped primary still is. The existing cases in that class andtest_nested_session_start_does_not_steal_forwardingpass unchanged.Verified on a real agent-deck session, where hooks were dropped before the change and bind correctly after it, withsession_map.jsongetting the right session id, transcript path andprovider_name: claude.