Version: hermes-agent 0.18.2, Windows 11, gateway-embedded dispatcher.
Summary
The dispatcher deliberately respects a "non-spawnable assignee" convention: dispatch_once skips ready/review tasks whose assignee doesn't resolve to a real profile (skipped_nonspawnable; the profile_exists filter in has_spawnable_ready). The auto-decomposer does not: it selects triage tasks by status alone, and _normalize_assignee_choice rewrites any non-profile assignee to kanban.default_assignee on every child, while the root task is reassigned to kanban.orchestrator_profile. With auto_promote_children (default true) the children go straight to ready and live workers spawn.
A task that was intentionally parked by assigning it to a non-profile name is therefore silently un-parked, reassigned, and executed.
Steps to reproduce
config.yaml: kanban.auto_decompose: true, with orchestrator_profile and default_assignee set to a real profile.
hermes kanban create "parked task - do not run" --assignee not-a-profile --triage
- Wait one dispatcher tick (~60 s).
- The card fans out into children assigned to real profiles, the root is reassigned to the orchestrator profile, children promote to
ready, and workers spawn. The explicit non-profile assignee survives nowhere.
Observed impact
We run shadow/dry-run pipelines contained by non-spawnable assignees. On 2026-07-11 the decomposer fanned a contained triage card into 6 children assigned to a live profile; they auto-promoted, spawned, and ran to completion - live agent tokens spent on work that was parked for human review.
Expected
The decomposer should honor the same convention the dispatcher already does - a task whose assignee is set but doesn't resolve to a real profile stays parked. Any of these would resolve it:
- (a)
decompose_task returns ok=False, reason="assignee is not a spawnable profile - parked" when task.assignee is non-empty and profile_exists() is false (mirrors dispatch semantics);
- (b) the same filter in
list_triage_ids;
- (c) per-board
auto_decompose in board.json (currently global-only);
- (d) a per-card
no_decompose/hold flag.
Relevant code (v0.18.2)
hermes_cli/kanban_decompose.py - triage-only guard ~L288; _normalize_assignee_choice ~L252; list_triage_ids ~L468
gateway/kanban_watchers.py - _auto_decompose_tick ~L1134
hermes_cli/kanban_db.py - decompose_triage_task ~L4984; children promoted via recompute_ready ~L5202
Version: hermes-agent 0.18.2, Windows 11, gateway-embedded dispatcher.
Summary
The dispatcher deliberately respects a "non-spawnable assignee" convention:
dispatch_onceskips ready/review tasks whose assignee doesn't resolve to a real profile (skipped_nonspawnable; theprofile_existsfilter inhas_spawnable_ready). The auto-decomposer does not: it selects triage tasks by status alone, and_normalize_assignee_choicerewrites any non-profile assignee tokanban.default_assigneeon every child, while the root task is reassigned tokanban.orchestrator_profile. Withauto_promote_children(default true) the children go straight toreadyand live workers spawn.A task that was intentionally parked by assigning it to a non-profile name is therefore silently un-parked, reassigned, and executed.
Steps to reproduce
config.yaml:kanban.auto_decompose: true, withorchestrator_profileanddefault_assigneeset to a real profile.hermes kanban create "parked task - do not run" --assignee not-a-profile --triageready, and workers spawn. The explicit non-profile assignee survives nowhere.Observed impact
We run shadow/dry-run pipelines contained by non-spawnable assignees. On 2026-07-11 the decomposer fanned a contained triage card into 6 children assigned to a live profile; they auto-promoted, spawned, and ran to completion - live agent tokens spent on work that was parked for human review.
Expected
The decomposer should honor the same convention the dispatcher already does - a task whose assignee is set but doesn't resolve to a real profile stays parked. Any of these would resolve it:
decompose_taskreturnsok=False, reason="assignee is not a spawnable profile - parked"whentask.assigneeis non-empty andprofile_exists()is false (mirrors dispatch semantics);list_triage_ids;auto_decomposeinboard.json(currently global-only);no_decompose/hold flag.Relevant code (v0.18.2)
hermes_cli/kanban_decompose.py- triage-only guard ~L288;_normalize_assignee_choice~L252;list_triage_ids~L468gateway/kanban_watchers.py-_auto_decompose_tick~L1134hermes_cli/kanban_db.py-decompose_triage_task~L4984; children promoted viarecompute_ready~L5202