diff --git a/contributors/emails/MaxFreedomPollard@users.noreply.github.com b/contributors/emails/MaxFreedomPollard@users.noreply.github.com new file mode 100644 index 00000000000..e7c920af9fb --- /dev/null +++ b/contributors/emails/MaxFreedomPollard@users.noreply.github.com @@ -0,0 +1 @@ +MaxFreedomPollard diff --git a/hermes_cli/config.py b/hermes_cli/config.py index 25ec774da45..d02ac053126 100644 --- a/hermes_cli/config.py +++ b/hermes_cli/config.py @@ -1562,21 +1562,6 @@ DEFAULT_CONFIG = { # Example: 1800 = compact after 30 min idle. }, - # Kanban subsystem (orchestrator workers + dispatcher-driven child tasks). - # See tools/kanban_tools.py and hermes_cli/kanban_db.py for the actual - # implementations. Per-platform notification opt-out is handled by the - # kanban dashboard (see ``hermes dashboard`` -> Notifications). - "kanban": { - # Auto-subscribe the originating gateway/TUI session to task - # completion + block events when ``kanban_create`` is called from - # inside a session that has a persistent delivery channel. The - # agent that dispatched the task will get notified automatically - # instead of having to poll. Disable to mirror pre-feature - # behaviour — e.g. for a profile that prefers explicit - # ``kanban_notify-subscribe`` calls per task. - "auto_subscribe_on_create": True, - }, - # Anthropic prompt caching (Claude via OpenRouter or native Anthropic API). # cache_ttl must be "5m" or "1h" (Anthropic-supported tiers); other values are ignored. "prompt_caching": { @@ -2920,6 +2905,14 @@ DEFAULT_CONFIG = { # each claimable ready task. One dispatcher per profile is sufficient; # running more than one on the same kanban.db will race for claims. "kanban": { + # Auto-subscribe the originating gateway/TUI session to task + # completion + block events when ``kanban_create`` is called from + # inside a session that has a persistent delivery channel. The + # agent that dispatched the task will get notified automatically + # instead of having to poll. Disable to mirror pre-feature + # behaviour — e.g. for a profile that prefers explicit + # ``kanban_notify-subscribe`` calls per task. + "auto_subscribe_on_create": True, # Run the dispatcher inside the gateway process. On by default — # the cost is ~300µs every `dispatch_interval_seconds` when idle, # and gateway is the supervisor users already have. Set to false diff --git a/tests/hermes_cli/test_config.py b/tests/hermes_cli/test_config.py index e62fa1a3d79..22935d0bbe1 100644 --- a/tests/hermes_cli/test_config.py +++ b/tests/hermes_cli/test_config.py @@ -2385,3 +2385,35 @@ class TestProviderEnabledRuntimeGate: assert "disabled" not in str(e).lower() except Exception: pass # any non-ValueError is fine; we only gate the disabled path + + +# --------------------------------------------------------------------------- +# DEFAULT_CONFIG must not carry a duplicate "kanban" key +# --------------------------------------------------------------------------- + +def test_default_config_kanban_block_not_dropped_by_duplicate_key(): + """DEFAULT_CONFIG previously declared ``"kanban"`` twice, so Python kept + only the second literal and silently dropped the first — losing the + ``auto_subscribe_on_create`` default. Both sets of defaults must survive. + """ + kanban = DEFAULT_CONFIG["kanban"] + # From the first (dropped) block: + assert kanban.get("auto_subscribe_on_create") is True + # From the second block: + assert "dispatch_in_gateway" in kanban + assert "auto_decompose" in kanban + + +def test_default_config_has_no_duplicate_top_level_keys(): + """Guard against any duplicate key silently shadowing a default.""" + import ast + import hermes_cli.config as cfg_mod + + src = open(cfg_mod.__file__, encoding="utf-8").read() + tree = ast.parse(src) + for node in ast.walk(tree): + if isinstance(node, ast.Dict): + keys = [k.value for k in node.keys if isinstance(k, ast.Constant)] + if "model" in keys and "kanban" in keys: # the DEFAULT_CONFIG literal + dupes = {k for k in keys if keys.count(k) > 1} + assert not dupes, f"duplicate DEFAULT_CONFIG keys: {sorted(dupes)}"