fix: Windows compatibility — path handling, Git Bash detection, test isolation, and win32 skip markers - #53166
fix: Windows compatibility — path handling, Git Bash detection, test isolation, and win32 skip markers#53166loes5050 wants to merge 10 commits into
Conversation
… SKILL.md snippets
…ubdirectory hints
teknium1
left a comment
There was a problem hiding this comment.
Thanks for collecting several concrete Windows compatibility cases. Current main still has relevant gaps: inline skill snippets invoke literal bash in agent/skill_preprocessing.py:74, raw drive paths are parsed before drive detection in acp_adapter/server.py:165-167, and terminal path extraction uses POSIX shlex.split in agent/subdirectory_hints.py:153.
Problems
tools/delegate_tool.py:3177-3196adds a per-task provider/model routing schema (commitb7c09736eb09). That is unrelated to the Windows fixes and conflicts with the standing delegation-model-routing policy; it should not be included in a Windows salvage.agent/shell_hooks.py:483falls back to literalbashafter_git_bash_path()rejects candidates, which can reselect the System32 WSL shim the helper intentionally filters. Return an actionable error when Git Bash is unavailable, and cover that case.
Suggested changes
- Separate the Windows fixes from the delegation model-routing change.
- Reconcile retained fixes with the current shell-hook and Windows subprocess implementations before salvage.
Automated hermes-sweeper review.
| "enum": ["leaf", "orchestrator"], | ||
| "description": "Per-task role override. See top-level 'role' for semantics.", | ||
| }, | ||
| "model": { |
There was a problem hiding this comment.
This adds model-facing per-task provider/model routing to delegate_task, which is unrelated to the Windows fixes and conflicts with the standing delegation-model-routing policy. Please exclude this schema and its supporting resolver/routing changes from a Windows-focused salvage.
| first = argv[0] | ||
| if first.lower().endswith((".sh", ".bash")) and os.path.isfile(first): | ||
| bash = _git_bash_path() | ||
| return [bash or "bash", first, *argv[1:]] |
There was a problem hiding this comment.
If _git_bash_path() rejected all candidates, this fallback can resolve the System32 WSL shim that the helper intentionally filtered. Return an actionable Git-Bash-not-found error instead, and add coverage for a PATH containing only that shim.
|
Thanks @loes5050 — closing: the core Windows-compat work here (IS_WINDOWS gating, USERPROFILE handling, SIGALRM fixture removal, win32 skip markers) has since been superseded by the merged Windows-native support work on main, and the branch is CONFLICTING across 62 files. The unrelated delegate-tool feature bundled in would need its own PR. Appreciate the early push on native Windows — much of what this identified did get fixed, independently. |
PR Description — Hermes Agent
fix(terminal): prefer $SHELL over bash for background process spawning (#42203)Summary
On macOS,
terminal(background=true)silently failed — the process returned asession_idandexit_code=0, but the command never ran (empty stdout, no sideeffects). Background processes now spawn under the user's actual login shell
(
$SHELL→/bin/zshon Catalina+), so the swallowed command no longer happens.Root cause (#42203)
Two interacting issues:
_find_shellwas aliased to_find_bash, which prefersshutil.which("bash")→/bin/bash(GNU bash 3.2, still shipped on macOS)over
$SHELL(/bin/zsh, the Catalina+ default).process_registry.spawn_localruns[shell, "-lic", "set +m; <cmd>"]withstdin=/dev/null. Bash 3.2 as a login shell (-l) sources~/.bash_profile,which on many macOS setups contains
exec /bin/zsh -l. Thatexecreplacesbash with zsh but drops the
-cargument, so the command is silentlyswallowed (exit 0, no output, no side effects).
The fix
Decouple
_find_shellfrom_find_bash:_find_shellnow prefers the user's configured$SHELLon POSIX (theshell they actually log in with), falling back to
_find_bashwhen$SHELLis unset/missing.
process_registryuses this for the user-facing backgroundcommand.
_find_bashis unchanged — callers that genuinely need bash (e.g. the_run_bashlogin-shell environment snapshot) keep bash semantics.zsh handles
-liccorrectly even with redirected stdin, so the swallow nolonger occurs.
Footprint
hermes_constants.py— new_find_shell();_find_bash()unchanged.tools/process_registry.py—spawn_local()uses_find_shellinstead of_find_bashfor the user-facing background command.tests/tools/test_find_shell.py— 9 unit tests + 1 E2E regression testreproducing the real swallow.
Validation
background=truewith~/.bash_profileexec /bin/zsh -l$SHELL=/bin/zsh_run_bashlogin-shell env snapshot$SHELL)_find_bashfallback path/bin/bash3.2 invoked-licwithstdin=/dev/nulland a~/.bash_profilecontainingexec /bin/zsh -l→exit 0, file NOT created, empty stdout (the silent swallow);
/bin/zsh -licwith the sameHOME→ file created._find_shellto the= _find_bashaliasfails the
$SHELL-preference test.test_process_registry.py::test_close_stdin_allows_eof_driven_process_to_finishreproduces identically on clean
origin/main— not caused by this change.)Tests
Coverage:
$SHELLset / unset / missing / empty → correct fallback$SHELL→ uses_find_bash_find_bashitself is unchanged (regression-protected)pathlib.Path_find_shellselects actually executes a-licbackground command (writes a sentinel file under a tempHOME)Test plan
pytest tests/tools/test_find_shell.py -q— 9 passedpytest tests/tools/test_process_registry.py -q— no new failuresruff check hermes_constants.py tools/process_registry.py tests/tools/test_find_shell.py— clean~/.bash_profileexec zsh)_run_bashlogin-shell snapshot still receives bash, not$SHELLhermes --toolsets terminal -q "run a 30s sleep in the background"on macOS with a~/.bash_profileexec zshOut of scope
_find_bashitself (riskier — bash-dependent callers like_run_bashwould then receive$SHELL/zsh).Checklist
Code
fix(scope):)pytest tests/ -qand all tests passDocumentation & Housekeeping
_find_shellandspawn_local) — README/CONTRIBUTING not affected (no user-facing config change)cli-config.yaml.exampleif I added/changed config keys — N/ACONTRIBUTING.mdorAGENTS.mdif I changed architecture orworkflows — N/A
compatibility guide
— Windows path unchanged (no
$SHELL); macOS specifically fixed(no model-tool surface change)
Closes #42203.
Supersedes #42290 (which changed
_find_bashitself — riskier, as bash-dependentcallers like
_run_bashwould then receive$SHELL/zsh).Why this template works
fix(scope):matches every mergedbug-fix PR in the last 5 (fix(terminal): prefer $SHELL over bash for background process spawning (#42203) #53110, fix(gateway): stamp drain marker with instantiation epoch so a durable-volume restart clears it (NS-570) #53050, fix(security): circuit breaker for tirith crashes to prevent agent hangs (#41400) #52997, fix(backup): include projects.db and kanban boards in pre-update snapshot (#52889) #52990, fix(security): circuit breaker for tirith crashes to prevent agent hangs (#41400) #52997 all use it)
and stays under 72 chars.
the fix. Mirrors fix(terminal): prefer $SHELL over bash for background process spawning (#42203) #53110 / fix(security): circuit breaker for tirith crashes to prevent agent hangs (#41400) #52997 exactly.
causes. Mirrors fix(gateway): stamp drain marker with instantiation epoch so a durable-volume restart clears it (NS-570) #53050, fix(security): circuit breaker for tirith crashes to prevent agent hangs (#41400) #52997, fix(backup): include projects.db and kanban boards in pre-update snapshot (#52889) #52990.
behaviour change. Mirrors all 5 sampled PRs.
git diff --stat-style block. Borrowed directly fromfix(gateway): stamp drain marker with instantiation epoch so a durable-volume restart clears it (NS-570) #53050.
bullet for the literal reproduction. Mirrors fix(security): circuit breaker for tirith crashes to prevent agent hangs (#41400) #52997 and fix(gateway): stamp drain marker with instantiation epoch so a durable-volume restart clears it (NS-570) #53050.
phrase used in fix(terminal): prefer $SHELL over bash for background process spawning (#42203) #53110, fix(gateway): stamp drain marker with instantiation epoch so a durable-volume restart clears it (NS-570) #53050, and fix(backup): include projects.db and kanban boards in pre-update snapshot (#52889) #52990.
PULL_REQUEST_TEMPLATE.mdwhile staying short.
same pattern used by feat(desktop): redesign the clarify prompt + fix its awaiting-input states #52993.
matching fix(security): circuit breaker for tirith crashes to prevent agent hangs (#41400) #52997 and fix(backup): include projects.db and kanban boards in pre-update snapshot (#52889) #52990.
the project template's checklist.