mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-11 08:42:11 +00:00
fix(kanban): decompose children inherit root workspace instead of forcing scratch (#37172)
decompose_triage_task hardcoded every fan-out child to workspace_kind 'scratch', ignoring the root task's workspace. A code-gen task created with a dir:/worktree: workspace would fan out into throwaway scratch tmp dirs (GC'd on archive), so generated code never landed in the project. Children now inherit the root's workspace_kind + workspace_path. A child dict may still override with its own workspace_kind/workspace_path; the path only carries over when kinds match. Scratch roots are unchanged.
This commit is contained in:
parent
fa3b06b035
commit
72e82f88c0
2 changed files with 87 additions and 3 deletions
|
|
@ -4353,13 +4353,21 @@ def decompose_triage_task(
|
|||
child_ids: list[str] = []
|
||||
with write_txn(conn):
|
||||
root_row = conn.execute(
|
||||
"SELECT id, status, tenant FROM tasks WHERE id = ?", (task_id,)
|
||||
"SELECT id, status, tenant, workspace_kind, workspace_path "
|
||||
"FROM tasks WHERE id = ?",
|
||||
(task_id,),
|
||||
).fetchone()
|
||||
if root_row is None:
|
||||
return None
|
||||
if root_row["status"] != "triage":
|
||||
return None
|
||||
tenant = root_row["tenant"]
|
||||
# Children inherit the root's workspace by default so a fan-out
|
||||
# of a code-gen task lands in the parent's project dir/worktree
|
||||
# rather than throwaway scratch tmp dirs. A child dict can still
|
||||
# override with its own 'workspace_kind' / 'workspace_path'.
|
||||
root_ws_kind = root_row["workspace_kind"] or "scratch"
|
||||
root_ws_path = root_row["workspace_path"]
|
||||
|
||||
# Create children. Status is 'todo' regardless of parents — we
|
||||
# link them under the root AFTER creation so the dispatcher
|
||||
|
|
@ -4370,16 +4378,30 @@ def decompose_triage_task(
|
|||
title = child["title"].strip()
|
||||
body = child.get("body")
|
||||
assignee = _canonical_assignee(child.get("assignee"))
|
||||
# Per-child override wins; otherwise inherit the root's
|
||||
# workspace. A child that sets workspace_kind without a path
|
||||
# falls back to the root path only when kinds match (so a
|
||||
# child can't accidentally point a 'dir' at the root's
|
||||
# worktree path or vice versa).
|
||||
child_ws_kind = child.get("workspace_kind") or root_ws_kind
|
||||
if child.get("workspace_path"):
|
||||
child_ws_path = child.get("workspace_path")
|
||||
elif child_ws_kind == root_ws_kind:
|
||||
child_ws_path = root_ws_path
|
||||
else:
|
||||
child_ws_path = None
|
||||
conn.execute(
|
||||
"INSERT INTO tasks "
|
||||
"(id, title, body, assignee, status, workspace_kind, "
|
||||
" tenant, created_at, created_by) "
|
||||
"VALUES (?, ?, ?, ?, 'todo', 'scratch', ?, ?, ?)",
|
||||
" workspace_path, tenant, created_at, created_by) "
|
||||
"VALUES (?, ?, ?, ?, 'todo', ?, ?, ?, ?, ?)",
|
||||
(
|
||||
new_id,
|
||||
title,
|
||||
body if isinstance(body, str) else None,
|
||||
assignee,
|
||||
child_ws_kind,
|
||||
child_ws_path,
|
||||
tenant,
|
||||
now,
|
||||
(author or "decomposer"),
|
||||
|
|
|
|||
|
|
@ -166,3 +166,65 @@ def test_decompose_records_audit_comment_and_event(kanban_home):
|
|||
|
||||
assert any("Decomposed into" in (c.body or "") for c in comments)
|
||||
assert any(ev.kind == "decomposed" for ev in events)
|
||||
|
||||
|
||||
def test_decompose_children_inherit_dir_workspace(kanban_home):
|
||||
"""Fan-out children inherit the root's dir workspace, not scratch."""
|
||||
proj = "/home/teknium/myproject"
|
||||
with kb.connect() as conn:
|
||||
tid = kb.create_task(
|
||||
conn, title="codegen root", assignee="worker",
|
||||
workspace_kind="dir", workspace_path=proj, triage=True,
|
||||
)
|
||||
child_ids = kb.decompose_triage_task(
|
||||
conn, tid, root_assignee="orchestrator",
|
||||
children=[{"title": "part A"}, {"title": "part B", "parents": [0]}],
|
||||
author="decomposer",
|
||||
)
|
||||
assert child_ids and len(child_ids) == 2
|
||||
with kb.connect() as conn:
|
||||
for cid in child_ids:
|
||||
t = kb.get_task(conn, cid)
|
||||
assert t.workspace_kind == "dir"
|
||||
assert t.workspace_path == proj
|
||||
|
||||
|
||||
def test_decompose_children_stay_scratch_when_root_scratch(kanban_home):
|
||||
"""No regression: a scratch root still fans out into scratch children."""
|
||||
with kb.connect() as conn:
|
||||
tid = kb.create_task(
|
||||
conn, title="scratch root", assignee="worker",
|
||||
workspace_kind="scratch", triage=True,
|
||||
)
|
||||
child_ids = kb.decompose_triage_task(
|
||||
conn, tid, root_assignee="orchestrator",
|
||||
children=[{"title": "s1"}], author="decomposer",
|
||||
)
|
||||
with kb.connect() as conn:
|
||||
t = kb.get_task(conn, child_ids[0])
|
||||
assert t.workspace_kind == "scratch"
|
||||
assert t.workspace_path is None
|
||||
|
||||
|
||||
def test_decompose_per_child_workspace_override(kanban_home):
|
||||
"""An explicit per-child workspace beats inheritance."""
|
||||
proj = "/home/teknium/myproject"
|
||||
with kb.connect() as conn:
|
||||
tid = kb.create_task(
|
||||
conn, title="root", assignee="worker",
|
||||
workspace_kind="dir", workspace_path=proj, triage=True,
|
||||
)
|
||||
child_ids = kb.decompose_triage_task(
|
||||
conn, tid, root_assignee="orchestrator",
|
||||
children=[
|
||||
{"title": "override", "workspace_kind": "dir",
|
||||
"workspace_path": "/other/repo"},
|
||||
{"title": "inherit"},
|
||||
],
|
||||
author="decomposer",
|
||||
)
|
||||
with kb.connect() as conn:
|
||||
over = kb.get_task(conn, child_ids[0])
|
||||
inh = kb.get_task(conn, child_ids[1])
|
||||
assert over.workspace_path == "/other/repo"
|
||||
assert inh.workspace_path == proj
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue