Skip to content

Isolate Codex Session notifications by thread - #1740

Open
scottt732 wants to merge 1 commit into
kelos-dev:mainfrom
scottt732:fix/codex-child-notifications
Open

Isolate Codex Session notifications by thread#1740
scottt732 wants to merge 1 commit into
kelos-dev:mainfrom
scottt732:fix/codex-child-notifications

Conversation

@scottt732

@scottt732 scottt732 commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

What type of PR is this?

/kind bug

What this PR does / why we need it:

Codex app-server publishes notifications for native subagent threads as well as the Session's root thread. Kelos currently applies every notification to the root interaction: a child error or completion can terminate the Session request while the root agent continues working, and child output or usage can be attributed to the parent.

Filter thread-scoped notifications to the provider's root thread before changing interaction state or emitting events. Preserve notifications without thread scope, including account-wide rate limits. Synchronize initial thread-ID publication with the notification reader.

Which issue(s) this PR is related to:

N/A

Special notes for your reviewer:

Reproduced with a persistent Codex Session spawning native analysts: one child failed while the root kept working, but the Session client received failure and disconnected. The regression test feeds child startup, text, error and completion notifications and then verifies the parent still emits output and completes normally.

Validation: go test ./internal/sessionruntime -run TestCodex -count=1 -race -timeout=90s passed. The new regression failed before the fix. Broader coverage is delegated to CI.

Does this PR introduce a user-facing change?

Native Codex subagent notifications no longer prematurely finish the parent Session turn or appear as parent output.

Summary by cubic

Filters Codex Session notifications by thread so child subagent events no longer affect the parent session. Previously, a child error or completion could prematurely finish the parent turn or be attributed as parent output.

  • Adds thread-scope filtering to the notification handler.
  • Adds a regression test verifying child notifications are ignored.

Written for commit 2e6e9b6. Summary will update on new commits.

Review in cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="internal/sessionruntime/codex.go">

<violation number="1" location="internal/sessionruntime/codex.go:796">
P3: During provider startup the readLoop goroutine starts before openThread/startThread publish `p.threadID`, so for a brief window `threadID == ""`. The new filter then drops any notification carrying a non-empty `threadId`, including a root-thread notification that arrives before the root ID is assigned. Only apply the filter once the root thread ID is known.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

done := p.turnDone
kind := p.interactionKind
p.activeMu.Unlock()
if json.Unmarshal(params, &scope) == nil && scope.ThreadID != "" && scope.ThreadID != threadID {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: During provider startup the readLoop goroutine starts before openThread/startThread publish p.threadID, so for a brief window threadID == "". The new filter then drops any notification carrying a non-empty threadId, including a root-thread notification that arrives before the root ID is assigned. Only apply the filter once the root thread ID is known.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At internal/sessionruntime/codex.go, line 796:

<comment>During provider startup the readLoop goroutine starts before openThread/startThread publish `p.threadID`, so for a brief window `threadID == ""`. The new filter then drops any notification carrying a non-empty `threadId`, including a root-thread notification that arrives before the root ID is assigned. Only apply the filter once the root thread ID is known.</comment>

<file context>
@@ -777,11 +781,21 @@ func (p *CodexProvider) readLoop(reader io.Reader) {
 	done := p.turnDone
 	kind := p.interactionKind
 	p.activeMu.Unlock()
+	if json.Unmarshal(params, &scope) == nil && scope.ThreadID != "" && scope.ThreadID != threadID {
+		return
+	}
</file context>
Suggested change
if json.Unmarshal(params, &scope) == nil && scope.ThreadID != "" && scope.ThreadID != threadID {
if threadID != "" && json.Unmarshal(params, &scope) == nil && scope.ThreadID != "" && scope.ThreadID != threadID {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant