fix(kanban): judge unachievable goals as blocked, never done
This commit is contained in:
+33
-10
@@ -2311,18 +2311,23 @@ def _worker_run_id_for(task_id: str) -> Optional[int]:
|
||||
return None
|
||||
|
||||
|
||||
def _goal_mode_handoff_rejection(task: Optional[kb.Task], evidence: str) -> Optional[str]:
|
||||
"""Apply the goal judge to every terminal worker handoff, including review."""
|
||||
def _goal_mode_handoff_rejection(task: Optional[kb.Task], evidence: str):
|
||||
"""Apply the goal judge to every terminal worker handoff, including review.
|
||||
|
||||
Returns ``(verdict, reason_or_None)`` — ``"done"`` allows the handoff;
|
||||
``"blocked"`` means the judge ruled the goal unachievable (#100954);
|
||||
``"continue"``/``"wait"`` reject with the judge's reason.
|
||||
"""
|
||||
if task is None or not task.goal_mode:
|
||||
return None
|
||||
return ("done", None)
|
||||
try:
|
||||
from agent.auxiliary_client import get_text_auxiliary_client
|
||||
|
||||
client, model = get_text_auxiliary_client("goal_judge")
|
||||
except Exception:
|
||||
return None
|
||||
return ("done", None)
|
||||
if client is None or not model:
|
||||
return None
|
||||
return ("done", None)
|
||||
|
||||
from hermes_cli.goals import judge_goal
|
||||
|
||||
@@ -2341,7 +2346,7 @@ def _goal_mode_handoff_rejection(task: Optional[kb.Task], evidence: str) -> Opti
|
||||
judge_exc,
|
||||
exc_info=True,
|
||||
)
|
||||
return reason if verdict != "done" else None
|
||||
return (verdict, None if verdict == "done" else reason)
|
||||
|
||||
|
||||
def _cmd_complete(args: argparse.Namespace) -> int:
|
||||
@@ -2379,11 +2384,21 @@ def _cmd_complete(args: argparse.Namespace) -> int:
|
||||
# to every terminal handoff so request-review cannot bypass the
|
||||
# acceptance contract that protects complete.
|
||||
task = kb.get_task(conn, tid)
|
||||
rejection = _goal_mode_handoff_rejection(
|
||||
gate_verdict, rejection = _goal_mode_handoff_rejection(
|
||||
task,
|
||||
(summary or args.result or "").strip(),
|
||||
)
|
||||
if rejection is not None:
|
||||
if gate_verdict == "blocked":
|
||||
print(
|
||||
f"kanban: goal completion of {tid} rejected: judge ruled "
|
||||
f"the goal unachievable — {rejection}. Re-scope with "
|
||||
f"kanban edit, or record the block with kanban block "
|
||||
f"instead of completing.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
failed.append(tid)
|
||||
continue
|
||||
if gate_verdict == "continue" or rejection is not None:
|
||||
print(
|
||||
f"kanban: goal completion of {tid} rejected by judge: {rejection}. "
|
||||
f"Provide evidence matching the task's acceptance criteria.",
|
||||
@@ -2532,11 +2547,19 @@ def _cmd_request_review(args: argparse.Namespace) -> int:
|
||||
return 2
|
||||
reviewer = getattr(args, "reviewer", None)
|
||||
with kb.connect_closing() as conn:
|
||||
rejection = _goal_mode_handoff_rejection(
|
||||
gate_verdict, rejection = _goal_mode_handoff_rejection(
|
||||
kb.get_task(conn, tid),
|
||||
summary or "",
|
||||
)
|
||||
if rejection is not None:
|
||||
if gate_verdict == "blocked":
|
||||
print(
|
||||
f"kanban: goal review handoff of {tid} rejected: judge ruled "
|
||||
f"the goal unachievable — {rejection}. Record the block with "
|
||||
f"kanban block instead of requesting review.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
if gate_verdict == "continue" or rejection is not None:
|
||||
print(
|
||||
f"kanban: goal review handoff of {tid} rejected by judge: "
|
||||
f"{rejection}. Provide acceptance evidence matching the task.",
|
||||
|
||||
Reference in New Issue
Block a user