hermes - 💡(How to fix) Fix `_default_spawn` injects `HERMES_HOME` but not `TERMINAL_CWD` — kanban workers load the dispatching gateway's `AGENTS.md` under multi-profile dispatch

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

hermes_cli/kanban_db.py::_default_spawn() builds the worker child's environment and correctly injects HERMES_HOME so the worker reads its profile-scoped config.yaml. It does not inject a matching TERMINAL_CWD. The child therefore inherits TERMINAL_CWD from the dispatching gateway's own environment (env = dict(os.environ)). build_context_files_prompt(cwd=TERMINAL_CWD) then locates AGENTS.md relative to that inherited cwd — the dispatching gateway's directory, not the task's profile directory.

In a multi-gateway / multi-profile install, any gateway with kanban.dispatch_in_gateway: true can claim any task assigned to any valid profile. Whichever gateway wins the claim race determines which AGENTS.md the spawned worker sees, so context-file loading is non-deterministic across the install.

Error Message

from hermes_cli.profiles import resolve_profile_env try: env["HERMES_HOME"] = resolve_profile_env(profile_arg) env["TERMINAL_CWD"] = env["HERMES_HOME"] # pin worker cwd to its own profile dir except FileNotFoundError: pass

Root Cause

Observed in our install as a worker implementing code despite its profile's AGENTS.md carrying a scope-escalation rule that should have made it block — because the wrong AGENTS.md was loaded.

Fix Action

Fix / Workaround

hermes_cli/kanban_db.py::_default_spawn() builds the worker child's environment and correctly injects HERMES_HOME so the worker reads its profile-scoped config.yaml. It does not inject a matching TERMINAL_CWD. The child therefore inherits TERMINAL_CWD from the dispatching gateway's own environment (env = dict(os.environ)). build_context_files_prompt(cwd=TERMINAL_CWD) then locates AGENTS.md relative to that inherited cwd — the dispatching gateway's directory, not the task's profile directory.

In a multi-gateway / multi-profile install, any gateway with kanban.dispatch_in_gateway: true can claim any task assigned to any valid profile. Whichever gateway wins the claim race determines which AGENTS.md the spawned worker sees, so context-file loading is non-deterministic across the install.

  1. Multi-profile install with ≥2 gateways that both have kanban.dispatch_in_gateway: true (e.g. root/default + a worker profile).
  2. Each profile has its own AGENTS.md with profile-specific rules.
  3. Create a task assigned to profile A; a different gateway (B, or root) wins the claim race.
  4. The spawned worker for profile A loads AGENTS.md relative to gateway B's cwd (often $HOME, picking up ~/AGENTS.md or none) instead of profile A's AGENTS.md. Profile-specific rules silently don't apply.

Code Example

from hermes_cli.profiles import resolve_profile_env
try:
    env["HERMES_HOME"] = resolve_profile_env(profile_arg)
    env["TERMINAL_CWD"] = env["HERMES_HOME"]   # pin worker cwd to its own profile dir
except FileNotFoundError:
    pass
RAW_BUFFERClick to expand / collapse

Summary

hermes_cli/kanban_db.py::_default_spawn() builds the worker child's environment and correctly injects HERMES_HOME so the worker reads its profile-scoped config.yaml. It does not inject a matching TERMINAL_CWD. The child therefore inherits TERMINAL_CWD from the dispatching gateway's own environment (env = dict(os.environ)). build_context_files_prompt(cwd=TERMINAL_CWD) then locates AGENTS.md relative to that inherited cwd — the dispatching gateway's directory, not the task's profile directory.

In a multi-gateway / multi-profile install, any gateway with kanban.dispatch_in_gateway: true can claim any task assigned to any valid profile. Whichever gateway wins the claim race determines which AGENTS.md the spawned worker sees, so context-file loading is non-deterministic across the install.

Why injecting HERMES_HOME isn't enough

The worker activates its profile via hermes -p <assignee>, which rewrites HERMES_HOME for config resolution. But TERMINAL_CWD is what build_context_files_prompt uses to find AGENTS.md and other context files. _default_spawn already pins HERMES_HOME to the task's profile dir; the natural peer — pinning TERMINAL_CWD to that same dir — is missing.

Reproduction

  1. Multi-profile install with ≥2 gateways that both have kanban.dispatch_in_gateway: true (e.g. root/default + a worker profile).
  2. Each profile has its own AGENTS.md with profile-specific rules.
  3. Create a task assigned to profile A; a different gateway (B, or root) wins the claim race.
  4. The spawned worker for profile A loads AGENTS.md relative to gateway B's cwd (often $HOME, picking up ~/AGENTS.md or none) instead of profile A's AGENTS.md. Profile-specific rules silently don't apply.

Observed in our install as a worker implementing code despite its profile's AGENTS.md carrying a scope-escalation rule that should have made it block — because the wrong AGENTS.md was loaded.

Affected code (HEAD 69dfcdcc1)

hermes_cli/kanban_db.py::_default_spawn(). The env dict receives HERMES_HOME, HERMES_KANBAN_*, HERMES_PROFILE, terminal timeouts and board pins — but no TERMINAL_CWD. grep -n TERMINAL_CWD hermes_cli/kanban_db.py returns nothing.

Proposed fix

Set TERMINAL_CWD symmetrically with HERMES_HOME, right after the HERMES_HOME injection:

from hermes_cli.profiles import resolve_profile_env
try:
    env["HERMES_HOME"] = resolve_profile_env(profile_arg)
    env["TERMINAL_CWD"] = env["HERMES_HOME"]   # pin worker cwd to its own profile dir
except FileNotFoundError:
    pass

This makes TERMINAL_CWD track the task's profile dir regardless of which gateway dispatches, so build_context_files_prompt deterministically loads the profile's AGENTS.md. One line, mirrors the existing HERMES_HOME pattern.

Our workaround (no source patch — we do NOT modify Hermes source)

We validated the one-line patch above locally, then reverted it (every hermes update wipes a source patch). Our standing workaround uses Hermes-native config only:

  • kanban.dispatch_in_gateway: false in the root config (one dispatcher per board is Hermes's own intended topology per the kanban_db.py comments); dispatch is enabled only on the profile gateway that should claim its own tasks.
  • explicit absolute terminal.cwd: /abs/path/to/profile in each worker profile's config.
  • hermes gateway uninstall for any stale/orphan gateway that can still claim tasks.

This works but is per-install bookkeeping that every multi-profile user has to rediscover. The one-line upstream fix would make multi-profile dispatch correct by default.

Environment

  • Hermes at commit 69dfcdcc1, macOS, Python 3.11
  • Multi-profile install (researcher / qa / builder / default) with per-profile gateways

Filed by: Barrett Slagle (The Factory)

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING