Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions tests/tools/test_kanban_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,83 @@ def test_create_happy_path(worker_env):
conn.close()


def test_create_inherits_worker_dir_workspace(monkeypatch, worker_env):
"""A worker scoped to a dir: task that spawns a child without a
workspace arg inherits the dir, not scratch (so follow-up code-gen
lands in the same project)."""
from tools import kanban_tools as kt
from hermes_cli import kanban_db as kb

proj = "/home/teknium/myproject"
conn = kb.connect()
try:
self_tid = kb.create_task(
conn, title="dir worker", assignee="test-worker",
workspace_kind="dir", workspace_path=proj,
)
kb.claim_task(conn, self_tid)
finally:
conn.close()
monkeypatch.setenv("HERMES_KANBAN_TASK", self_tid)

d = json.loads(kt._handle_create({"title": "follow-up", "assignee": "peer"}))
assert d["ok"] is True
conn = kb.connect()
try:
child = kb.get_task(conn, d["task_id"])
assert child.workspace_kind == "dir"
assert child.workspace_path == proj
finally:
conn.close()


def test_create_explicit_workspace_beats_inheritance(monkeypatch, worker_env):
"""An explicit workspace arg overrides worker-task inheritance."""
from tools import kanban_tools as kt
from hermes_cli import kanban_db as kb

conn = kb.connect()
try:
self_tid = kb.create_task(
conn, title="dir worker", assignee="test-worker",
workspace_kind="dir", workspace_path="/home/teknium/proj",
)
kb.claim_task(conn, self_tid)
finally:
conn.close()
monkeypatch.setenv("HERMES_KANBAN_TASK", self_tid)

d = json.loads(kt._handle_create({
"title": "scratch child", "assignee": "peer",
"workspace_kind": "scratch",
}))
assert d["ok"] is True
conn = kb.connect()
try:
child = kb.get_task(conn, d["task_id"])
assert child.workspace_kind == "scratch"
finally:
conn.close()


def test_create_no_worker_task_stays_scratch(monkeypatch, worker_env):
"""Orchestrator/CLI callers (no HERMES_KANBAN_TASK) still default to
scratch — inheritance only applies to task-scoped workers."""
from tools import kanban_tools as kt
from hermes_cli import kanban_db as kb

monkeypatch.delenv("HERMES_KANBAN_TASK", raising=False)
d = json.loads(kt._handle_create({"title": "orch child", "assignee": "peer"}))
assert d["ok"] is True
conn = kb.connect()
try:
child = kb.get_task(conn, d["task_id"])
assert child.workspace_kind == "scratch"
assert child.workspace_path is None
finally:
conn.close()


def test_create_stamps_session_id_from_env(monkeypatch, worker_env):
"""When the agent loop runs under ACP, the server propagates the
originating chat session id via HERMES_SESSION_ID. ``kanban_create``
Expand Down
21 changes: 20 additions & 1 deletion tools/kanban_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -743,8 +743,18 @@ def _handle_create(args: dict, **kw) -> str:
# CLI / dashboard paths and on legacy hosts that don't set the env.
session_id = args.get("session_id") or os.environ.get("HERMES_SESSION_ID")
priority = args.get("priority")
workspace_kind = args.get("workspace_kind") or "scratch"
# Resolve workspace. If the caller passed one explicitly, honor it.
# Otherwise, a dispatcher-spawned worker (HERMES_KANBAN_TASK set)
# inherits its own running task's workspace, so a worker editing a
# dir:/worktree project that spawns a follow-up child keeps the child
# in that project instead of a throwaway scratch dir. Orchestrators
# (kanban toolset, no HERMES_KANBAN_TASK) and CLI/dashboard callers
# fall back to scratch as before. Explicit None path stays None.
workspace_kind = args.get("workspace_kind")
workspace_path = args.get("workspace_path")
_inherit_workspace = workspace_kind is None and workspace_path is None
if workspace_kind is None:
workspace_kind = "scratch"
triage, bool_error = _parse_bool_arg(args, "triage")
if bool_error:
return tool_error(bool_error)
Expand Down Expand Up @@ -773,6 +783,15 @@ def _handle_create(args: dict, **kw) -> str:
try:
kb, conn = _connect(board=board)
try:
# Inherit the spawning worker's own task workspace when the
# caller didn't specify one (see resolution note above).
if _inherit_workspace:
_self_tid = os.environ.get("HERMES_KANBAN_TASK")
if _self_tid:
_self_task = kb.get_task(conn, _self_tid)
if _self_task is not None and _self_task.workspace_kind:
workspace_kind = _self_task.workspace_kind
workspace_path = _self_task.workspace_path
new_tid = kb.create_task(
conn,
title=str(title).strip(),
Expand Down
Loading