From ce996d40577c242dc04cc6d66e827dcdf8daa569 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Sat, 15 Aug 2026 00:20:32 -0700 Subject: [PATCH] feat(delegation): raise max_concurrent_children default 3 -> 10 (+migration) (#86745) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit delegation.max_concurrent_children caps how many delegated children run in parallel per batch (and concurrent background delegation units). The old default of 3 needlessly serialized independent fan-outs (e.g. reviewing/​investigating N PRs or issues at once), so large batches ran in slow chunks of 3. Raise the shipped default to 10, which sits at/below the existing high-cost advisory threshold (>10), so the default never trips the warning. Each child still consumes API tokens independently, so this is a throughput/latency win the user pays for in parallel token spend — the floor stays 1 and there is no ceiling, so anyone can tune it down or up. - config_defaults.py: default 3 -> 10; _config_version 36 -> 37. - delegate_tool.py: _DEFAULT_MAX_CONCURRENT_CHILDREN 3 -> 10 (+ docstring). - config_migrations.py: _migrate_to_37 lifts configs pinned at exactly the old default 3 to 10 (deliberate non-3 overrides preserved; unset inherits 10). - cli-config.yaml.example: documented default updated. Verified: default/fallback read 10, version 37, and the migration lifts 3->10, preserves an explicit 5, and leaves unset untouched. Co-authored-by: Teknium --- cli-config.yaml.example | 2 +- hermes_cli/config_defaults.py | 4 ++-- hermes_cli/config_migrations.py | 32 ++++++++++++++++++++++++++++++++ tools/delegate_tool.py | 4 ++-- 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/cli-config.yaml.example b/cli-config.yaml.example index 92a194e1bc..4dee7a61f3 100644 --- a/cli-config.yaml.example +++ b/cli-config.yaml.example @@ -1396,7 +1396,7 @@ code_execution: # Supports single tasks and batch mode (default 3 parallel, configurable). delegation: max_iterations: 250 # Max tool-calling turns per child (default: 250) - # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling). + # max_concurrent_children: 10 # Max parallel child agents per batch (default: 10, floor: 1, no ceiling). # WARNING: values above 10 multiply API cost linearly. # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat). # Raise to 2 to allow workers to spawn their own subagents. diff --git a/hermes_cli/config_defaults.py b/hermes_cli/config_defaults.py index 5ffd661354..0f29f5059e 100644 --- a/hermes_cli/config_defaults.py +++ b/hermes_cli/config_defaults.py @@ -1813,7 +1813,7 @@ DEFAULT_CONFIG = { # (floor 30s) to enforce a hard cap. "reasoning_effort": "", # subagent effort: "ultra", "max", "xhigh", "high", # "medium", "low", "minimal", "none" (empty = inherit) - "max_concurrent_children": 3, # unified concurrency cap: max parallel children per batch + "max_concurrent_children": 10, # unified concurrency cap: max parallel children per batch # AND max concurrent background (background=true) # delegation units. New async dispatches beyond the cap # fall back to synchronous execution. Floor of 1, no ceiling. @@ -3402,7 +3402,7 @@ DEFAULT_CONFIG = { }, # Config schema version - bump this when adding new required fields - "_config_version": 36, + "_config_version": 37, } # Optional environment variables that enhance functionality diff --git a/hermes_cli/config_migrations.py b/hermes_cli/config_migrations.py index 54f360c319..b1464604fa 100644 --- a/hermes_cli/config_migrations.py +++ b/hermes_cli/config_migrations.py @@ -784,6 +784,37 @@ def _migrate_to_36(results: Dict[str, Any], quiet: bool) -> None: ) +def _migrate_to_37(results: Dict[str, Any], quiet: bool) -> None: + # ── Version 36 → 37: raise the delegation concurrency default 3 → 10 ── + # delegation.max_concurrent_children caps how many children run in parallel + # per batch (and concurrent background delegation units). The old default of + # 3 needlessly serialized independent fan-outs (e.g. reviewing N PRs at + # once). The shipped default is now 10, which stays at/below the high-cost + # warning threshold. Configs still pinned at exactly the old default 3 — + # almost always the inherited default rather than a deliberate choice — are + # lifted to 10 so existing installs get the wider fan-out on update. Any + # OTHER explicit value (a deliberate override) is preserved; unset inherits + # 10 at read time. + _c = _cfg() + read_raw_config = _c.read_raw_config + _persist_migration = _c._persist_migration + + config = read_raw_config() + raw_deleg = config.get("delegation") + if isinstance(raw_deleg, dict) and raw_deleg.get("max_concurrent_children") == 3: + raw_deleg["max_concurrent_children"] = 10 + config["delegation"] = raw_deleg + _persist_migration(config) + results["config_added"].append("delegation.max_concurrent_children=10 (was: 3)") + if not quiet: + print( + " ✓ Raised delegation.max_concurrent_children from 3 to 10 — " + "independent delegated children now fan out wider in parallel. " + "Each child consumes API tokens independently; set " + "delegation.max_concurrent_children back to 3 to restore the old cap." + ) + + #: Registry of (target_version, migration_fn), strictly ascending. The driver #: applies every entry whose target version is greater than the on-disk #: observe earlier steps' writes via read_raw_config() (filesystem state). @@ -807,6 +838,7 @@ MIGRATIONS: Tuple[Tuple[int, Callable[[Dict[str, Any], bool], None]], ...] = ( (34, _migrate_to_34), (35, _migrate_to_35), (36, _migrate_to_36), + (37, _migrate_to_37), ) diff --git a/tools/delegate_tool.py b/tools/delegate_tool.py index fd47d15133..a8da6988a5 100644 --- a/tools/delegate_tool.py +++ b/tools/delegate_tool.py @@ -119,7 +119,7 @@ def _get_subagent_approval_callback(): # "delegation" toolset in _build_child_agent), NOT by the model naming toolsets # — the model has no toolsets argument. Subagents inherit the parent's toolsets. -_DEFAULT_MAX_CONCURRENT_CHILDREN = 3 +_DEFAULT_MAX_CONCURRENT_CHILDREN = 10 # One-shot guard: the high-concurrency cost advisory is emitted at most once # per process. _get_max_concurrent_children() runs on every get_definitions() # schema rebuild (via _build_top_level_description / _build_tasks_param_description), @@ -733,7 +733,7 @@ def _normalize_role(r: Optional[str]) -> str: def _get_max_concurrent_children() -> int: """Read delegation.max_concurrent_children from config, falling back to - DELEGATION_MAX_CONCURRENT_CHILDREN env var, then the default (3). + DELEGATION_MAX_CONCURRENT_CHILDREN env var, then the default (10). Users can raise this as high as they want; only the floor (1) is enforced.