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:
@@ -9,8 +9,20 @@ board as the dispatcher that claimed the task. The same applies to
|
||||
|
||||
In standard installs ``<root>`` is ``~/.hermes``. In Docker / custom
|
||||
deployments where ``HERMES_HOME`` points outside ``~/.hermes`` (e.g.
|
||||
``/opt/hermes``), ``<root>`` is ``HERMES_HOME``. Set ``HERMES_KANBAN_HOME``
|
||||
to override the resolution explicitly (tests, unusual deployments).
|
||||
``/opt/hermes``), ``<root>`` is ``HERMES_HOME``. Three env-var overrides
|
||||
are available (highest precedence first, all optional):
|
||||
|
||||
* ``HERMES_KANBAN_DB`` — pin the database file path directly.
|
||||
* ``HERMES_KANBAN_WORKSPACES_ROOT`` — pin the workspaces root directly.
|
||||
* ``HERMES_KANBAN_HOME`` — pin the umbrella root that anchors all three
|
||||
kanban paths (db + workspaces + logs). Useful for tests and unusual
|
||||
deployments where a single override is enough.
|
||||
|
||||
The dispatcher injects ``HERMES_KANBAN_DB`` and
|
||||
``HERMES_KANBAN_WORKSPACES_ROOT`` into the worker subprocess env as a
|
||||
defense-in-depth measure: even if the worker's ``get_default_hermes_root()``
|
||||
resolution somehow disagrees with the dispatcher's (unusual symlink or
|
||||
Docker layout), the two processes still converge on the same files.
|
||||
|
||||
Schema is intentionally small: tasks, task_links, task_comments,
|
||||
task_events. The ``workspace_kind`` field decouples coordination from git
|
||||
@@ -87,7 +99,7 @@ def kanban_home() -> Path:
|
||||
"""
|
||||
override = os.environ.get("HERMES_KANBAN_HOME", "").strip()
|
||||
if override:
|
||||
return Path(override)
|
||||
return Path(override).expanduser()
|
||||
from hermes_constants import get_default_hermes_root
|
||||
return get_default_hermes_root()
|
||||
|
||||
@@ -97,8 +109,13 @@ def kanban_db_path() -> Path:
|
||||
|
||||
Anchored at :func:`kanban_home`, not the active profile's
|
||||
``HERMES_HOME``, so profile workers and the dispatcher converge on
|
||||
the same board.
|
||||
the same board. ``HERMES_KANBAN_DB`` pins the path directly (highest
|
||||
precedence) — the dispatcher injects this into worker subprocess env
|
||||
as defense-in-depth.
|
||||
"""
|
||||
override = os.environ.get("HERMES_KANBAN_DB", "").strip()
|
||||
if override:
|
||||
return Path(override).expanduser()
|
||||
return kanban_home() / "kanban.db"
|
||||
|
||||
|
||||
@@ -107,7 +124,13 @@ def workspaces_root() -> Path:
|
||||
|
||||
Anchored at :func:`kanban_home` so workspace paths are stable across
|
||||
profile workers spawned by the dispatcher.
|
||||
``HERMES_KANBAN_WORKSPACES_ROOT`` pins the path directly (highest
|
||||
precedence) — the dispatcher injects this into worker subprocess env
|
||||
as defense-in-depth.
|
||||
"""
|
||||
override = os.environ.get("HERMES_KANBAN_WORKSPACES_ROOT", "").strip()
|
||||
if override:
|
||||
return Path(override).expanduser()
|
||||
return kanban_home() / "kanban" / "workspaces"
|
||||
|
||||
|
||||
@@ -2111,6 +2134,14 @@ def _default_spawn(task: Task, workspace: str) -> Optional[int]:
|
||||
env["HERMES_TENANT"] = task.tenant
|
||||
env["HERMES_KANBAN_TASK"] = task.id
|
||||
env["HERMES_KANBAN_WORKSPACE"] = workspace
|
||||
# Pin the shared board + workspaces root the dispatcher resolved, so
|
||||
# that even when the worker activates a profile (`hermes -p <name>`
|
||||
# rewrites HERMES_HOME), its kanban paths still match the
|
||||
# dispatcher's. Belt-and-braces with the `get_default_hermes_root()`
|
||||
# resolution in `kanban_home()` — symmetric resolution is the norm,
|
||||
# but unusual symlink / Docker layouts are caught here too.
|
||||
env["HERMES_KANBAN_DB"] = str(kanban_db_path())
|
||||
env["HERMES_KANBAN_WORKSPACES_ROOT"] = str(workspaces_root())
|
||||
# HERMES_PROFILE is the author the kanban_comment tool defaults to.
|
||||
# `hermes -p <assignee>` activates the profile, but the env var is
|
||||
# what the tool reads — set it explicitly here so comments are
|
||||
|
||||
Reference in New Issue
Block a user