fix(kanban): share board, workspaces, and worker logs across profiles#19378
Merged
Conversation
…Hermes root The Kanban board is documented as shared across all Hermes profiles, but `kanban_db_path()` and `workspaces_root()` resolved through `get_hermes_home()`, which returns the active profile's HERMES_HOME. When the dispatcher spawned a worker with `hermes -p <profile> --skills kanban-worker chat -q "work kanban task <id>"`, the worker rewrote HERMES_HOME to the profile subdirectory before kanban_db.py imported, opening a profile-local `kanban.db` that did not contain the dispatcher's task. `kanban_show` and `kanban_complete` failed; the dispatcher's row stayed `running` and was retried/crashed. The same defect applied to `_default_spawn`'s log directory and `worker_log_path`, so `hermes kanban tail` did not see the worker's output. Add `kanban_home()` in `hermes_cli/kanban_db.py` that resolves through `HERMES_KANBAN_HOME` (explicit override) then `get_default_hermes_root()`, which already understands the `<root>/profiles/<name>` and Docker / custom HERMES_HOME shapes. Reroute `kanban_db_path`, `workspaces_root`, the `_default_spawn` log directory, `gc_worker_logs`, and `worker_log_path` through it. Profile-specific config, `.env`, memory, and sessions stay isolated as before; only the kanban surface is shared. Add a `TestSharedBoardPaths` regression class to `tests/hermes_cli/test_kanban_db.py` covering: default install, profile-worker convergence, Docker custom HERMES_HOME, Docker profile layout, explicit `HERMES_KANBAN_HOME` override, and a real SQLite round-trip across dispatcher and worker HERMES_HOME perspectives. The dispatcher/worker convergence tests fail on origin/main and pass after the fix. Update the `kanban.md` user-guide page and the misleading docstrings in `kanban_db.py` to describe the shared-root behavior. Fixes #19348
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>
Collaborator
Collaborator
This was referenced May 3, 2026
1 task
7 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Profile workers (
hermes -p <name>) now share the same kanban board, workspaces, and worker-log directory as the dispatcher that spawned them — fixes #18442.Root cause:
kanban_db_path(),workspaces_root(), and the three worker-log-dir sites inhermes_cli/kanban_db.pyall resolved paths throughget_hermes_home(), which returns the active profile's HERMES_HOME. When the dispatcher spawned a worker withhermes -p <profile> chat -q "work kanban task <id>", the worker's_apply_profile_override()rewrote HERMES_HOME to~/.hermes/profiles/<profile>and opened a profile-localkanban.dbthat didn't contain the dispatcher's task.Changes
hermes_cli/kanban_db.py:kanban_home()helper resolves throughHERMES_KANBAN_HOME→get_default_hermes_root().get_default_hermes_root()already handles both<root>/profiles/<name>layouts and Docker / custom HERMES_HOME paths correctly.kanban_db_path()honoursHERMES_KANBAN_DBfirst, thenkanban_home()/kanban.db.workspaces_root()honoursHERMES_KANBAN_WORKSPACES_ROOTfirst, thenkanban_home()/kanban/workspaces._default_spawnlog dir,gc_worker_logs, andworker_log_path()all route throughkanban_home()— these three sibling sites were the log-dir tail end of the same bug._default_spawn()now injectsHERMES_KANBAN_DBandHERMES_KANBAN_WORKSPACES_ROOTinto the worker subprocess env. Defense-in-depth: even when the worker'sget_default_hermes_root()resolution disagrees with the dispatcher's (unusual symlink / Docker layouts), the two processes still open the same SQLite file..expanduser()for consistency.hermes_cli/kanban.py: help string forlogsubcommand updated ($HERMES_HOME/kanban/logs/→<kanban-root>/kanban/logs/).tests/hermes_cli/test_kanban_db.py: 12 regression tests underTestSharedBoardPathscovering default install, profile-worker convergence, Docker custom root, Docker profile layout,HERMES_KANBAN_HOMEoverride, per-pathHERMES_KANBAN_DBandHERMES_KANBAN_WORKSPACES_ROOTpins, empty/whitespace overrides falling through, real SQLite cross-profile handoff, and dispatcher env-injection on spawn.website/docs/reference/environment-variables.md: documents the three new kanban env vars.Validation
hermes -p workerworker opens~/.hermes/profiles/worker/kanban.db~/.hermes/kanban.dbhermes kanban tail <id>from profile/opt/hermes/profiles/xworker/opt/hermes/profiles/x/kanban.db/opt/hermes/kanban.db221 passed(test_kanban_db+test_kanban_cli+test_kanban_core_functionality+test_kanban_tools)HERMES_KANBAN_DBpin wins, cross-profile SQLite handoff, dispatcher env injectionConsolidation
This PR fuses the best aspects of the seven parallel PRs that targeted #18442:
kanban_home()helper anchored atget_default_hermes_root(), reroutes all 5 kanban path sites through it (the 3 sibling log-dir sites were missed by every other PR), 8-test regression class including a real-sqlite dispatcher/worker convergence test.HERMES_KANBAN_DBandHERMES_KANBAN_WORKSPACES_ROOTpins with highest precedence.get_default_hermes_root()direction first proposed by @beibi9966 (fix(kanban): anchor shared kanban.db at default Hermes root #18503) and @Gosuj (fix(kanban): anchor board to default Hermes root, not active profile #18985).Closes #18300, #18503, #18670, #18985, #19037, #19056, #19100. Fixes #18442, closes #19348.
Test plan
scripts/run_tests.sh tests/hermes_cli/test_kanban_db.py tests/hermes_cli/test_kanban_cli.py tests/hermes_cli/test_kanban_core_functionality.py tests/tools/test_kanban_tools.py # 221 passedManual repro (without fix):
hermes kanban create "smoke" --assignee worker1hermes -p worker1 ..., worker opens~/.hermes/profiles/worker1/kanban.db,kanban_showfails, dispatcher retries forever.After fix:
~/.hermes/kanban.db; worker completes the task.HERMES_KANBAN_DBinjected into the worker env guarantees this even if resolution inside the worker somehow disagreed.