# NousResearch/hermes-agent · Kanban config, concurrency, and child task promotion

## Open now

- Nothing open on GitHub.

These items address kanban dispatcher concurrency controls (capping concurrent workers, per-profile limits), a new config toggle for auto-promoting child tasks to ready, and related documentation and bug fixes.

## TL;DR

- 3 member(s): 0 open issue(s), 1 closed issue(s), 0 open PR(s), 1 merged PR(s), 1 PR(s) closed without merging.
- A fix was merged to main in PR #28344. Verify it addresses your case.
- **Recommended:** read PR #28344 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `resolved` · deciding PR(s): `pr-28344`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_SPANNING`): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
- Pairwise link density `0.67`: the share of member pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. `1.00` means every pair is linked.

## Closed / duplicate issues

- #28016: \[Feature\]: Config option to keep decomposed child tasks in todo instead of auto-promoting to ready

## Prior & competing PRs

- **#28344: feat(kanban): add auto_promote_children config toggle (#28220)**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`
  - files: `hermes_cli/kanban_db.py`, `hermes_cli/kanban_decompose.py`, `plugins/kanban/dashboard/plugin_api.py`
  - fix hunks:
  - `hermes_cli/kanban_db.py`

    ```diff
    @@ -2798,6 +2798,7 @@ def decompose_triage_task(
         root_assignee: Optional[str],
         children: list[dict],
         author: Optional[str] = None,
    +    auto_promote: bool = True,
     ) -> Optional[list[str]]:
         """Fan a triage task out into child tasks and promote the root to ``todo``.
     
    ```

    ```diff
    @@ -2983,8 +2984,11 @@ def decompose_triage_task(
     
         # Outside the write_txn: promote parent-free children to 'ready'
         # so the dispatcher picks them up on its next tick. Same pattern
    -    # specify_triage_task uses.
    -    recompute_ready(conn)
    +    # specify_triage_task uses.  When auto_promote is False children
    +    # stay in 'todo' until the user manually promotes them — useful
    +    # for manual-review-first workflows.
    +    if auto_promote:
    +        recompute_ready(conn)
         return child_ids
     
     
    ```
  - `hermes_cli/kanban_decompose.py`

    ```diff
    @@ -271,6 +271,8 @@ def decompose_task(
         cfg = _load_config()
         orchestrator = _resolve_orchestrator_profile(cfg)
         default_assignee = _resolve_default_assignee(cfg)
    +    kanban_cfg = cfg.get("kanban", {}) if isinstance(cfg, dict) else {}
    +    auto_promote = bool(kanban_cfg.get("auto_promote_children", True))
         roster, valid_names = _build_roster()
     
         try:
    ```

    ```diff
    @@ -410,6 +412,7 @@ def decompose_task(
                     root_assignee=orchestrator,
                     children=children,
                     author=audit_author,
    +                auto_promote=auto_promote,
                 )
         except ValueError as exc:
             return DecomposeOutcome(task_id, False, f"DB rejected graph: {exc}")
    ```
  - `plugins/kanban/dashboard/plugin_api.py`

    ```diff
    @@ -1701,6 +1701,7 @@ class OrchestrationSettingsBody(BaseModel):
         orchestrator_profile: Optional[str] = None
         default_assignee: Optional[str] = None
         auto_decompose: Optional[bool] = None
    +    auto_promote_children: Optional[bool] = None
     
     
     @router.get("/orchestration")
    ```

    ```diff
    @@ -1716,6 +1717,7 @@ def get_orchestration_settings():
         explicit_orch = (kanban_cfg.get("orchestrator_profile") or "").strip()
         explicit_default = (kanban_cfg.get("default_assignee") or "").strip()
         auto_decompose = bool(kanban_cfg.get("auto_decompose", True))
    +    auto_promote_children = bool(kanban_cfg.get("auto_promote_children", True))
     
         # Resolve fallbacks the same way the decomposer does.
         resolved_orch = explicit_orch
    ```

    ```diff
    @@ -1738,6 +1740,7 @@ def get_orchestration_settings():
             "orchestrator_profile": explicit_orch,
             "default_assignee": explicit_default,
             "auto_decompose": auto_decompose,
    +        "auto_promote_children": auto_promote_children,
             "resolved_orchestrator_profile": resolved_orch,
             "resolved_default_assignee": resolved_default,
             "active_profile": active_default,
    ```

    ```diff
    @@ -1803,6 +1806,9 @@ def set_orchestration_settings(payload: OrchestrationSettingsBody):
         if payload.auto_decompose is not None:
             kanban_section["auto_decompose"] = bool(payload.auto_decompose)
     
    +    if payload.auto_promote_children is not None:
    +        kanban_section["auto_promote_children"] = bool(payload.auto_promote_children)
    +
         try:
             save_config(cfg)
         except Exception as exc:
    ```
- **#28220: feat(kanban): add auto_promote_children config toggle**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `2e09d2567c851787fbc8f42ead23cf9ff9f06f6a`, `#28344`

## Fix status

- Merged to main in PR #28344 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-05-19`.
