fix: assign single-task kanban decompositions

This commit is contained in:
DoGMaTiiC 2026-05-18 20:25:56 -07:00 committed by Teknium
parent 6c4f11c64a
commit 4da4133d34
3 changed files with 165 additions and 18 deletions

View file

@ -114,7 +114,7 @@ def test_decompose_with_fanout_creates_children(kanban_home):
assert c1.assignee == "engineer"
def test_decompose_fanout_false_falls_back_to_specify(kanban_home):
def test_decompose_fanout_false_assigns_default_when_unassigned(kanban_home):
with kb.connect() as conn:
tid = kb.create_task(conn, title="just one thing", triage=True)
@ -125,11 +125,14 @@ def test_decompose_fanout_false_falls_back_to_specify(kanban_home):
"body": "**Goal**\nDo the thing.",
})
patches = _patch_list_profiles(["orchestrator"])
patches = _patch_list_profiles(["orchestrator", "fallback"])
for p in patches:
p.start()
try:
with _patch_aux_client(llm_payload), _patch_extra_body():
with _patch_aux_client(llm_payload), _patch_extra_body(), patch(
"hermes_cli.kanban_decompose._load_config",
return_value={"kanban": {"default_assignee": "fallback"}},
):
outcome = decomp.decompose_task(tid, author="me")
finally:
for p in patches:
@ -140,9 +143,113 @@ def test_decompose_fanout_false_falls_back_to_specify(kanban_home):
assert outcome.new_title == "Tightened title"
with kb.connect() as conn:
task = kb.get_task(conn, tid)
assert task is not None
# specify path with no parents -> recompute_ready flips to 'ready'
assert task.status == "ready"
assert task.title == "Tightened title"
assert task.assignee == "fallback"
def test_decompose_fanout_false_preserves_existing_assignee(kanban_home):
with kb.connect() as conn:
tid = kb.create_task(
conn,
title="already routed",
assignee="engineer",
triage=True,
)
llm_payload = jsonlib.dumps({
"fanout": False,
"rationale": "single unit",
"title": "Tightened title",
"body": "Keep existing lane.",
"assignee": "fallback",
})
patches = _patch_list_profiles(["orchestrator", "engineer", "fallback"])
for p in patches:
p.start()
try:
with _patch_aux_client(llm_payload), _patch_extra_body(), patch(
"hermes_cli.kanban_decompose._load_config",
return_value={"kanban": {"default_assignee": "fallback"}},
):
outcome = decomp.decompose_task(tid, author="me")
finally:
for p in patches:
p.stop()
assert outcome.ok, outcome.reason
with kb.connect() as conn:
task = kb.get_task(conn, tid)
assert task is not None
assert task.assignee == "engineer"
assert task.title == "Tightened title"
def test_decompose_fanout_false_uses_valid_llm_assignee(kanban_home):
with kb.connect() as conn:
tid = kb.create_task(conn, title="route me", triage=True)
llm_payload = jsonlib.dumps({
"fanout": False,
"rationale": "single unit",
"title": "Tightened title",
"body": "Route to specialist.",
"assignee": "engineer",
})
patches = _patch_list_profiles(["orchestrator", "engineer", "fallback"])
for p in patches:
p.start()
try:
with _patch_aux_client(llm_payload), _patch_extra_body(), patch(
"hermes_cli.kanban_decompose._load_config",
return_value={"kanban": {"default_assignee": "fallback"}},
):
outcome = decomp.decompose_task(tid, author="me")
finally:
for p in patches:
p.stop()
assert outcome.ok, outcome.reason
with kb.connect() as conn:
task = kb.get_task(conn, tid)
assert task is not None
assert task.assignee == "engineer"
def test_decompose_fanout_false_invalid_llm_assignee_uses_default(kanban_home):
with kb.connect() as conn:
tid = kb.create_task(conn, title="route me safely", triage=True)
llm_payload = jsonlib.dumps({
"fanout": False,
"rationale": "single unit",
"title": "Tightened title",
"body": "Route to fallback.",
"assignee": "made_up",
})
patches = _patch_list_profiles(["orchestrator", "fallback"])
for p in patches:
p.start()
try:
with _patch_aux_client(llm_payload), _patch_extra_body(), patch(
"hermes_cli.kanban_decompose._load_config",
return_value={"kanban": {"default_assignee": "fallback"}},
):
outcome = decomp.decompose_task(tid, author="me")
finally:
for p in patches:
p.stop()
assert outcome.ok, outcome.reason
with kb.connect() as conn:
task = kb.get_task(conn, tid)
assert task is not None
assert task.assignee == "fallback"
def test_decompose_unknown_assignee_falls_back_to_default(kanban_home):