From 26bf45f8c55ebf7fb79a988fd2de2d534fe6a96a Mon Sep 17 00:00:00 2001 From: Eric Litovsky Date: Wed, 6 May 2026 11:02:44 -0600 Subject: [PATCH] fix(kanban): parse include_archived explicitly --- tests/tools/test_kanban_tools.py | 46 ++++++++++++++++++++++++++++++++ tools/kanban_tools.py | 18 ++++++++++++- 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/tests/tools/test_kanban_tools.py b/tests/tools/test_kanban_tools.py index ae21366839e..c2f1f830628 100644 --- a/tests/tools/test_kanban_tools.py +++ b/tests/tools/test_kanban_tools.py @@ -180,6 +180,52 @@ def test_list_rejects_bad_limit(worker_env): assert json.loads(kt._handle_list({"limit": 0})).get("error") +def test_list_parses_include_archived_string_false(worker_env): + from hermes_cli import kanban_db as kb + conn = kb.connect() + try: + live = kb.create_task(conn, title="live task", assignee="factory") + archived = kb.create_task(conn, title="archived task", assignee="factory") + assert kb.archive_task(conn, archived) + finally: + conn.close() + + from tools import kanban_tools as kt + out = kt._handle_list({ + "assignee": "factory", + "include_archived": "false", + }) + ids = [t["id"] for t in json.loads(out)["tasks"]] + assert live in ids + assert archived not in ids + + +def test_list_parses_include_archived_string_true(worker_env): + from hermes_cli import kanban_db as kb + conn = kb.connect() + try: + live = kb.create_task(conn, title="live task", assignee="factory") + archived = kb.create_task(conn, title="archived task", assignee="factory") + assert kb.archive_task(conn, archived) + finally: + conn.close() + + from tools import kanban_tools as kt + out = kt._handle_list({ + "assignee": "factory", + "include_archived": "true", + }) + ids = [t["id"] for t in json.loads(out)["tasks"]] + assert live in ids + assert archived in ids + + +def test_list_rejects_bad_include_archived(worker_env): + from tools import kanban_tools as kt + out = kt._handle_list({"include_archived": "sometimes"}) + assert "include_archived must be" in json.loads(out).get("error", "") + + def test_complete_happy_path(worker_env): from tools import kanban_tools as kt out = kt._handle_complete({ diff --git a/tools/kanban_tools.py b/tools/kanban_tools.py index 754f77c2baa..d8ba7c725a0 100644 --- a/tools/kanban_tools.py +++ b/tools/kanban_tools.py @@ -145,6 +145,20 @@ def _normalize_profile(value: Any) -> Optional[str]: return text +def _parse_bool_arg(args: dict, name: str, *, default: bool = False): + value = args.get(name) + if value is None: + return default, None + if isinstance(value, bool): + return value, None + text = str(value).strip().lower() + if text in ("true", "1", "yes"): + return True, None + if text in ("false", "0", "no"): + return False, None + return default, f"{name} must be a boolean or 'true'/'false'" + + def _task_summary_dict(kb, conn, task) -> dict[str, Any]: """Compact task shape for board-listing tools.""" parents = kb.parent_ids(conn, task.id) @@ -250,7 +264,9 @@ def _handle_list(args: dict, **kw) -> str: assignee = args.get("assignee") status = args.get("status") tenant = args.get("tenant") - include_archived = bool(args.get("include_archived")) + include_archived, bool_error = _parse_bool_arg(args, "include_archived") + if bool_error: + return tool_error(bool_error) limit = args.get("limit") if limit is not None: try: