Skip to content

Commit ce73504

Browse files
author
IronRod Ops
committed
fix(kanban): scope worker guidance to dispatched tasks
1 parent b20d9f3 commit ce73504

2 files changed

Lines changed: 57 additions & 6 deletions

File tree

agent/agent_init.py

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,30 @@ def _moa_reference_output_allowed(agent: Any) -> bool:
112112
)
113113

114114

115+
def _resolve_kanban_worker_guidance(valid_tool_names: set[str]) -> str:
116+
"""Return task lifecycle guidance only for a real dispatcher worker.
117+
118+
Orchestrator profiles may expose ``kanban_show`` in ordinary chat so they
119+
can route board work. Tool availability alone therefore does not mean the
120+
current process owns a task. Injecting worker guidance into those chats
121+
falsely tells the model to call ``kanban_show()`` first, even when no
122+
``HERMES_KANBAN_TASK`` exists and the user explicitly requested direct
123+
execution.
124+
125+
Requiring both the dispatcher task marker and the worker tool keeps the
126+
prompt off ordinary orchestrator sessions and delegated children while
127+
preserving the full lifecycle contract for real workers.
128+
"""
129+
if not os.environ.get("HERMES_KANBAN_TASK"):
130+
return ""
131+
if "kanban_show" not in valid_tool_names:
132+
return ""
133+
134+
from agent.prompt_builder import KANBAN_GUIDANCE
135+
136+
return KANBAN_GUIDANCE
137+
138+
115139
def _relay_moa_reference_event(agent: Any, event: str, **kwargs: Any) -> None:
116140
"""Relay MoA display events while preserving the ``-Q`` stdout contract."""
117141
if not _moa_reference_output_allowed(agent):
@@ -1643,15 +1667,14 @@ def init_agent(
16431667
elif not agent.quiet_mode:
16441668
print("🛠️ No tools loaded (all tools filtered out or unavailable)")
16451669

1646-
# Kanban worker/orchestrator lifecycle guidance is session-static:
1647-
# the dispatcher decides at spawn time whether this process is a kanban
1648-
# worker (kanban_show tool is present iff HERMES_KANBAN_TASK is set).
1670+
# Kanban worker lifecycle guidance is session-static. Orchestrator profiles
1671+
# can expose kanban_show in ordinary chat, so tool presence alone is not a
1672+
# worker identity signal; the dispatcher task marker must also be present.
16491673
# Resolving the ~835-token block once here avoids re-running the
16501674
# membership test + reference on every system-prompt rebuild
16511675
# (init + each context compression).
1652-
from agent.prompt_builder import KANBAN_GUIDANCE
1653-
agent._kanban_worker_guidance = (
1654-
KANBAN_GUIDANCE if "kanban_show" in agent.valid_tool_names else ""
1676+
agent._kanban_worker_guidance = _resolve_kanban_worker_guidance(
1677+
agent.valid_tool_names
16551678
)
16561679

16571680
# Check tool requirements
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""Regression tests for task-scoped Kanban lifecycle guidance."""
2+
3+
from agent.agent_init import _resolve_kanban_worker_guidance
4+
from agent.prompt_builder import KANBAN_GUIDANCE
5+
6+
7+
def test_orchestrator_chat_does_not_receive_worker_guidance(monkeypatch):
8+
"""Kanban tools in a normal profile chat do not make it a task worker."""
9+
monkeypatch.delenv("HERMES_KANBAN_TASK", raising=False)
10+
11+
assert _resolve_kanban_worker_guidance({"kanban_show", "kanban_create"}) == ""
12+
13+
14+
def test_dispatcher_worker_receives_worker_guidance(monkeypatch):
15+
"""A real dispatcher worker keeps the task lifecycle prompt."""
16+
monkeypatch.setenv("HERMES_KANBAN_TASK", "t_direct_scope")
17+
18+
assert (
19+
_resolve_kanban_worker_guidance({"kanban_show", "kanban_complete"})
20+
== KANBAN_GUIDANCE
21+
)
22+
23+
24+
def test_delegated_child_without_kanban_tools_gets_no_guidance(monkeypatch):
25+
"""An inherited marker cannot inject guidance after tools are stripped."""
26+
monkeypatch.setenv("HERMES_KANBAN_TASK", "t_parent")
27+
28+
assert _resolve_kanban_worker_guidance({"terminal", "read_file"}) == ""

0 commit comments

Comments
 (0)