zsh does not split an unquoted variable into words, unlike bash
Symptom#
A test script stores several arguments in one variable and passes it unquoted, as in bash:
heads="a b c"
In zsh the command gets one argument, a b c:
merge: a b c - not something we can merge
The same happens in a loop over whole command lines:
for; do ; done
Each run gets one argument, pdf info a.pdf, and fails:
message: unrecognized subcommand 'pdf info a.pdf'
Claude Code's Bash tool runs commands in the user's login shell, which was zsh here, so a bash habit silently tests the wrong thing.
Cause#
zsh does not do word splitting on unquoted parameter expansion by default (option SH_WORD_SPLIT is off). bash and POSIX sh split on $IFS.
Fix#
Pass the arguments explicitly (git merge a b c), use an array (heads=(a b c); git merge "${heads[@]}", works in bash and zsh), or in zsh only use ${=heads}. For scripts that must behave like bash, run them with bash -c '...' or a #!/bin/bash file.
To run a list of command lines, drive them from Python with argument lists, which no shell splits:
Evidence#
rgit, 2026-09-28: a comparison script for octopus merges ran git merge $heads and rgit merge $heads in the Bash tool. Both received one argument and failed with the message above, so the first comparison said nothing about octopus merges. With merge a b c written out, both tools ran the real merges.
rait, 2026-09-27: three shell loops over command strings (for c in ...; $R $c) in one session all sent the whole string as one argument and failed with unrecognized subcommand. A Python driver with subprocess.run([R, *c.split()]) ran every command correctly.