fix: detect forked subagents from the hook payload, not env alone - #2031
Open
abseg wants to merge 1 commit into
Open
fix: detect forked subagents from the hook payload, not env alone#2031abseg wants to merge 1 commit into
abseg wants to merge 1 commit into
Conversation
isSubagentContext() tested environment variables only. A forked subagent does not reliably set any of them — including CLAUDE_CODE_FORK_SUBAGENT — and it shares the parent's session_id, so the test returned false inside a fork and every consumer treated the fork as the main session. Observed effects: PostToolObserver injected main-session nudges and the SYSTEM line into a fork's own transcript, ISASync injected the ascent strip there too, and ConfigEvalFire spawned the full behavioural suite when a fork touched a sentinel file. PostToolUse carries agent_id/agent_type for delegated calls and omits them in the main session, which makes the payload the reliable signal. isSubagentContext() now takes the parsed hook input as an optional first argument and returns true when either field is present; the environment union is unchanged and still covers non-fork delegates and standalone runs, where no payload exists. Existing no-argument callers keep working. ISASync scope note: only the ascent strip is gated. syncToWorkJson is deliberately left ungated, because a fork editing an ISA is real work on the run and its progress belongs in work.json.
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.
Problem
isSubagentContext()tests environment variables only. A forked subagent does not reliably set any of them — includingCLAUDE_CODE_FORK_SUBAGENT— and it shares the parent'ssession_idbyte for byte. Measured live: a fork'sPostToolUsehook process had every marker in the union unset.So the test returns
falseinside a fork, and every consumer treats the fork as the main session:This is the residual half of #1831: the fork marker was added for exactly this, and on at least some runtimes it is never set.
Fix
PostToolUsecarriesagent_id/agent_typefor delegated calls and omits them in the main session, which makes the payload the reliable signal.isSubagentContext()now takes the parsed hook input as an optional first argument and returnstruewhen either field is present. The environment union is unchanged and still covers the non-fork delegate families and standalone runs, where no payload exists. Existing no-argument callers keep working — the parameter is optional, so this is not a breaking change.The three consumers above pass their already-parsed
inputthrough.ConfigEvalFire.readInput()is widened to carry the two fields.Scope note
In
ISASync, only the ascent strip is gated.syncToWorkJsonis deliberately left ungated and never was gated: a fork editing an ISA is real work on the run, and its progress belongs inwork.json. Only the strip is per-context, so only the strip is gated.Verification
All four files parse-check clean (
bun build --target=bun).inputis confirmed in scope at each insertion site, and no duplicate imports are introduced.