Problem: hermes -w sessions accumulated 37+ worktrees and 1200+ orphaned
branches because:
- _cleanup_worktree bailed on any dirty working tree, but agent sessions
almost always leave untracked files/artifacts behind
- _prune_stale_worktrees had the same dirty-check, so stale worktrees
survived indefinitely
- pr-* and hermes/* branches from PR review had zero cleanup mechanism
Changes:
- _cleanup_worktree: check for unpushed commits instead of dirty state.
Agent work lives in pushed commits/PRs — dirty working tree without
unpushed commits is just artifacts, safe to remove.
- _prune_stale_worktrees: three-tier age system:
- Under 24h: skip (session may be active)
- 24h-72h: remove if no unpushed commits
- Over 72h: force remove regardless
- New _prune_orphaned_branches: on each -w startup, deletes local
hermes/hermes-* and pr-* branches with no corresponding worktree.
Protects main, checked-out branch, and active worktree branches.
Tests: 42 pass (6 new covering unpushed-commit logic, force-prune
tier, and orphaned branch cleanup).
* refactor: re-architect tests to mirror the codebase
* Update tests.yml
* fix: add missing tool_error imports after registry refactor
* fix(tests): replace patch.dict with monkeypatch to prevent env var leaks under xdist
patch.dict(os.environ) can leak TERMINAL_ENV across xdist workers,
causing test_code_execution tests to hit the Modal remote path.
* fix(tests): fix update_check and telegram xdist failures
- test_update_check: replace patch("hermes_cli.banner.os.getenv") with
monkeypatch.setenv("HERMES_HOME") — banner.py no longer imports os
directly, it uses get_hermes_home() from hermes_constants.
- test_telegram_conflict/approval_buttons: provide real exception classes
for telegram.error mock (NetworkError, TimedOut, BadRequest) so the
except clause in connect() doesn't fail with "catching classes that do
not inherit from BaseException" when xdist pollutes sys.modules.
* fix(tests): accept unavailable_models kwarg in _prompt_model_selection mock