Skip to content

feat(subagent): add session-wide cumulative spawn cap - #6669

Merged
bug-ops merged 1 commit into
mainfrom
feat/issue-6545/parity-subagent-no-cumulative
Jul 28, 2026
Merged

feat(subagent): add session-wide cumulative spawn cap#6669
bug-ops merged 1 commit into
mainfrom
feat/issue-6545/parity-subagent-no-cumulative

Conversation

@bug-ops

@bug-ops bug-ops commented Jul 28, 2026

Copy link
Copy Markdown
Owner

Summary

Zeph's zeph-subagent crate guards runaway spawning along two axes only: recursion depth (max_spawn_depth) and in-flight concurrency (max_concurrent). Neither bounds the cumulative total number of subagents spawned over a session's lifetime, leaving a shallow, low-concurrency but high-frequency sequential delegation loop unprotected. This mirrors a guardrail Claude Code shipped default-on in the same release wave (CLAUDE_CODE_MAX_SUBAGENTS_PER_SESSION), and follows the same "default-on, not opt-in" precedent set by #6469/PR #6528 for the tool-call rate limiter and cost cap.

  • Adds SessionSpawnBudget (crates/zeph-subagent/src/budget.rs): a plain AtomicUsize newtype, no Arc/Clone by design. SubAgentManager owns the origin instance; OrchestrationState owns an independent fallback used only when no manager is wired (serve/daemon/acp). Both are reached through a single accessor that cannot desynchronize by construction.
  • Two spawn paths are covered: SubAgentManager::spawn/resume, and the ACP /subagent spawn external-process chokepoint, which never touches SubAgentManager at all and would otherwise bypass the cap entirely.
  • Check/consume split: the budget is checked read-only at the guard but consumed only at each path's true resource-commit point, so a spawn rejected for any other reason (NotFound, a retried ConcurrencyLimit, a failed ACP launch) never burns budget it never used. On the ACP path specifically, the commit point is unconditional after the process launch attempt returns (both Ok and Err), since the underlying zeph_acp::run_session can launch a real child process and then time out or fail post-launch — undercounting that case would leave a real bypass.
  • New SubAgentConfig.max_spawns_per_session (default 100, 0 = unlimited sentinel), new SubAgentError::SessionSpawnLimit variant, a --migrate-config step, and /agent status//agent list now surface Session spawns: N/max.
  • SessionSpawnLimit classifies into the orchestration scheduler's terminal Failed branch (bounded retries only under FailureStrategy::Retry, per max_retries), not the transient ConcurrencyLimit-style retry branch.

Closes #6545

Test plan

  • cargo +nightly fmt --check
  • cargo clippy --profile ci --workspace --all-targets --features "desktop,ide,server,chat,pdf,scheduler,testing" -- -D warnings
  • cargo nextest run --config-file .github/nextest.toml --workspace --features "desktop,ide,server,chat,pdf,scheduler" --lib --bins (15128 passed, 0 failed)
  • Rustdoc gate (RUSTFLAGS="-D warnings" RUSTDOCFLAGS="--deny rustdoc::broken_intra_doc_links") clean workspace-wide
  • cargo test --doc --workspace clean
  • gitleaks protect --staged clean
  • Unit tests: cap reached, 0-unlimited sentinel, check/consume split (rejected spawns don't consume), resume() parity, priority over ConcurrencyLimit, ACP-path launch-then-fail still consumes, config default/migration (both serde round-trip and direct SubAgentConfig::default()), full MIGRATIONS registry migration test, scheduler Failed-not-retried classification (plus bounded-retry case under FailureStrategy::Retry), rendered status-line tests
  • Testing playbook and coverage-status row added (.local/testing/)

@github-actions github-actions Bot added enhancement New feature or request size/XL Extra large PR (500+ lines) documentation Improvements or additions to documentation rust Rust code changes core zeph-core crate config Configuration file changes labels Jul 28, 2026
Zeph's existing subagent guardrails bound recursion depth and in-flight
concurrency but not the cumulative total spawned over a session's
lifetime, leaving a shallow, low-concurrency but high-frequency
sequential delegation loop unprotected. Adds a configurable, default-on
SessionSpawnBudget shared between SubAgentManager::spawn/resume and the
ACP /subagent spawn chokepoint, so both spawn paths enforce the same
session-wide cap.

Closes #6545
@bug-ops
bug-ops force-pushed the feat/issue-6545/parity-subagent-no-cumulative branch from 290034b to 3f0ebc0 Compare July 28, 2026 00:13
@bug-ops
bug-ops enabled auto-merge (squash) July 28, 2026 00:13
@bug-ops
bug-ops merged commit 5ec0f49 into main Jul 28, 2026
47 checks passed
@bug-ops
bug-ops deleted the feat/issue-6545/parity-subagent-no-cumulative branch July 28, 2026 00:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

config Configuration file changes core zeph-core crate documentation Improvements or additions to documentation enhancement New feature or request rust Rust code changes size/XL Extra large PR (500+ lines)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

parity(subagent): no cumulative session-wide cap on total subagent spawns

1 participant