Parallel-first subagents and nonblocking background task management

Open 💬 10 comments Opened May 11, 2026 by LEON-gittech
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

Summary

This issue collects several related discussions around subagent delegation, background terminal/subagent lifecycle management, and task visibility in the TUI.

We have implemented a working fork, Open Codex CLI, that explores one possible direction:

  • more proactive parallel-first subagent planning
  • nonblocking background execution for terminals and subagents
  • a persistent Down task panel for inspecting and managing background work
  • task-list visibility in the same panel, so plan/task progress is not only visible as a compact status-line counter

Fork: https://github.com/LEON-gittech/codex

The implementation is fork-specific today, but the interaction model may be useful as prior art for upstream Codex.

Related upstream discussions

Subagent planning / delegation

  • #21858 - mission-entropy detection and bounded sub-agent delegation

https://github.com/openai/codex/issues/21858

  • #16996 - subagent spawn policy conflicts with repo-level workflow instructions

https://github.com/openai/codex/issues/16996

  • #16183 - configurable sub-agent spawning limits

https://github.com/openai/codex/issues/16183

These point to a common theme: Codex needs a clearer policy for when to split work into subagents, how aggressive that split should be, and how repo/user instructions should shape the delegation strategy.

Background terminals / nonblocking execution

  • #3968 - Background Terminal Sessions

https://github.com/openai/codex/issues/3968

  • #17737 - Claude Code-like monitoring for background terminals

https://github.com/openai/codex/issues/17737

  • #16935 - view full output of background terminals

https://github.com/openai/codex/issues/16935

  • #17821 - /stop <id> for a single background terminal

https://github.com/openai/codex/issues/17821

  • #19197 - orphaned subagents, lifecycle controls, session freezes

https://github.com/openai/codex/issues/19197

These discussions all touch the same interaction problem: long-running terminal or subagent work should remain visible and manageable, but it should not block the main conversation loop.

Design direction explored in Open Codex CLI

1. Parallel-first subagent planning policy

Open Codex currently implements this mostly through the instruction layer rather than a hardcoded scheduler.

The policy asks the main agent to classify non-trivial work by independent axes before editing:

  • investigation
  • review
  • test discovery
  • docs/reference comparison
  • validation
  • implementation

When independent axes exist, the agent is encouraged to spawn read-only subagents first, while keeping final code changes in one implementation lane unless write ownership is clearly disjoint.

This is intentionally more proactive than the default conservative behavior, but still bounded:

  • do not duplicate work between main thread and subagents
  • do not spawn if the next critical-path step is blocked on the result
  • prefer read-only lanes for evidence gathering
  • keep implementation centralized unless ownership is cleanly separable
  • report what each lane found before finalizing

2. Nonblocking background execution

Open Codex treats long-running terminals and subagents as background work by default.

The goal is to avoid this pattern:

Waiting for background terminal...
Waiting for subagent...

Instead, background work continues independently while the user can keep typing and submitting new messages.

The foreground model turn still shows normal Working state when the model is active, but background terminals/subagents are tracked separately and summarized in the status line.

3. Persistent Down task panel

Open Codex adds a persistent bottom-pane panel opened with Down.

The panel separates:

  • Tasks
  • Subagents
  • Terminals

It updates in place rather than inserting a chat-stream summary.

For terminals, the detail view shows:

  • status
  • elapsed runtime
  • wrapped command/task metadata
  • recent output tail
  • stop action when the process is stoppable

For subagents, the detail view shows:

  • agent title / nickname
  • role
  • task prompt
  • status
  • elapsed runtime
  • progress lines when available

4. Plan/task monitoring

In addition to background terminals and subagents, Open Codex also surfaces the latest plan/task list in the same Down panel.

This matters because compact status-line counters are useful, but insufficient when the user wants to inspect:

  • what tasks exist
  • which tasks completed
  • which task is currently active
  • what remains pending

So the panel acts as a lightweight task manager for the current session.

Other features in Open Codex CLI

Open Codex also includes several adjacent TUI / workflow improvements:

  • /effort slash command for changing reasoning effort
  • query-level ulw / ultra / xhigh marker support for one-turn xhigh reasoning
  • per-turn reasoning effort visibility in the status line while the turn is running
  • /export <path> for markdown session export
  • status-line token throughput visibility
  • workspace git diff stats in the status line
  • Open Codex-aware npm update prompts
  • active memory staging on top of upstream memories
  • stale turn output protection in the TUI
  • improved user-query highlighting in chat history and composer

More details are in the fork README:

https://github.com/LEON-gittech/codex

Why this may be useful upstream

The core idea is that background work should be first-class TUI state, not a blocking foreground wait state.

A useful upstream design could separate three concepts:

  1. foreground model activity

The main assistant is currently generating or deciding.

  1. background task activity

Terminals/subagents are still running, but they should not block input.

  1. task management UI

Users need a stable place to inspect, switch, stop, and review background work.

This distinction makes the TUI easier to reason about:

  • Working means the foreground model is active
  • background work is visible but nonblocking
  • users can inspect details through a dedicated panel
  • completed work can be surfaced without hijacking the chat stream

Implementation reference

The current implementation is available here:

https://github.com/LEON-gittech/codex

This is not proposed as a direct patch to copy verbatim. It is a working fork implementation that may help evaluate the interaction model and identify which parts would be appropriate for upstream Codex.

View original on GitHub ↗

10 Comments

github-actions[bot] contributor · 2 months ago

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

  • #21858
  • #21998
  • #21355
  • #20907

Powered by Codex Action

LEON-gittech · 2 months ago

Here are some screenshots:
<img width="1237" height="306" alt="Image" src="https://github.com/user-attachments/assets/37e1dfa1-70e7-499e-9d41-78b715ce69bd" />
<img width="1260" height="494" alt="Image" src="https://github.com/user-attachments/assets/d3efb353-eef5-49f6-871c-ae100e87e9ca" />
<img width="933" height="410" alt="Image" src="https://github.com/user-attachments/assets/8700d4fc-b3d3-4e02-a662-f3d7a387fdad" />
<img width="1125" height="308" alt="Image" src="https://github.com/user-attachments/assets/411e73c2-6c8f-44e2-a354-1950636487e2" />

Keesan12 · 2 months ago

The interaction model makes sense, but I would be careful not to turn parallel-first into an out first and ask questions later. The missing primitive for me is not just a better panel, it is a shared run contract across parent, subagents, and background terminals: ownership, budget slice, last-progress receipt, and terminal halt reason. Once those exist, the Down panel becomes much more useful because it shows why work is still admitted, not only that it exists. We have been hitting the same problem in MartinLoop around coding-agent orchestration.

Keesan12 · 2 months ago

Parallel-first design gets much safer if each background worker emits a tiny receipt before the parent keeps going: worker id, claimed scope, last verifier result, and terminal stop reason. Otherwise you can make subagents nonblocking and still leave operators blind to whether a worker is making progress or just staying busy in the background.

LEON-gittech · 2 months ago

I read the relevant Open Codex implementation (the fork, not upstream codex) before replying — most of the receipt fields you describe are already there on CollabAgentState; the gap is the verifier slot. Concretely:

  • Worker id / nickname — returned by spawn_agent (agent_id + nickname in v1, task_name + nickname in v2; codex-rs/core/src/tools/handlers/multi_agents_spec.rs:354-401) and surfaced as the cell title in the Down panel.
  • Claimed scopeagent_role, lane, ownership, output_contract, spawn_reason, phase ride on CollabAgentState (codex-rs/app-server-protocol/src/protocol/v2/item.rs:1014-1034) and are merged into per-thread metadata on every update (codex-rs/tui/src/chatwidget/background_agents.rs:76-99). The instruction layer that asks the parent to name a consumer decision and output contract before spawning lives in docs/parallel-first-agent-execution.md (Subagent Prompt Requirements section).
  • Terminal stop reasonCollabAgentStatus distinguishes Completed / Interrupted / Errored / Shutdown / NotFound, and notify_collab_agent_background_completion emits an info line "{title} {status_verb}. {summary}" into the chat the moment a worker leaves the live set (background_agents.rs:192-234). The parent is not blocked on it, but the receipt lands in the main lane, not just buried in the panel.
  • Progress vs. "just staying busy"state.message is the latest progress line and shows up in the Down-panel subagent detail view alongside status + elapsed runtime; cells update in place rather than re-inserting into the stream (background_agents.rs:101-150).

Where I agree the model is still weak: there is no structured last verifier result field. Today the implicit verifier is whatever the subagent puts into its final message, gated only by the human-readable output_contract string. A small protocol addition — e.g. CollabAgentState.last_verification: { kind, passed, summary } populated by an explicit report_verification tool — would give the parent a typed receipt to gate the consumer decision on, instead of having to parse the free-form summary. The parallel-first doc already names "validation/test runner" as a lane role, so this slots in without expanding the policy surface.

Happy to prototype that in the fork if it sounds useful.

LEON-gittech · 2 months ago

Agreed on the "fan out first, ask later" risk — that's why the policy doc leads with explicit caps, not just hints: 0 for trivial, 1 for one bounded evidence lane, 2–3 for multi-axis tasks, 4–6 only for broad audits (docs/parallel-first-agent-execution.md, "How Many Subagents to Spawn"). The spawn tool itself takes a max_concurrent_threads_per_session knob that gets injected into the model's tool description so the cap is part of the contract, not a silent ceiling (codex-rs/core/src/tools/handlers/multi_agents_spec.rs:33,710-716). And the four pre-spawn gates — Independent / Consumer decision / Bounded / Worth it — are required to pass before a spawn_agent is justified (same file, SPAWN_AGENT_CONTRACT_GUIDANCE_V2).

On the shared run contract, the local mapping is:

  • ownership: CollabAgentState.ownership (string-typed, e.g. write boundary), plumbed through to the Down panel detail view.
  • last-progress receipt: state.message updated live + status transition events (background_agents.rs:101-150).
  • terminal halt reason: CollabAgentStatus enum — Completed / Interrupted / Errored / Shutdown / NotFound — and a chat-stream info line emitted at the transition so the parent lane sees the halt without polling (background_agents.rs:192-234).
  • budget slice: this is the honest gap. There is a per-thread goal budget (tokens_used / token_budget in tools/handlers/goal.rs), but no per-subagent budget slice carved out of the parent and reconciled on close. So the panel can tell you a worker is still admitted, but not that its budget is still admitted.

So I think your framing is right: the receipt fields exist, the admission story doesn't — once each lane carries {spawn_reason, output_contract, budget_slice, last_progress_at} and the panel renders the first three plus a "stale" badge driven by the fourth, "still admitted" becomes a property of the worker, not a UI inference. Curious what MartinLoop landed on for budget reconciliation across parent ↔ child threads — that's the part that gets ugly when a child re-spawns.

Keesan12 · 1 month ago

Budget reconciliation got much saner for us once respawn stopped mutating the original slice.

A practical shape is to give every child run its own {budget_slice_id, parent_budget_id} plus admitted_tokens, spent_tokens, verifier_state, halt_reason, and handoff_to. If child A re-spawns child B, A closes with something like halt_reason=delegated_followup, and only A's committed spend rolls up. The parent can then admit the next step off (remaining_parent_budget, active_child_slices, last_verifier_delta) instead of a blob of descendant spend.

That boundary is what kept retries cheaper for us: stalled children could be cut cleanly without poisoning the whole parent budget or hiding where the spend actually went.

Keesan12 · 1 month ago

This seems like the right place to separate background task exists from admission to run again. If you can keep the parent goal visible while subagents are running, users can make a real choice about whether to keep waiting, stop, or fork. That would make me much more likely to trust parallel-first behavior in a long session.

Keesan12 · 1 month ago

The line that stands out is the distinction between foreground work and background work. That split is what keeps long-running subagents useful instead of noisy. I would make stop_reason and next_attempt_allowed visible at the task level too, so the operator can tell whether the loop actually moved.

torramlabs · 1 month ago

One extra guardrail I would want before parallel-first is a typed next_attempt_allowed plus halt_reason on each child, so the parent can decide whether to keep waiting, re-dispatch, or stop without reading tea leaves from a spinner. That keeps background work nonblocking while preventing the "looks busy / actually wedged" failure mode.