mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-06 07:51:53 +00:00
feat(kanban): configure worktree paths and branches
Salvages #26496 by @aqilaziz. Adds branch_name column + CLI flag so
tasks with workspace_kind='worktree' can pin a target branch on
create. Schema migration added to _migrate_add_optional_columns.
- Task.branch_name field + DB column + migration
- create_task accepts branch_name kwarg
- hermes kanban create --branch <name> flag
- kanban show output includes 'Branch: <name>' when set
Cherry-picked the substantive commit (a7558cf27); the PR's tip was
an unrelated service-path-dirs commit. Resolved 2 INSERT-column-list
and show-output conflicts alongside main's session_id and
max_runtime_seconds additions; kept all three.
This commit is contained in:
parent
53cf82a1ea
commit
1733cb3a13
6 changed files with 145 additions and 18 deletions
|
|
@ -32,6 +32,7 @@ def kanban_home(tmp_path, monkeypatch):
|
|||
[
|
||||
("scratch", ("scratch", None)),
|
||||
("worktree", ("worktree", None)),
|
||||
("worktree:/tmp/wt", ("worktree", "/tmp/wt")),
|
||||
("dir:/tmp/work", ("dir", "/tmp/work")),
|
||||
],
|
||||
)
|
||||
|
|
@ -45,8 +46,12 @@ def test_parse_workspace_flag_expands_user():
|
|||
assert path.endswith("/vault")
|
||||
assert not path.startswith("~")
|
||||
|
||||
kind, path = kc._parse_workspace_flag("worktree:~/trees/t6-wire")
|
||||
assert kind == "worktree"
|
||||
assert path.endswith("/trees/t6-wire")
|
||||
assert not path.startswith("~")
|
||||
|
||||
@pytest.mark.parametrize("bad", ["cloud", "dir:", "", "worktree:/x"])
|
||||
@pytest.mark.parametrize("bad", ["cloud", "dir:", "worktree:", ""])
|
||||
def test_parse_workspace_flag_rejects(bad):
|
||||
if not bad:
|
||||
# Empty -> defaults; not an error.
|
||||
|
|
@ -56,6 +61,17 @@ def test_parse_workspace_flag_rejects(bad):
|
|||
kc._parse_workspace_flag(bad)
|
||||
|
||||
|
||||
def test_parse_branch_flag_rejects_empty_and_option_like():
|
||||
assert kc._parse_branch_flag(None) is None
|
||||
assert kc._parse_branch_flag(" wt/t6-wire ") == "wt/t6-wire"
|
||||
with pytest.raises(argparse.ArgumentTypeError):
|
||||
kc._parse_branch_flag(" ")
|
||||
with pytest.raises(argparse.ArgumentTypeError):
|
||||
kc._parse_branch_flag("-bad")
|
||||
with pytest.raises(argparse.ArgumentTypeError):
|
||||
kc._parse_branch_flag("bad branch")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# run_slash smoke tests (end-to-end via the same entry both CLI and gateway use)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
@ -74,6 +90,27 @@ def test_run_slash_create_and_list(kanban_home):
|
|||
assert "alice" in out
|
||||
|
||||
|
||||
def test_run_slash_create_worktree_path_and_branch(kanban_home, tmp_path):
|
||||
target = tmp_path / ".worktrees" / "t6-wire"
|
||||
target_arg = target.as_posix()
|
||||
out = kc.run_slash(
|
||||
f"create 'ship worktree' --workspace worktree:{target_arg} --branch wt/t6-wire"
|
||||
)
|
||||
assert "Created" in out
|
||||
|
||||
with kb.connect() as conn:
|
||||
tasks = kb.list_tasks(conn)
|
||||
task = tasks[0]
|
||||
assert task.workspace_kind == "worktree"
|
||||
assert task.workspace_path == target_arg
|
||||
assert task.branch_name == "wt/t6-wire"
|
||||
|
||||
|
||||
def test_run_slash_rejects_branch_without_worktree(kanban_home):
|
||||
out = kc.run_slash("create 'bad branch' --workspace scratch --branch wt/bad")
|
||||
assert "--branch is only valid with --workspace worktree" in out
|
||||
|
||||
|
||||
def test_run_slash_create_with_parent_and_cascade(kanban_home):
|
||||
# Parent then child via --parent
|
||||
out1 = kc.run_slash("create 'parent' --assignee alice")
|
||||
|
|
|
|||
|
|
@ -81,6 +81,35 @@ def test_workspace_kind_validation(kanban_home):
|
|||
kb.create_task(conn, title="bad ws", workspace_kind="cloud")
|
||||
|
||||
|
||||
def test_create_task_persists_worktree_branch_name(kanban_home, tmp_path):
|
||||
target = tmp_path / ".worktrees" / "t6-wire"
|
||||
with kb.connect() as conn:
|
||||
tid = kb.create_task(
|
||||
conn,
|
||||
title="ship worktree",
|
||||
workspace_kind="worktree",
|
||||
workspace_path=str(target),
|
||||
branch_name=" wt/t6-wire ",
|
||||
)
|
||||
task = kb.get_task(conn, tid)
|
||||
events = kb.list_events(conn, tid)
|
||||
context = kb.build_worker_context(conn, tid)
|
||||
|
||||
assert task.branch_name == "wt/t6-wire"
|
||||
assert events[0].payload["branch_name"] == "wt/t6-wire"
|
||||
assert "Branch: wt/t6-wire" in context
|
||||
|
||||
|
||||
def test_branch_name_requires_worktree_workspace(kanban_home):
|
||||
with kb.connect() as conn, pytest.raises(ValueError, match="worktree"):
|
||||
kb.create_task(
|
||||
conn,
|
||||
title="bad branch",
|
||||
workspace_kind="scratch",
|
||||
branch_name="wt/bad",
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Links + dependency resolution
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
@ -1654,11 +1683,12 @@ class TestSharedBoardPaths:
|
|||
created_at=0,
|
||||
started_at=None,
|
||||
completed_at=None,
|
||||
workspace_kind="scratch",
|
||||
workspace_path=None,
|
||||
workspace_kind="worktree",
|
||||
workspace_path=str(tmp_path / "ws"),
|
||||
claim_lock=None,
|
||||
claim_expires=None,
|
||||
tenant=None,
|
||||
branch_name="wt/t_dispatch_env",
|
||||
)
|
||||
kb._default_spawn(task, str(tmp_path / "ws"))
|
||||
|
||||
|
|
@ -1668,6 +1698,7 @@ class TestSharedBoardPaths:
|
|||
default_home / "kanban" / "workspaces"
|
||||
)
|
||||
assert env["HERMES_KANBAN_TASK"] == "t_dispatch_env"
|
||||
assert env["HERMES_KANBAN_BRANCH"] == "wt/t_dispatch_env"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
@ -1907,6 +1938,7 @@ def test_migrate_add_optional_columns_tolerates_concurrent_migration(kanban_home
|
|||
tenant TEXT,
|
||||
result TEXT,
|
||||
idempotency_key TEXT,
|
||||
branch_name TEXT,
|
||||
consecutive_failures INTEGER NOT NULL DEFAULT 0,
|
||||
worker_pid INTEGER,
|
||||
last_failure_error TEXT,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue