mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-07 02:51:50 +00:00
fix(kanban): add per-path env overrides + dispatcher env injection
Layers defense-in-depth on top of the shared-root anchoring (base commit). Changes in hermes_cli/kanban_db.py: - kanban_db_path() now honours HERMES_KANBAN_DB first, then falls through to kanban_home()/kanban.db. - workspaces_root() now honours HERMES_KANBAN_WORKSPACES_ROOT first, then falls through to kanban_home()/kanban/workspaces. - All three overrides (HERMES_KANBAN_HOME, HERMES_KANBAN_DB, HERMES_KANBAN_WORKSPACES_ROOT) now call .expanduser() for consistency. - _default_spawn() injects HERMES_KANBAN_DB and HERMES_KANBAN_WORKSPACES_ROOT into the worker subprocess env. Even when the worker's get_default_hermes_root() resolution somehow disagrees with the dispatcher's (symlinks, unusual Docker layouts), the two processes still open the same SQLite file. Module docstring updated to describe all three overrides and the dispatcher env-injection contract. Tests (tests/hermes_cli/test_kanban_db.py, TestSharedBoardPaths): - test_hermes_kanban_db_pin_beats_kanban_home - test_hermes_kanban_workspaces_root_pin_beats_kanban_home - test_empty_per_path_overrides_fall_through - test_dispatcher_spawn_injects_kanban_db_and_workspaces_root (monkeypatches subprocess.Popen, asserts both env vars reach the child even after HERMES_HOME is rewritten by `hermes -p <profile>`.) Docs: website/docs/reference/environment-variables.md gets entries for the three kanban env vars. This fusion is built on the cleanest of the seven competing PRs that targeted issue #18442: * Base commit (from PR #19350 by @GodsBoy): add `kanban_home()` helper anchored at `get_default_hermes_root()`, reroute all 5 kanban path sites through it (including the 3 sibling log-dir sites that the other six PRs missed), 8-test regression class. * Dispatcher env-var injection approach drawn from PRs #18300 (@quocanh261997) and #19100 (@cg2aigc). * Per-path env overrides drawn from PR #19100 (@cg2aigc). * get_default_hermes_root() resolution direction first proposed in PR #18503 (@beibi9966) and PR #18985 (@Gosuj). Closes the duplicate/competing PRs: #18300, #18503, #18670, #18985, #19037, #19056, #19100. Fixes #18442 and #19348. Co-authored-by: quocanh261997 <17986614+quocanh261997@users.noreply.github.com> Co-authored-by: cg2aigc <232694053+cg2aigc@users.noreply.github.com> Co-authored-by: beibi9966 <beibei1988@proton.me> Co-authored-by: Gosuj <123411271+Gosuj@users.noreply.github.com> Co-authored-by: LeonSGP43 <154585401+LeonSGP43@users.noreply.github.com>
This commit is contained in:
parent
f5bd77b3e1
commit
2658494e81
3 changed files with 143 additions and 4 deletions
|
|
@ -607,3 +607,108 @@ class TestSharedBoardPaths:
|
|||
task = kb.get_task(conn, task_id)
|
||||
assert task is not None
|
||||
assert task.title == "cross-profile"
|
||||
|
||||
def test_hermes_kanban_db_pin_beats_kanban_home(
|
||||
self, tmp_path, monkeypatch
|
||||
):
|
||||
# HERMES_KANBAN_DB pins the file path directly and beats both
|
||||
# HERMES_KANBAN_HOME and the `get_default_hermes_root()` path.
|
||||
# This is the env the dispatcher injects into workers.
|
||||
default_home = tmp_path / ".hermes"
|
||||
default_home.mkdir()
|
||||
umbrella = tmp_path / "umbrella"
|
||||
umbrella.mkdir()
|
||||
pinned_db = tmp_path / "pinned" / "board.db"
|
||||
pinned_db.parent.mkdir()
|
||||
|
||||
monkeypatch.setattr(Path, "home", lambda: tmp_path)
|
||||
monkeypatch.setenv("HERMES_HOME", str(default_home))
|
||||
monkeypatch.setenv("HERMES_KANBAN_HOME", str(umbrella))
|
||||
monkeypatch.setenv("HERMES_KANBAN_DB", str(pinned_db))
|
||||
|
||||
assert kb.kanban_db_path() == pinned_db
|
||||
# workspaces_root still follows HERMES_KANBAN_HOME -- the pins
|
||||
# are independent.
|
||||
assert kb.workspaces_root() == umbrella / "kanban" / "workspaces"
|
||||
|
||||
def test_hermes_kanban_workspaces_root_pin_beats_kanban_home(
|
||||
self, tmp_path, monkeypatch
|
||||
):
|
||||
# HERMES_KANBAN_WORKSPACES_ROOT pins the workspaces root directly.
|
||||
default_home = tmp_path / ".hermes"
|
||||
default_home.mkdir()
|
||||
umbrella = tmp_path / "umbrella"
|
||||
umbrella.mkdir()
|
||||
pinned_ws = tmp_path / "pinned-workspaces"
|
||||
pinned_ws.mkdir()
|
||||
|
||||
monkeypatch.setattr(Path, "home", lambda: tmp_path)
|
||||
monkeypatch.setenv("HERMES_HOME", str(default_home))
|
||||
monkeypatch.setenv("HERMES_KANBAN_HOME", str(umbrella))
|
||||
monkeypatch.setenv("HERMES_KANBAN_WORKSPACES_ROOT", str(pinned_ws))
|
||||
|
||||
assert kb.workspaces_root() == pinned_ws
|
||||
# kanban_db_path still follows HERMES_KANBAN_HOME.
|
||||
assert kb.kanban_db_path() == umbrella / "kanban.db"
|
||||
|
||||
def test_empty_per_path_overrides_fall_through(
|
||||
self, tmp_path, monkeypatch
|
||||
):
|
||||
# Empty/whitespace pins are treated as unset, same as
|
||||
# HERMES_KANBAN_HOME.
|
||||
default_home = tmp_path / ".hermes"
|
||||
default_home.mkdir()
|
||||
monkeypatch.setattr(Path, "home", lambda: tmp_path)
|
||||
monkeypatch.setenv("HERMES_HOME", str(default_home))
|
||||
monkeypatch.setenv("HERMES_KANBAN_DB", " ")
|
||||
monkeypatch.setenv("HERMES_KANBAN_WORKSPACES_ROOT", "")
|
||||
|
||||
assert kb.kanban_db_path() == default_home / "kanban.db"
|
||||
assert kb.workspaces_root() == default_home / "kanban" / "workspaces"
|
||||
|
||||
def test_dispatcher_spawn_injects_kanban_db_and_workspaces_root(
|
||||
self, tmp_path, monkeypatch
|
||||
):
|
||||
# The dispatcher's `_default_spawn` must inject HERMES_KANBAN_DB
|
||||
# and HERMES_KANBAN_WORKSPACES_ROOT into the worker env so the
|
||||
# worker converges on the dispatcher's paths even when the
|
||||
# `-p <profile>` flag rewrites HERMES_HOME.
|
||||
default_home = tmp_path / ".hermes"
|
||||
default_home.mkdir()
|
||||
self._set_home(monkeypatch, tmp_path, default_home)
|
||||
|
||||
captured = {}
|
||||
|
||||
class _FakePopen:
|
||||
def __init__(self, cmd, **kwargs):
|
||||
captured["cmd"] = cmd
|
||||
captured["env"] = kwargs.get("env", {})
|
||||
self.pid = 4242
|
||||
|
||||
monkeypatch.setattr("subprocess.Popen", _FakePopen)
|
||||
|
||||
task = kb.Task(
|
||||
id="t_dispatch_env",
|
||||
title="x",
|
||||
body=None,
|
||||
assignee="coder",
|
||||
status="ready",
|
||||
priority=0,
|
||||
created_by=None,
|
||||
created_at=0,
|
||||
started_at=None,
|
||||
completed_at=None,
|
||||
workspace_kind="scratch",
|
||||
workspace_path=None,
|
||||
claim_lock=None,
|
||||
claim_expires=None,
|
||||
tenant=None,
|
||||
)
|
||||
kb._default_spawn(task, str(tmp_path / "ws"))
|
||||
|
||||
env = captured["env"]
|
||||
assert env["HERMES_KANBAN_DB"] == str(default_home / "kanban.db")
|
||||
assert env["HERMES_KANBAN_WORKSPACES_ROOT"] == str(
|
||||
default_home / "kanban" / "workspaces"
|
||||
)
|
||||
assert env["HERMES_KANBAN_TASK"] == "t_dispatch_env"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue