Skip to content

fix(kanban): drop approval context env vars from spawned workers - #63189

Open
liuhao1024 wants to merge 1 commit into
NousResearch:mainfrom
liuhao1024:liuhao/cron-bugfix-63183
Open

fix(kanban): drop approval context env vars from spawned workers#63189
liuhao1024 wants to merge 1 commit into
NousResearch:mainfrom
liuhao1024:liuhao/cron-bugfix-63183

Conversation

@liuhao1024

Copy link
Copy Markdown
Contributor

What does this PR do?

Kanban dispatcher spawns detached worker subprocesses via subprocess.Popen(env=dict(os.environ)), which copies the parent gateway's full environment. This includes approval-related environment variables (HERMES_EXEC_ASK, HERMES_CRON_SESSION, HERMES_SESSION_*, HERMES_INTERACTIVE, HERMES_YOLO_MODE). However, gateway approval notification callbacks and queues are process-local Python dictionaries that cannot be inherited by subprocesses. The detached worker therefore identifies itself as gateway/approval-capable and attempts to send approval requests that cannot be delivered, causing silent failures or misrouted notifications.

This fix drops all approval-related environment variables from the worker's environment before spawning, matching the existing pattern for HERMES_TUI. Workers run in non-approval mode by design—they should never require gateway approval.

Related Issue

Fixes #63183

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)

Changes Made

  • hermes_cli/kanban_db.py: In _default_spawn, add code after env.pop("HERMES_TUI", None) to drop approval context environment variables (HERMES_EXEC_ASK, HERMES_CRON_SESSION, HERMES_INTERACTIVE, HERMES_YOLO_MODE, and all HERMES_SESSION_* prefixed keys).

How to Test

  1. Start a gateway with approval enabled (hermes gateway run --replace).
  2. Create a Kanban task assigned to a profile.
  3. Observe the worker subprocess environment via ps eww -p <pid> (on macOS/Linux) or Process Explorer (on Windows).
  4. Verify that HERMES_EXEC_ASK, HERMES_CRON_SESSION, HERMES_INTERACTIVE, HERMES_YOLO_MODE, and all HERMES_SESSION_* variables are NOT present in the worker's environment.
  5. The HERMES_KANBAN_TASK variable should still be present (kanban context is intentional).

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: macOS 26.4.1

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

@alt-glitch alt-glitch added type/bug Something isn't working comp/cron Cron scheduler and job management P3 Low — cosmetic, nice to have sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data labels Jul 12, 2026

fxfitz commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Thanks for jumping on this so quickly — the diagnosis around process-local approval callbacks is right, and scrubbing ambient gateway/cron/session/YOLO state is definitely an important part of the fix.

I walked the patch through the current approval flow, though, and I think it needs one more piece before it is safe to merge as a fix for #63183.

The main concern is that removing these markers puts the worker into the existing non-interactive, non-gateway, non-ask, non-cron branch. In check_all_command_guards(), that branch returns approved=True before the recoverable dangerous-command/Tirith checks run. check_execute_code_guard() similarly approves local arbitrary code when there is no gateway/ask context. So this can trade the current unanswerable pending_approval for silent approval of the same non-hardline action. Hardline blocks and configured deny rules still apply, but the recoverable consent layer is bypassed.

That is the inverse gap already tracked in #55945, with related proposed work in #55946. It is also why the issue description calls out that environment scrubbing should not ship by itself.

A couple of related details:

My suggestion would be to keep the scrub, but pair it with an explicit Kanban/card-owner execution context and policy. If an action requires consent and no durable route exists, the interim behavior should fail closed rather than fall through to generic headless auto-approval. If ask is supported, the request/grant needs shared durable state or IPC, exact task/run/profile/action binding, expiry, and one-time consumption.

If the intended scope here is only the environment-cleanup portion, it may be better to present it as a partial mitigation and avoid Fixes #63183 until the worker policy/approval lifecycle is covered. This is a useful start; I just don't think it is safe as a standalone change yet. Happy to share the controlled reproductions from the issue if that would help with tests.

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for isolating the ambient-environment inheritance path; current main does copy the parent environment into the detached worker (hermes_cli/kanban_db.py:8085, passed at :8229), while the gateway sets HERMES_EXEC_ASK=1 at gateway/run.py:1739.

Problems

  • The scrub is incomplete: the changed code removes HERMES_SESSION_* but not HERMES_GATEWAY_SESSION (hermes_cli/kanban_db.py:7808-7810). tools/approval.py:241-245 reads that direct marker, so an inherited legacy marker still places the worker in gateway approval handling.
  • More importantly, removing ask/gateway context alone changes a recoverable consent path into headless approval. check_all_command_guards() returns approved=True before normal dangerous-command/Tirith processing in that branch (tools/approval.py:2696-2762); check_execute_code_guard() has the same headless approval path (tools/approval.py:3140-3146).
  • The one-file diff adds no regression test, although _default_spawn env capture already has a test seam at tests/hermes_cli/test_kanban_core_functionality.py:2785-2815.

Suggested changes

  • Pair any scrub with explicit task-scoped Kanban approval policy that fails closed when no durable approval route exists, and test the resulting terminal and execute_code decisions.

Automated hermes-sweeper review.

Comment thread hermes_cli/kanban_db.py
# approval-capable and attempt to send approval requests that cannot be
# delivered, causing silent failures or misrouted notifications.
# See issue #63183.
env.pop("HERMES_EXEC_ASK", None)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing the ask marker together with the remaining context markers sends non-hardline worker actions through the documented headless auto-approve path: check_all_command_guards() returns approved before normal dangerous-command/Tirith handling (tools/approval.py:2696-2762), and check_execute_code_guard() returns approved without gateway/ask context (:3140-3146). This needs an explicit Kanban fail-closed policy or durable approval route, not scrub-only behavior.

Comment thread hermes_cli/kanban_db.py
env.pop("HERMES_CRON_SESSION", None)
env.pop("HERMES_INTERACTIVE", None)
env.pop("HERMES_YOLO_MODE", None)
for key in list(env.keys()):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This prefix cleanup does not cover HERMES_GATEWAY_SESSION, which _is_gateway_approval_context() reads directly (tools/approval.py:241-245). A worker inheriting that legacy marker remains in gateway approval context and can still produce child-local pending approval.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cron Cron scheduler and job management P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Security] Kanban workers inherit gateway/cron approval context but not the approval callback

4 participants