Skip to content

fix(kanban): prevent nested hermes chat from inheriting parent Kanban ownership (#70809) - #70898

Open
webtecnica wants to merge 2 commits into
NousResearch:mainfrom
webtecnica:fix/kanban-nested-chat-inheritance
Open

fix(kanban): prevent nested hermes chat from inheriting parent Kanban ownership (#70809)#70898
webtecnica wants to merge 2 commits into
NousResearch:mainfrom
webtecnica:fix/kanban-nested-chat-inheritance

Conversation

@webtecnica

Copy link
Copy Markdown
Contributor

Problem

A dispatcher-owned Kanban worker can run a nested hermes chat -q ... via terminal. That nested CLI inherits HERMES_KANBAN_TASK env vars and is treated as the parent worker itself — can load the parent card, emit heartbeats, and finalize the parent run.

Root cause

Several lifecycle paths use os.environ.get('HERMES_KANBAN_TASK') as proof of ownership. Environment presence cannot distinguish the dispatcher-owned entry process from a nested CLI subprocess.

Fix

  1. agent/delegation_context.py — Added _KANBAN_WORKER_OWNER ContextVar with set_kanban_worker_owner() / is_kanban_worker_owner(). Also added scrub_kanban_env() and delegated_child_subprocess_env() for env propagation safety. (Previously committed.)

  2. cli.py — Three raw env reads converted to ContextVar-gated checks:

    • Signal handler (SIGTERM → os._exit(0) path)
    • Exit code logic (rate-limit exit code for kanban workers)
    • _run_kanban_goal_loop_q (goal mode worker loop)
  3. agent/conversation_loop.py — Logger no longer reads HERMES_KANBAN_TASK from env.

  4. Consumer code with ContextVar-first patterns (previously committed):

    • agent/kanban_stop.py, agent/turn_finalizer.py, run_agent.py, agent/skill_utils.py, model_tools.py, tools/send_message_tool.py, tools/kanban_tools.py, hermes_cli/kanban.py, hermes_cli/doctor.py
  5. Tests — 4 new tests for ContextVar-based ownership verification.

Closes #70809

… ownership (NousResearch#70809)

Derive Kanban worker ownership once at the CLI boundary by matching the
dispatcher query marker ("work kanban task <id>") to HERMES_KANBAN_TASK
and store the result in a ContextVar. Lifecycle code (heartbeat, goal
loop, tool gates, signal handler, stop-nudge, turn-finalizer) now reads
the verified ContextVar instead of re-reading os.environ, so a nested
hermes chat subprocess that inherited HERMES_KANBAN_* env vars is never
treated as the parent worker.

The fix spans 12 files and adds is_kanban_worker_owner() /
set_kanban_worker_owner() to agent/delegation_context.py, then replaces
os.environ.get("HERMES_KANBAN_TASK") checks in:
- cli.py (CLI boundary, signal handler, goal loop, exit code)
- run_agent.py (_touch_activity heartbeat)
- agent/turn_finalizer.py (iteration budget timeout)
- agent/kanban_stop.py (stop-nudge guard)
- model_tools.py (tool cache key, kanban toolset auto-inject)
- tools/kanban_tools.py (tool gates, task-id resolution, ownership
  enforcement, heartbeat)
- tools/send_message_tool.py (send_message gate)
- agent/skill_utils.py (skill visibility filter)
- hermes_cli/doctor.py (runtime-gated diagnostics)
- hermes_cli/kanban.py (_worker_run_id_for)

Each replacement falls back to the env var on import failure.
… ownership (NousResearch#70809)

Add missing cli.py ContextVar gates for signal handler, goal loop, and
exit-code logic. Fix conversation_loop.py logger to not read env var.
Add tests for ContextVar-based ownership verification.
@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard comp/cron Cron scheduler and job management comp/tools Tool registry, model_tools, toolsets labels Jul 24, 2026
@vadelma-agent

Copy link
Copy Markdown

Read-only review of current head 2be9d739e515e33a91ad0f84ff0c187083355a48: the ownership ContextVar tests pass, and the focused overlap tests pass (20 isolation tests; 67 isolation + Kanban CLI tests).

One remaining coverage gap: the new tests simulate the ownership gate, but do not exercise a real nested hermes chat -q ... subprocess with inherited HERMES_KANBAN_* variables through the full CLI path. A bounded subprocess regression could assert that a non-matching nested query does not enter the goal loop, heartbeat, task lookup, or finalization path, while the matching dispatcher marker still does.

This would make the #70809 process-boundary contract explicit without requiring a production database or live dispatcher.

@teknium1

Copy link
Copy Markdown
Contributor

Thanks for tracing the ownership problem to inherited process environment and introducing a process-local ownership signal. The current main premise is real: cli.py:17730-17738 reads HERMES_KANBAN_TASK and loads that task before any ownership verification, while the dispatcher sets the marker query at hermes_cli/kanban_db.py:8817.

Problems

  • The conversion is incomplete. gateway/session_context.py:382 still classifies any inherited HERMES_KANBAN_TASK process as a one-shot worker. That makes a nested normal CLI disable background terminal delivery (tools/terminal_tool.py:2692-2713) and can force background delegation inline (tools/delegate_tool.py:3167-3205).
  • tools/kanban_tools.py:360 remains an environment-only runtime guard. After the PR makes a non-owner Kanban-enabled profile eligible at the schema gate, this guard still rejects kanban_list and kanban_unblock solely because the nested process inherited the variable.
  • The new tests simulate the proposed marker condition rather than drive a real nested hermes chat -q process. This matches the coverage gap already noted in the PR discussion.

Suggested changes

  • Audit remaining environment-only ownership checks and use the verified-owner predicate where ownership, rather than board routing/configuration, is being decided.
  • Add a bounded real-CLI inherited-environment regression with matching and non-matching query controls.

Automated hermes-sweeper review.

@teknium1 teknium1 added sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 30, 2026
@GottZ

GottZ commented Aug 3, 2026

Copy link
Copy Markdown

This was generated by AI during triage.

Summary

One open PR, #70898, addresses #70809 by deriving Kanban worker ownership from an exact dispatcher-query/task match and propagating that process-local identity across lifecycle consumers. The diff blocks several nested-CLI ownership paths, but environment-only classification and runtime gating remain outside the conversion.

Related pull requests

Suggested consolidation

Keep #70898 open with a salvage path, consistent with the maintainer-bot keep_open review: retain the CLI-boundary ownership ContextVar, convert the remaining ownership-sensitive environment checks, remove fail-open environment fallbacks from worker-only lifecycle paths, and add a bounded inherited-environment full-CLI regression with matching and non-matching queries. It is the sole and best available implementation for #70809, so there are no duplicate PRs to close.

Complex graph

flowchart LR
    classDef open fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
    classDef merged fill:#dcfce7,stroke:#15803d,color:#14532d
    classDef closed fill:#e5e7eb,stroke:#6b7280,color:#1f2937
    classDef unverified fill:#f3f4f6,stroke:#9ca3af,color:#374151
    classDef best stroke-width:3px,stroke:#b45309
    classDef target stroke-width:3px,stroke:#4338ca
    I70809(["issue #70809 (open)"])
    P70898["PR #70898 (open)"]
    P70898 -->|best fix| I70809
    class I70809 open
    class P70898 open
    class P70898 best
    class P70898 target
    click I70809 "https://github.com/NousResearch/hermes-agent/issues/70809"
    click P70898 "https://github.com/NousResearch/hermes-agent/pull/70898"
Loading

Graph: solid arrow = fixes / best fix, dashed arrow = partial or unverified (see edge label); boxed group = PRs duplicating each other; amber border = best fix; indigo border = target; gray node = closed (state tag in the node label).

Cross-PR triage: Reviewed 1 pull request and 1 issue in this complex. Each diff was read against this issue; Assessment working set: 31 kB of PR diffs, 4 kB of issue/PR text, <1 kB of discussion (1 comments), 3 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch.

@DavidMetcalfe

Copy link
Copy Markdown
Contributor

Steering note: narrower alternative fix proposed in #81843

Thanks for tracing the nested-CLI ownership leak to inherited HERMES_KANBAN_* env and building the ContextVar ownership model in this PR — the direction is right, and the sweeper's keep_open review captured the remaining gaps (env-only checks in gateway/session_context.py and tools/kanban_tools.py, plus the missing real-CLI regression).

While this PR has been waiting on those requested changes, a narrower fix for the same root cause was proposed in #81843: it strips HERMES_KANBAN_* unconditionally at the terminal-tool subprocess boundary (_make_run_env, _sanitize_subprocess_env, _scrub_child_env), so a nested hermes CLI launched from a worker never inherits the dispatcher identity in the first place — no new ContextVar lifecycle, ~200 LOC including tests, and it does not touch kanban_db.py (which the #79543/#81324 four-field CAS plan declares ownership of).

If maintainers prefer this PR's ContextVar approach as the deeper fix, #81843 composes with it rather than conflicting — it just narrows what a nested process can see. Otherwise, closing this PR in favor of #81843 is a reasonable option. Please let us know which direction you'd like to take, or if you have questions about how the two changes interact.

@alt-glitch alt-glitch removed the sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages label Aug 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard comp/cron Cron scheduler and job management comp/tools Tool registry, model_tools, toolsets P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform 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.

[Bug]: nested hermes chat subprocess inherits parent Kanban worker lifecycle ownership

6 participants