Skip to content

Regression in muli-agent spawning in 0.113.0 #14288

Open

Description

@randall-8080

What version of Codex CLI is running?

codex-cli 0.113.0

What subscription do you have?

Pro

Which model were you using?

gpt-5.4

What platform is your computer?

Linux 6.19.6-2-cachyos x86_64 unknown

What terminal emulator and version are you using (if applicable)?

Ghostty

What issue are you seeing?

0.113.0 introduced a regression in multi-agent handing. Previously, if an agent's developer prompt contained instructions to spawn sub-agents, it would do so. Now what I see happening is that an "Orchestrater" agent is spawned of the correct type, when then spawns another agent of the same type, which then spawns the desired sub-agents.

What steps can reproduce the bug?

Uploaded thread: 019cdaa2-05c5-7c51-97ce-79c3528a2017

  1. Spawn an agent that spawns specialized sub-agents
  2. Codex then spawns an "Orchestrater" (A0) agent that then spawns the same agent (A1) again
  3. Agent (A1) then finally spawns the specialized sub-agents

What is the expected behavior?

The spawned top-level agent uses the developer_prompt as written and then spawns the specialized sub-agents.

Additional information

No response

Activity

  1. added
    CLIIssues related to the Codex CLI
    tool-callsIssues related to tool calling
    on Mar 11, 2026
  2. github-actions commented on Mar 11, 2026

    @github-actions
    Contributor

    Potential duplicates detected. Please review them and close your issue if it is a duplicate.

    Powered by Codex Action

  3. SproutSeeds commented on Mar 16, 2026

    @SproutSeeds

    I have a strong local root-cause candidate for this one, but I’d frame it narrowly.

    My current read is that the issue is likely in forked sub-agent handoff rather than generic multi-agent planning.

    What seems to be happening:

    • spawn_agent(..., fork_context=true) inherits parent rollout history into the child
    • the child also gets a synthetic handoff telling it to treat the next user message as its task
    • but the parent’s most recent live user directive can still remain in inherited history
    • if that parent directive is itself about delegation/orchestration, the child can reinterpret it as its own instruction and recurse

    This also seems consistent with the older related report in #13491.

    Locally, I patched the forked-spawn path to:

    • truncate inherited history before the parent’s last user message
    • preserve the parent spawn_agent function-call marker when needed
    • keep the synthetic fork handoff output

    That gives the child completed background context without the parent’s active directive.

    I added targeted regression coverage for:

    • unit-level forked spawn history shaping
    • rollout truncation before the last user message
    • integration coverage showing the child still receives earlier parent context, but not the current parent turn prompt

    Those targeted codex-core tests passed locally.

    I also ran a full workspace cargo test. It was not fully green on this machine, but the failures I hit were in unrelated EMFILE rollback-reconstruction tests and local seatbelt sandbox tests, not in the multi-agent fork path.

  4. jif-oai commented on Mar 16, 2026

    @jif-oai
    Contributor

    This is due to model behaviour. We will study this and see what's the best way to fix.
    Truncating in the parent history might actually be sub-optimal...

  5. randall-8080 commented on Mar 16, 2026

    @randall-8080
    Author

    Created a new log file for a slightly different behavior that I just observed where the top-level agent spawned the three specialized agents. Each of the specialized agent spawn three additional specialized agents, for a total of ten agents.

    Log: 019cf8a6-81a4-7a22-bf56-98dcc64c270e

  6. SproutSeeds commented on Mar 17, 2026

    @SproutSeeds

    Model behavior can still affect whether the child chooses to delegate again.
    The forked child history is also leaking a product-side instruction boundary.

    • spawn_agent(..., fork_context=true) snapshots parent rollout into the child
    • the child already receives its own task framing from the synthetic fork handoff and synthetic spawn_agent output
    • on clean upstream main, the second-generation fork test still fails at assert!(!body_contains(&grandchild_request, TURN_1_PROMPT)), so the grandchild request still contains the parent's live "spawn a child and continue" directive instead of only its own spawned task
    • in the targeted local tests, trimming inherited rollout before the newest surviving user-turn segment kept earlier completed context and spawn markers but removed the conflicting live delegation turn
    • that also removes the leaked parent TurnStarted scaffolding instead of cutting only at the raw user-message item
    • re-attaching the specific parent spawn_agent call and synthetic output keeps the fork handoff contract intact after trimming
    • the segment-based version still passes the same first-generation and second-generation fork tests locally

    The leaked boundary is the inherited live delegation turn, not generic background context.
    The strongest current fix shape is: drop the newest surviving user-turn segment from the forked rollout, then re-attach the specific parent spawn_agent call and synthetic output before handing control to the child.

    Patch I validated:
    SproutSeeds@a26e5df

    Full branch:
    https://github.com/SproutSeeds/codex/tree/sprout/issue-14288-fork-turn-segment

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    CLIIssues related to the Codex CLIbugSomething isn't workingtool-callsIssues related to tool calling

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions