Recorded from T-1124 (#283), which set out to establish #71's install-root containment for the wrapper path on Windows. The measurement answered a different question than the one it was written to ask, so it is recorded here rather than absorbed into that ticket.
All of the below is from a windows-latest runner. Nothing here is inferred from macOS behaviour.
1. The commit-msg hook never returns on Windows
A real git commit in a repository where commitlore init installed the hook does not fail and does not succeed. It hangs. In CI it was killed at a 90s bound; the job's cleanup then reported Terminate orphan process: pid (7764) (sh), so the shell was still spinning after the commit was killed.
The cause is the last resolution step in the stub (src/hooks/commit-msg.ts):
dir=$PWD
while [ -n "$dir" ]; do
if [ -x "$dir/node_modules/.bin/commitlore" ]; then exec ...; fi
dir=${dir%/*}
done
${dir%/*} returns its input unchanged once no / remains. A probe hook installed in a scratch repository on the same runner reports what $PWD actually is inside a Git for Windows hook:
hook argv0 = .git/hooks/commit-msg
hook PWD = C:/Users/RUNNER~1/AppData/Local/Temp/.../probe
hook pwd = C:/Users/RUNNER~1/AppData/Local/Temp/.../probe
hook pwd -P = /c/Users/runneradmin/AppData/Local/Temp/.../probe
hook uname = MINGW64_NT-10.0-26100
walk: NON-TERMINATING, stuck at [C:]
The walk was executed with a counter on the runner. It does not terminate; it settles on C: and stays there. For a user this is worse than either accepting or refusing: the commit never comes back.
2. #71's install-root containment cannot match on Windows
The guard the walk is reached through:
recorded_dir=$(cd "$(dirname "$recorded")" 2>/dev/null && pwd -P) || recorded_dir=
case "$recorded_dir" in
"$recorded_root"|"$recorded_root"/*) exec "$recorded_node" "$recorded" ... ;;
esac
Measured on the runner:
commitlore.bin = C:\Users\...\commitlore\v9.9.9\dist\commitlore.mjs
commitlore.root = C:\Users\...\commitlore\v9.9.9
dirname+pwd -P = /c/Users/.../commitlore/v9.9.9/dist
containment case: NO MATCH -- the stub falls through
test -x node: yes
test -L bin: no
commitlore.root is written by recordBinPath as realpathSync(PACKAGE_ROOT), which on Windows is a win32 path. The stub reads it under Git for Windows' sh, where pwd -P answers in POSIX form. Both of the guard's other conditions pass; only the comparison fails, and it fails for every value — the installer's own bundle included. The subdirectory alternative is also hardcoded to /, which a win32 recorded_root never contains.
So the containment check is not holding on Windows and it is not permitting on Windows. It is a dead branch, and it is the reason control reaches the walk in §1 at all.
Note the same section 1 probe shows $PWD and pwd -P disagreeing inside a single hook invocation — the two path worlds are both present, which is what makes a single-form comparison unsafe here.
3. doctor reports no problem in exactly this state
readRecordedHookTarget in src/core/hook-target.ts mirrors the stub for doctor, using node:path:
const isInsidePackage = (path: string): boolean => {
const fromRoot = relative(realpathSync(PACKAGE_ROOT), realpathSync(path));
return fromRoot !== '..' && !fromRoot.startsWith(`..${sep}`) && !isAbsolute(fromRoot);
};
On Windows node:path is path.win32, so it handles the backslashed pair correctly and returns dist\commitlore.mjs — contained, no problem pushed. The mirror is green precisely when the hook it claims to mirror is dead. Any fix has to close this divergence too, or the check that is supposed to warn about a broken hook keeps reporting it as healthy.
4. The PATH fallback cannot find the installed shim
command -v commitlore: NOT found (the shim is commitlore.cmd)
install.ps1 installs commitlore.cmd. MSYS sh's command search does not append .cmd, so the fall-through cannot recover here either. This is also why the PreToolUse hook runtime check reports configured PreToolUse hook executable "commitlore" is not resolvable from PATH.
What this does not say
doctor's commit-msg hook and hook runtime checks report could not run the hook: spawnSync /bin/sh ENOENT. That is the probe's own failure and not the hook's: the runner shows git using /usr/bin/sh and the hook demonstrably runs, since it hangs. Those two doctor checks cannot report on a Windows hook at all until they stop spawning /bin/sh directly. Separating the probe's failure from the hook's was the first thing T-1124 had to measure, and this is the result.
Consequence for the Windows support claim
Windows is reachable through install.ps1 (T-1121, #282). It is not supported, and no document should say otherwise. #283's baseline — a valid record accepted and an invalid one refused, through the recorded install — fails on windows-latest, so #71's two containment attacks behind it have not executed and nothing has been established about them either way.
Evidence: PR #320's install-ps1 job. git version 2.55.0.windows.2, /usr/bin/sh, MINGW64_NT-10.0-26100, Node 22.
Recorded from T-1124 (#283), which set out to establish #71's install-root containment for the wrapper path on Windows. The measurement answered a different question than the one it was written to ask, so it is recorded here rather than absorbed into that ticket.
All of the below is from a
windows-latestrunner. Nothing here is inferred from macOS behaviour.1. The
commit-msghook never returns on WindowsA real
git commitin a repository wherecommitlore initinstalled the hook does not fail and does not succeed. It hangs. In CI it was killed at a 90s bound; the job's cleanup then reportedTerminate orphan process: pid (7764) (sh), so the shell was still spinning after the commit was killed.The cause is the last resolution step in the stub (
src/hooks/commit-msg.ts):${dir%/*}returns its input unchanged once no/remains. A probe hook installed in a scratch repository on the same runner reports what$PWDactually is inside a Git for Windows hook:The walk was executed with a counter on the runner. It does not terminate; it settles on
C:and stays there. For a user this is worse than either accepting or refusing: the commit never comes back.2. #71's install-root containment cannot match on Windows
The guard the walk is reached through:
Measured on the runner:
commitlore.rootis written byrecordBinPathasrealpathSync(PACKAGE_ROOT), which on Windows is a win32 path. The stub reads it under Git for Windows'sh, wherepwd -Panswers in POSIX form. Both of the guard's other conditions pass; only the comparison fails, and it fails for every value — the installer's own bundle included. The subdirectory alternative is also hardcoded to/, which a win32recorded_rootnever contains.So the containment check is not holding on Windows and it is not permitting on Windows. It is a dead branch, and it is the reason control reaches the walk in §1 at all.
Note the same section 1 probe shows
$PWDandpwd -Pdisagreeing inside a single hook invocation — the two path worlds are both present, which is what makes a single-form comparison unsafe here.3.
doctorreports no problem in exactly this statereadRecordedHookTargetinsrc/core/hook-target.tsmirrors the stub fordoctor, usingnode:path:On Windows
node:pathispath.win32, so it handles the backslashed pair correctly and returnsdist\commitlore.mjs— contained, no problem pushed. The mirror is green precisely when the hook it claims to mirror is dead. Any fix has to close this divergence too, or the check that is supposed to warn about a broken hook keeps reporting it as healthy.4. The PATH fallback cannot find the installed shim
install.ps1installscommitlore.cmd. MSYSsh's command search does not append.cmd, so the fall-through cannot recover here either. This is also why thePreToolUse hook runtimecheck reportsconfigured PreToolUse hook executable "commitlore" is not resolvable from PATH.What this does not say
doctor'scommit-msg hookandhook runtimechecks reportcould not run the hook: spawnSync /bin/sh ENOENT. That is the probe's own failure and not the hook's: the runner shows git using/usr/bin/shand the hook demonstrably runs, since it hangs. Those twodoctorchecks cannot report on a Windows hook at all until they stop spawning/bin/shdirectly. Separating the probe's failure from the hook's was the first thing T-1124 had to measure, and this is the result.Consequence for the Windows support claim
Windows is reachable through
install.ps1(T-1121, #282). It is not supported, and no document should say otherwise. #283's baseline — a valid record accepted and an invalid one refused, through the recorded install — fails onwindows-latest, so #71's two containment attacks behind it have not executed and nothing has been established about them either way.Evidence: PR #320's
install-ps1job.git version 2.55.0.windows.2,/usr/bin/sh,MINGW64_NT-10.0-26100, Node 22.