From 9ca8ce4335072e9055359d3821d373e456fd97c6 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Mon, 20 Jul 2026 03:13:44 -0700 Subject: [PATCH] fix(kanban): unpack judge_goal's 4-tuple at the completion gate (#67973) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit judge_goal() returns (verdict, reason, parse_failed, wait_directive) since the goals.py wait-directive change, but the kanban goal-mode completion gate at tools/kanban_tools.py still unpacked 3 values. Every judge call raised ValueError, the defensive except swallowed it, and the pre-initialized verdict='done' let every completion through — the acceptance gate was silently disabled. Now unpacks all 4 values; the test mock is updated to match the real contract. The other two judge_goal consumers (hermes_cli/goals.py) already use 4-value unpacks. Reported and diagnosed by @bill3wits in PR #57276; reimplemented under project authorship because the original commit was authored under a non-existent local identity (bash@hermes.local) that cannot be carried into history. Also fixes #58066 (duplicate report by @Gibcity). --- tests/tools/test_kanban_tools.py | 4 +++- tools/kanban_tools.py | 7 ++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/tools/test_kanban_tools.py b/tests/tools/test_kanban_tools.py index 10eaef59695ce..e9ea181ec2a18 100644 --- a/tests/tools/test_kanban_tools.py +++ b/tests/tools/test_kanban_tools.py @@ -648,7 +648,9 @@ def test_complete_goal_mode_rejected_by_judge(monkeypatch, tmp_path): # Mock the judge to reject the completion. The gate only runs when a # judge is reachable, so force the availability probe True as well. def mock_judge_goal(goal, last_response, *, timeout=30.0, subgoals=None): - return "continue", "missing verification evidence", False + # Match the real judge_goal contract: + # (verdict, reason, parse_failed, wait_directive) + return "continue", "missing verification evidence", False, None monkeypatch.setattr("tools.kanban_tools.judge_goal", mock_judge_goal) monkeypatch.setattr("tools.kanban_tools._goal_judge_available", lambda: True) diff --git a/tools/kanban_tools.py b/tools/kanban_tools.py index 8d27522f18296..51fac3f8eaa2d 100644 --- a/tools/kanban_tools.py +++ b/tools/kanban_tools.py @@ -602,7 +602,12 @@ def _handle_complete(args: dict, **kw) -> str: verdict = "done" reason = "" try: - verdict, reason, _ = judge_goal( + # judge_goal returns (verdict, reason, parse_failed, + # wait_directive) — see hermes_cli/goals.py. Unpacking + # fewer raises ValueError, which the defensive handler + # below swallows, leaving verdict="done" and silently + # disabling the gate. + verdict, reason, _, _ = judge_goal( goal=f"{task.title}\n\n{task.body or ''}".strip(), last_response=(summary or result or "").strip(), )