Files
hermes-agent/tests/gateway/test_kanban_auto_decompose_secret_scope.py
teknium1 9541317aee fix(kanban): trim auto-decompose scope shim and add an invariant test
Salvage follow-up to #107955 (Alex Tu) and #109494 (EloquentBrush0x):

- _default_profile_secret_scope: drop the import try/except, the
  current_secret_scope() short-circuit and the build-failure fallthrough.
  The tick always runs in a fresh Context (no scope can be present) and a
  failure to build the launch profile scope must surface, not silently
  degrade to an unscoped tick.
- Regression test proven red on origin/main: run the real
  auto_decompose_tick through _to_thread_process_service under multiplex
  and assert the decomposer reads the launch profile .env value; ports the
  contract from #57837 (srojk34) to the post-refactor dispatcher.
- Trim the #109494 test docstring to the invariant.
2026-09-13 14:43:19 -07:00

50 lines
1.8 KiB
Python

"""Auto-decompose tick under multiplex.
Regression for #107955 / #57837: the tick runs off-turn in a fresh Context, so
``get_secret`` fails closed unless the tick installs the launch profile's scope.
"""
from __future__ import annotations
import asyncio
import sys
from types import SimpleNamespace
from agent import secret_scope as ss
from gateway import kanban_watchers_dispatcher as kwd
from gateway.kanban_watchers_common import _to_thread_process_service
def _dispatcher():
settings = kwd._DispatcherSettings(60.0, None, None, 2, 0, True, None, None)
return kwd._KanbanDispatcher(SimpleNamespace(DEFAULT_BOARD="default"), settings)
def test_auto_decompose_tick_reads_launch_profile_secrets_under_multiplex(monkeypatch, tmp_path):
import hermes_cli
(tmp_path / ".env").write_text("ANTHROPIC_API_KEY=launch-profile-key\n", encoding="utf-8")
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
monkeypatch.setattr(kwd, "_board_slugs", lambda kb: ["default"])
seen = {}
def fake_decompose(task_id, author=None):
seen["value"] = ss.get_secret("ANTHROPIC_API_KEY")
return SimpleNamespace(ok=True, fanout=False, child_ids=None, reason=None)
fake = SimpleNamespace(list_triage_ids=lambda: ["t1"], decompose_task=fake_decompose)
monkeypatch.setitem(sys.modules, "hermes_cli.kanban_decompose", fake)
monkeypatch.setattr(hermes_cli, "kanban_decompose", fake, raising=False)
ss.set_multiplex_active(True)
try:
# Same hop the gateway uses: fresh Context, no inherited per-turn scope.
decomposed = asyncio.run(_to_thread_process_service(_dispatcher().auto_decompose_tick, 5))
finally:
ss.set_multiplex_active(False)
assert decomposed == 1
assert seen["value"] == "launch-profile-key"
assert ss.current_secret_scope() is None