Skip to content

fix(codex): delegate_task children start as Codex subagents - #36525

Merged
juliusmarminge merged 3 commits into
mainfrom
factory/issue-36520
Nov 14, 2026
Merged

juliusmarminge merged 3 commits into
mainfrom
factory/issue-36520

Conversation

@juliusmarminge

@juliusmarminge juliusmarminge commented Nov 14, 2026 •

Copy link
Copy Markdown
Member

The rework brief listed nothing to change; it asked me to stay on PR #36525. CI then failed 'Test Server 2' (and the aggregate 'Check') on 6bedaa6. The failure was real: the four OrchestratorMcpToolkit.integration.test.ts tests ('reports distinct/identical/emoji/long child results') replay the recorded delegated_task_status Codex transcript, and its child.thread/start frame had no threadSource. With the fix, T3 sends threadSource "subagent" for delegate_task children, so the replay harness rejected the frame. The job's later 'not enough space on the disk' error was runner noise, not the cause. I reproduced the four failures locally with the same test file, then added "threadSource":"subagent" to that one expected outbound frame in apps/server/src/orchestration-v2/testkit/fixtures/delegated_task_status/codex_transcript.ndjson. No other Codex fixture starts a delegated child. The fix is unchanged. On f8074ae all 8 required checks pass and Macroscope approved with no comments on any line. CodeRabbit's review of 6bedaa6 had no actionable comments. Its re-run on f8074ae was still in progress when I stopped watching, and it had reported 0 included reviews left that hour. No finding rated high or above is open.

Fixes #36520

Reproduction on main

On origin/main 56fd98b: ran T3CODE_DEV_SEED=0 CODEX_HOME=<scratch> vp run dev, where the scratch Codex home held the hub config plus a stdio MCP server metalog whose tool record_marker logs params._meta["x-codex-turn-metadata"] on every tools/call. Paired the web client in the preview browser and, in a new Codex thread (GPT-6-Astra, codex-cli 0.169.0), asked the agent to call record_marker("parent"), then delegate_task a Codex child that calls record_marker("child"). Both calls were logged. Each had session_id, thread_id, turn_id, model and codex_version, and neither had a thread_source key. The database showed the child's native Codex thread (01a1d8c9-b23f-…) owned by app thread thread:delegated-task:command%3Amcp%3A…, so it was a delegate_task child, not a native Codex subagent. Then added a failing adapter test (CodexAdapterV2.test.ts, 'starts delegate_task children with the subagent thread source'). It records threadSource on thread/start for a parent thread and for a thread whose id comes from delegatedTaskThread, and gets [undefined, undefined] where it expects [undefined, "subagent"].

Evidence

Run the real Codex parent → record_marker('parent') → T3 delegate_task → child record_marker('child') flow on fresh databases. GPT-6-Astra, codex-cli 0.169.0, Chromium, 1280×800, light theme. Reproduced twice per commit; screenshots show the second run. Received MCP logs independently confirm the displayed results and identify both children as T3-owned delegates.

Before After
56fd98be52 f8074ae16a
Before: both markers lack thread_source After: child has thread_source subagent; parent remains unmarked
Delegation recording Delegation recording
Received MCP metadata and child identity Received MCP metadata and child identity

Reproduction driver · MCP logger

Captures are cropped to the conversation for privacy. Recordings begin with delegation underway; the head recording ends with a three-second hold of the final still. Runtime exports remove only the private workspaces map. All captures were visually inspected before upload.

Tests

vp test run src/mcp/OrchestratorMcpToolkit.integration.test.ts on 6bedaa6: 4 failed (reproduces CI). Same file after the fixture fix: 4 passed. vp test run of RestartContinuation, ThreadStop, RemoteDelegatedTask, ProviderRuntimeRecoveryService, peer/RemoteDelegation and mcp/McpHttpServer tests: 196 passed. Pre-commit fmt and lint passed. CI on f8074ae: all 8 required checks passed.

Not in this change

  • thread/fork also accepts threadSource. delegate_task does not create children through forkThread today, so I left fork unchanged.
  • Other providers' adapters (Claude, Cursor, OpenCode, ...) were not checked for an equivalent sub-agent marker; the issue is Codex-specific.
  • thread/fork also accepts threadSource. delegate_task does not create children through forkThread today, so fork is unchanged.
  • Other providers' adapters were not checked for an equivalent sub-agent marker; the issue is Codex-specific.

Who did the work

  • Written by Claude Opus 5.5, in Claude Code
  • Reviewed by GPT-6.1 Sol, in Codex, and by Claude Opus 5.5, each on its own
  • Proof captured by GPT-6.1 Sol and checked by Claude Opus 5.5

Run by the t3code factory in T3 Code.

juliusmarminge and others added 2 commits November 14, 2026 06:42
…urce

Failing test for #36520: Codex thread/start for a delegate_task child
carries no threadSource, so the child's MCP tool calls look like a
user thread's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex delegate_task children were started with thread/start and no
threadSource, so their MCP tool calls carried no thread_source and MCP
servers could not tell them from threads the user opened. ensureThread
now sends threadSource "subagent" when the app thread id is a
delegate_task child.

Fixes #36520

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:trusted Author is trusted by repo permissions or the VOUCHED list. size:S 10-29 changed lines (additions + deletions). labels Nov 14, 2026
macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Nov 14, 2026
@macroscopeapp

macroscopeapp Bot commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at f8074ae

Macroscope's review found this PR approvable — Delegated Codex children now start with subagent classification so their MCP calls carry the appropriate source. Ordinary threads, identifier formats and runtime policies remain unchanged, keeping the change narrowly scoped.

Notes:

  • No code objects were reviewed. Approvability was decided on eligibility alone.

You can add or adjust custom eligibility rules. Learn more.

@coderabbitai

coderabbitai Bot commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

Explain Change →

No actionable comments were generated in the recent review.

Walkthrough

The change identifies delegated-task thread IDs with a shared prefix. When Codex starts one of these threads, it sets threadSource to "subagent". A replay-based test checks the source values for the parent and child threads.

Changes: Codex delegated-task thread source

Layer / File(s) Summary
Delegated-task thread identification
packages/provider-core/src/server/IdAllocator.ts
A shared prefix and predicate identify delegated-task thread IDs. The ID derivation uses the prefix and URI-encodes the command ID as its suffix.
Codex thread start source
apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts, apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts, apps/server/src/orchestration-v2/testkit/fixtures/delegated_task_status/codex_transcript.ndjson
Codex sets threadSource to "subagent" when it starts a delegated-task thread. The replay-based test checks that the parent has no source and the child has the subagent source. The transcript fixture includes the child source.

Estimated code review effort: 2 (Simple) | ~8 minutes

Change: Bug fix · Severity of issue fixed: Low

Suggested reviewers: t3dotgg

Merge Risk: Minimal · up to f8074

Delegated-task children now start as Codex subagents, and user threads are unchanged. No merge-blocking risk was found.

Pre-merge checks: Passed 4

Check name Status Explanation
Title check Passed The title "fix(codex): delegate_task children start as Codex subagents" names the main change. The PR adds threadSource "subagent" to thread/start params for delegate_task child threads in the Codex a…
Linked Issues check Passed [#36520] The issue requires that a Codex delegate_task child send thread_source "subagent" in its MCP turn metadata, and that threads opened by users stay unchanged. At the reviewed head, CodexAdapter…
Out of Scope Changes check Passed Every changed area connects to #36520. CodexAdapterV2.ts carries the behavior change. IdAllocator.ts adds the predicate and the shared prefix constant that the gate needs. It also changes derive.deleg…
Description check Passed The description has a clear problem statement, a change explanation, a reproduction on main, and detailed verification. It does not follow the template exactly. It has no Scope section that links an i…
Full details: Title check

Explanation

The title "fix(codex): delegate_task children start as Codex subagents" names the main change. The PR adds threadSource "subagent" to thread/start params for delegate_task child threads in the Codex adapter, so the title matches the changeset and follows the conventional commit form.

Full details: Linked Issues check

Explanation

[#36520] The issue requires that a Codex delegate_task child send thread_source "subagent" in its MCP turn metadata, and that threads opened by users stay unchanged. At the reviewed head, CodexAdapterV2.ensureThread adds threadSource: "subagent" to the thread/start params only when isDelegatedTaskThread(threadInput.threadId) is true. Other threads receive no threadSource. isDelegatedTaskThread in packages/provider-core/src/server/IdAllocator.ts is a startsWith check on DELEGATED_TASK_THREAD_PREFIX. derive.delegatedTaskThread builds its ID from the same constant, so the gate matches the IDs the app creates. The regression test 'starts delegate_task children with the subagent thread source' covers a parent thread (no threadSource) and a child thread (threadSource "subagent"). The PR reports that this test fails without the fix and that CodexAdapterV2.test.ts passes (168/168). The issue's app-server check shows that threadSource on thread/start reaches later tool-call metadata. The PR does not run a live codex-cli. Only coding requirements apply. The PR's pending evidence section is not a coding requirement. Incremental change since the previous review is limited to the same gate and the same test fixture, so the prior conclusion still holds.

Full details: Out of Scope Changes check

Explanation

Every changed area connects to #36520. CodexAdapterV2.ts carries the behavior change. IdAllocator.ts adds the predicate and the shared prefix constant that the gate needs. It also changes derive.delegatedTaskThread to use that constant. The test file adds the regression test. The replay fixture codex_transcript.ndjson adds threadSource "subagent" to the child's expected thread/start frame, which the strict replay harness needs. The PR leaves thread/fork and other providers unchanged. The issue does not require either change, so their absence is not out of scope. No unrelated change appears in the reviewed summary or diff hunks.

Full details: Description check

Explanation

The description has a clear problem statement, a change explanation, a reproduction on main, and detailed verification. It does not follow the template exactly. It has no Scope section that links an issue for the bug, but it does cite Fixes #36520. It also omits the required 'Problem' and 'Scope' headings and has no conventional commit title. The Verification section is thorough, with before/after evidence. It also has duplicated bullets in the 'Not in this change' section and refers to a 'rework brief' and 'factory' process that is internal context, not repository template content. Overall the description is mostly complete.

Finishing Touches

Generate unit tests (beta)

  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Recent review info

Run configuration

Commits

Reviewing files that changed from the base of the PR and between 6bedaa6 and f8074ae.

Files selected for processing: 1

  • apps/server/src/orchestration-v2/testkit/fixtures/delegated_task_status/codex_transcript.ndjson

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 0 remain after this review.

@github-actions

github-actions Bot commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 5.0 KiB 5.0 KiB 0 B (0.0%) 6.8 KiB ✅
Codex Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Codex Live turn WebSocket wire 1.2 KiB 1.2 KiB 0 B (0.0%) 2.0 KiB ✅
Codex Live turn WebSocket decoded 21.5 KiB 21.5 KiB 0 B (0.0%) 24.4 KiB ✅
Codex Live turn messages 1 1 0 (0.0%) 8 ✅
Claude Total thread wire 5.0 KiB 5.0 KiB 0 B (0.0%) 6.8 KiB ✅
Claude Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Claude Live turn WebSocket wire 1.2 KiB 1.2 KiB 0 B (0.0%) 2.0 KiB ✅
Claude Live turn WebSocket decoded 21.9 KiB 21.9 KiB 0 B (0.0%) 24.4 KiB ✅
Claude Live turn messages 1 1 0 (0.0%) 8 ✅

Other scenarios

All 64 scenario measurements match main.

All 64 scenario measurements
Provider Scenario Metric Main baseline This PR Impact PR ceiling
Codex short turn resume WebSocket decoded 10.5 KiB 10.5 KiB 0 B (0.0%) 12.7 KiB ✅
Codex short turn resume WebSocket messages 2 2 0 (0.0%) 2 ✅
Codex long turn running, cold open HTTP decoded 281.7 KiB 281.7 KiB 0 B (0.0%) 302.7 KiB ✅
Codex long turn running, cold open rows 75 75 0 (0.0%) 75 ✅
Codex long turn running, history page HTTP decoded 92.4 KiB 92.4 KiB 0 B (0.0%) 107.4 KiB ✅
Codex long turn running, history page rows 75 75 0 (0.0%) 75 ✅
Codex long turn finished, cold open HTTP decoded 381.5 KiB 381.5 KiB 0 B (0.0%) 410.2 KiB ✅
Codex long turn finished, cold open rows 151 151 0 (0.0%) 151 ✅
Codex long turn finished, socket cold open WebSocket decoded 381.6 KiB 381.6 KiB 0 B (0.0%) 410.2 KiB ✅
Codex long turn finished, socket cold open WebSocket messages 2 2 0 (0.0%) 2 ✅
Codex long turn finished, socket cold open rows 151 151 0 (0.0%) 151 ✅
Codex long turn finished, work page HTTP decoded 186.2 KiB 186.2 KiB 0 B (0.0%) 200.2 KiB ✅
Codex long turn finished, work page rows 150 150 0 (0.0%) 150 ✅
Codex idle thread resume WebSocket decoded 65 B 65 B 0 B (0.0%) 200 B ✅
Codex idle thread resume WebSocket messages 1 1 0 (0.0%) 1 ✅
Codex fleet shell snapshot HTTP decoded 42.4 KiB 42.4 KiB 0 B (0.0%) 53.7 KiB ✅
Codex fleet shell snapshot rows 27 27 0 (0.0%) 27 ✅
Codex fleet shell subscribe WebSocket decoded 65 B 65 B 0 B (0.0%) 200 B ✅
Codex fleet shell subscribe WebSocket messages 1 1 0 (0.0%) 1 ✅
Codex fleet shell resume check HTTP decoded 18 B 18 B 0 B (0.0%) 100 B ✅
Codex fleet shell resume check rows 0 0 0 0 ✅
Codex stale shell reload HTTP decoded 42.4 KiB 42.4 KiB 0 B (0.0%) 53.7 KiB ✅
Codex stale shell reload rows 27 27 0 (0.0%) 27 ✅
Codex stale shell resume WebSocket decoded 140 B 140 B 0 B (0.0%) 400 B ✅
Codex stale shell resume WebSocket messages 2 2 0 (0.0%) 2 ✅
Codex stale todo list cold open HTTP decoded 72.4 KiB 72.4 KiB 0 B (0.0%) 83.0 KiB ✅
Codex stale todo list cold open rows 75 75 0 (0.0%) 75 ✅
Codex stale todo list cold open nodes 2 2 0 (0.0%) 4 ✅
Codex stale todo list cold open subagents 2 2 0 (0.0%) 4 ✅
Codex stale todo list cold open plans 1 1 0 (0.0%) 2 ✅
Codex stale todo list cold open providerTurns 1 1 0 (0.0%) 2 ✅
Codex stale todo list cold open checkpoints 1 1 0 (0.0%) 2 ✅
Claude short turn resume WebSocket decoded 10.6 KiB 10.6 KiB 0 B (0.0%) 12.7 KiB ✅
Claude short turn resume WebSocket messages 2 2 0 (0.0%) 2 ✅
Claude long turn running, cold open HTTP decoded 283.3 KiB 283.3 KiB 0 B (0.0%) 302.7 KiB ✅
Claude long turn running, cold open rows 75 75 0 (0.0%) 75 ✅
Claude long turn running, history page HTTP decoded 93.4 KiB 93.4 KiB 0 B (0.0%) 107.4 KiB ✅
Claude long turn running, history page rows 75 75 0 (0.0%) 75 ✅
Claude long turn finished, cold open HTTP decoded 384.2 KiB 384.2 KiB 0 B (0.0%) 410.2 KiB ✅
Claude long turn finished, cold open rows 151 151 0 (0.0%) 151 ✅
Claude long turn finished, socket cold open WebSocket decoded 384.3 KiB 384.3 KiB 0 B (0.0%) 410.2 KiB ✅
Claude long turn finished, socket cold open WebSocket messages 2 2 0 (0.0%) 2 ✅
Claude long turn finished, socket cold open rows 151 151 0 (0.0%) 151 ✅
Claude long turn finished, work page HTTP decoded 188.1 KiB 188.1 KiB 0 B (0.0%) 200.2 KiB ✅
Claude long turn finished, work page rows 150 150 0 (0.0%) 150 ✅
Claude idle thread resume WebSocket decoded 66 B 66 B 0 B (0.0%) 200 B ✅
Claude idle thread resume WebSocket messages 1 1 0 (0.0%) 1 ✅
Claude fleet shell snapshot HTTP decoded 43.0 KiB 43.0 KiB 0 B (0.0%) 53.7 KiB ✅
Claude fleet shell snapshot rows 27 27 0 (0.0%) 27 ✅
Claude fleet shell subscribe WebSocket decoded 66 B 66 B 0 B (0.0%) 200 B ✅
Claude fleet shell subscribe WebSocket messages 1 1 0 (0.0%) 1 ✅
Claude fleet shell resume check HTTP decoded 18 B 18 B 0 B (0.0%) 100 B ✅
Claude fleet shell resume check rows 0 0 0 0 ✅
Claude stale shell reload HTTP decoded 43.0 KiB 43.0 KiB 0 B (0.0%) 53.7 KiB ✅
Claude stale shell reload rows 27 27 0 (0.0%) 27 ✅
Claude stale shell resume WebSocket decoded 140 B 140 B 0 B (0.0%) 400 B ✅
Claude stale shell resume WebSocket messages 2 2 0 (0.0%) 2 ✅
Claude stale todo list cold open HTTP decoded 73.4 KiB 73.4 KiB 0 B (0.0%) 83.0 KiB ✅
Claude stale todo list cold open rows 75 75 0 (0.0%) 75 ✅
Claude stale todo list cold open nodes 2 2 0 (0.0%) 4 ✅
Claude stale todo list cold open subagents 2 2 0 (0.0%) 4 ✅
Claude stale todo list cold open plans 1 1 0 (0.0%) 2 ✅
Claude stale todo list cold open providerTurns 1 1 0 (0.0%) 2 ✅
Claude stale todo list cold open checkpoints 1 1 0 (0.0%) 2 ✅

Baseline: 2c61677 · PR result: f8074ae · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

The delegated_task_status Codex transcript recorded the child's
thread/start without threadSource; T3 now sends "subagent" for
delegate_task children.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@macroscopeapp
macroscopeapp Bot dismissed their stale review November 14, 2026 07:13

Dismissing prior approval to re-evaluate f8074ae

@juliusmarminge
juliusmarminge merged commit 99071c4 into main Nov 14, 2026
38 checks passed
@juliusmarminge
juliusmarminge deleted the factory/issue-36520 branch November 14, 2026 07:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S 10-29 changed lines (additions + deletions). vouch:trusted Author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Codex delegate_task children start with no threadSource, so their MCP servers see them as user threads

1 participant