Context
In #78 we marked several test modules skipif(win32) to get the new 3-OS CI matrix green:
tests/test_log_sh.py — bash dispatch + ownership checks
tests/test_migration.py — bash bootstrap-dirs.sh
tests/test_security_fixes.py — RCE prevention in safe_eval, path-with-space cleanup, _jq_fallback quoting
tests/test_path_resolution.py — POSIX path layouts + bash subprocess capture
Three of these (test_log_sh, test_migration, test_security_fixes) test logic that does run on Windows when Claude Code invokes hooks via Git Bash. Skipping them means we ship no Windows security/regression coverage for those code paths.
Root cause of the skip
The GitHub Actions Windows runner's bash on PATH resolves to the WSL launcher (C:\Windows\System32\bash.exe), not Git Bash. WSL isn't installed on the runner, so every subprocess.run(["bash", "-c", script], ...) emits a UTF-16-encoded "no distributions installed" error and returns exit 1. The tests see garbage output and fail.
Proposed fix
In .github/workflows/tests.yml, on the Windows row, prepend Git Bash to PATH so subprocess.run(["bash", ...]) resolves to C:\Program Files\Git\bin\bash.exe:
- name: Use Git Bash on Windows
if: runner.os == 'Windows'
shell: pwsh
run: echo "C:\Program Files\Git\bin" | Out-File -FilePath $env:GITHUB_PATH -Append -Encoding utf8
Then revert the pytestmark = pytest.mark.skipif(sys.platform == "win32", ...) blocks added in #78 on:
tests/test_log_sh.py
tests/test_migration.py
tests/test_security_fixes.py
Leave test_umask.py skipped — POSIX mode bits genuinely can't exist on NTFS. Possibly test_path_resolution.py too — /c/Users vs C:\Users path-form assumptions may need targeted per-test fixes.
Out of scope
- Per-test debugging once Git Bash runs — may surface real Windows bugs in the scripts that need actual fixes (path normalization, line endings, etc.).
test_umask.py — NTFS lacks Unix permission bits, not a CI configuration problem.
Test plan
Context
In #78 we marked several test modules
skipif(win32)to get the new 3-OS CI matrix green:tests/test_log_sh.py— bash dispatch + ownership checkstests/test_migration.py— bash bootstrap-dirs.shtests/test_security_fixes.py— RCE prevention insafe_eval, path-with-space cleanup,_jq_fallbackquotingtests/test_path_resolution.py— POSIX path layouts + bash subprocess captureThree of these (
test_log_sh,test_migration,test_security_fixes) test logic that does run on Windows when Claude Code invokes hooks via Git Bash. Skipping them means we ship no Windows security/regression coverage for those code paths.Root cause of the skip
The GitHub Actions Windows runner's
bashonPATHresolves to the WSL launcher (C:\Windows\System32\bash.exe), not Git Bash. WSL isn't installed on the runner, so everysubprocess.run(["bash", "-c", script], ...)emits a UTF-16-encoded "no distributions installed" error and returns exit 1. The tests see garbage output and fail.Proposed fix
In
.github/workflows/tests.yml, on the Windows row, prepend Git Bash toPATHsosubprocess.run(["bash", ...])resolves toC:\Program Files\Git\bin\bash.exe:Then revert the
pytestmark = pytest.mark.skipif(sys.platform == "win32", ...)blocks added in #78 on:tests/test_log_sh.pytests/test_migration.pytests/test_security_fixes.pyLeave
test_umask.pyskipped — POSIX mode bits genuinely can't exist on NTFS. Possiblytest_path_resolution.pytoo —/c/UsersvsC:\Userspath-form assumptions may need targeted per-test fixes.Out of scope
test_umask.py— NTFS lacks Unix permission bits, not a CI configuration problem.Test plan
PATHshim added.windows-latest× 3.9/3.10/3.11/3.12.