Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion gateway/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2082,7 +2082,9 @@ def _enable_from_env(platform: Platform) -> PlatformConfig:
if hass_token:
if Platform.HOMEASSISTANT not in config.platforms:
config.platforms[Platform.HOMEASSISTANT] = PlatformConfig()
config.platforms[Platform.HOMEASSISTANT].enabled = True
ha_explicit = bool(config.platforms[Platform.HOMEASSISTANT].extra.get("_enabled_explicit", False))
if not ha_explicit or config.platforms[Platform.HOMEASSISTANT].enabled:
config.platforms[Platform.HOMEASSISTANT].enabled = True
config.platforms[Platform.HOMEASSISTANT].token = hass_token
hass_url = getenv("HASS_URL")
if hass_url:
Expand Down
115 changes: 59 additions & 56 deletions gateway/kanban_watchers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1347,75 +1347,78 @@ def _auto_decompose_tick(auto_decompose_per_tick: int) -> int:
"""
try:
from hermes_cli import kanban_decompose as _decomp
from gateway.run import _profile_runtime_scope
except Exception as exc: # pragma: no cover
logger.warning(
"kanban auto-decompose: import failed (%s); skipping", exc,
)
return 0
try:
boards = _kb.list_boards(include_archived=False)
except Exception:
boards = [_kb.read_board_metadata(_kb.DEFAULT_BOARD)]
attempted = 0
successes = 0
for b in boards:
slug = b.get("slug") or _kb.DEFAULT_BOARD
if attempted >= auto_decompose_per_tick:
break
# Pin this board for the duration of the call — same
# pattern as the dashboard specify endpoint. The
# decomposer module connects with no board kwarg and
# relies on the env var.
prev_env = os.environ.get("HERMES_KANBAN_BOARD")
profile_home = getattr(self, "home", None) or Path(os.environ.get("HERMES_HOME", "~/.hermes")).expanduser()
with _profile_runtime_scope(Path(profile_home)):
try:
os.environ["HERMES_KANBAN_BOARD"] = slug
boards = _kb.list_boards(include_archived=False)
except Exception:
boards = [_kb.read_board_metadata(_kb.DEFAULT_BOARD)]
attempted = 0
successes = 0
for b in boards:
slug = b.get("slug") or _kb.DEFAULT_BOARD
if attempted >= auto_decompose_per_tick:
break
# Pin this board for the duration of the call — same
# pattern as the dashboard specify endpoint. The
# decomposer module connects with no board kwarg and
# relies on the env var.
prev_env = os.environ.get("HERMES_KANBAN_BOARD")
try:
triage_ids = _decomp.list_triage_ids()
except Exception as exc:
logger.debug(
"kanban auto-decompose: list_triage_ids failed on board %s (%s)",
slug, exc,
)
triage_ids = []
for tid in triage_ids:
if attempted >= auto_decompose_per_tick:
break
attempted += 1
os.environ["HERMES_KANBAN_BOARD"] = slug
try:
outcome = _decomp.decompose_task(
tid, author="auto-decomposer",
)
except Exception:
logger.exception(
"kanban auto-decompose: decompose_task crashed on %s",
tid,
triage_ids = _decomp.list_triage_ids()
except Exception as exc:
logger.debug(
"kanban auto-decompose: list_triage_ids failed on board %s (%s)",
slug, exc,
)
continue
if outcome.ok:
successes += 1
if outcome.fanout and outcome.child_ids:
logger.info(
"kanban auto-decompose [%s]: %s → %d children",
slug, tid, len(outcome.child_ids),
triage_ids = []
for tid in triage_ids:
if attempted >= auto_decompose_per_tick:
break
attempted += 1
try:
outcome = _decomp.decompose_task(
tid, author="auto-decomposer",
)
except Exception:
logger.exception(
"kanban auto-decompose: decompose_task crashed on %s",
tid,
)
continue
if outcome.ok:
successes += 1
if outcome.fanout and outcome.child_ids:
logger.info(
"kanban auto-decompose [%s]: %s → %d children",
slug, tid, len(outcome.child_ids),
)
else:
logger.info(
"kanban auto-decompose [%s]: %s → single task (no fanout)",
slug, tid,
)
else:
logger.info(
"kanban auto-decompose [%s]: %s → single task (no fanout)",
slug, tid,
# Common no-op reasons (no aux client configured) shouldn't
# spam logs every tick. Log at debug.
logger.debug(
"kanban auto-decompose [%s]: decompose_task failed on %s (%s)",
slug, tid, outcome.reason,
)
finally:
if prev_env is not None:
os.environ["HERMES_KANBAN_BOARD"] = prev_env
else:
# Common no-op reasons (no aux client configured) shouldn't
# spam logs every tick. Log at debug.
logger.debug(
"kanban auto-decompose [%s]: %s skipped: %s",
slug, tid, outcome.reason,
)
finally:
if prev_env is None:
os.environ.pop("HERMES_KANBAN_BOARD", None)
else:
os.environ["HERMES_KANBAN_BOARD"] = prev_env
return successes
os.environ.pop("HERMES_KANBAN_BOARD", None)
return successes

logger.info(
"kanban dispatcher: embedded in gateway (interval=%.1fs)", interval
Expand Down