Summary
The codex-implementer agent's timeout guard is a no-op under zsh. The command dies with exit 127 before codex exec ever starts, so the advertised ten-minute cap silently never applies. Since zsh is the macOS default shell and the surrounding comment specifically targets macOS, this likely affects most users on the platform.
Location
agents/codex-implementer.md, section "How you run codex", step 2 (v4.0.0, lines 73-76):
T=$(command -v gtimeout || command -v timeout || true)
[ -z "$T" ] && echo "WARN: no timeout binary - codex runs uncapped (brew install coreutils to cap)"
${T:+$T 600} codex exec \
The flag-discipline table at line 94 documents this as "Ten-minute wall clock when timeout/gtimeout exists".
Cause
bash performs word splitting on the unquoted ${T:+$T 600} expansion, producing two arguments. zsh does not split unquoted parameter expansions, so the whole thing becomes a single word: a filename literally named /opt/homebrew/bin/gtimeout 600, which does not exist.
Reproduction
$ zsh -c 'T=$(command -v gtimeout || true); ${T:+$T 2} echo hello'
zsh:1: no such file or directory: /opt/homebrew/bin/gtimeout 2
$ bash -c 'T=$(command -v gtimeout || true); ${T:+$T 2} echo hello'
hello
Impact
Two failure modes, and the second is the concerning one:
- On a machine with coreutils installed, the lane's first
codex exec attempt fails outright with exit 127. Observed live on 2026-08-07: the agent caught the empty result and retried without the wrapper, so the task still completed, but the retry runs uncapped.
- On a machine without coreutils,
T is empty, the expansion vanishes, and codex runs uncapped. That path is intended and warned about, so it is fine.
The net effect is that the cap never actually applies under zsh either way, while the documentation says it does. A hung codex run has no wall-clock bound and never returns STATUS: timeout.
Suggested fix
Positional parameters expand correctly in both shells and handle the empty case cleanly:
T=$(command -v gtimeout || command -v timeout || true)
[ -z "$T" ] && echo "WARN: no timeout binary - codex runs uncapped (brew install coreutils to cap)"
if [ -n "$T" ]; then set -- "$T" 600; else set --; fi
"$@" codex exec \
--model gpt-5.6-luna \
-c model_reasoning_effort=max \
...
Verified four ways, bash and zsh, each with and without the binary present, all correct. Confirmed it actually caps:
$ zsh -c 'T=$(command -v gtimeout || true); if [ -n "$T" ]; then set -- "$T" 2; else set --; fi; "$@" sleep 10; echo "exit=$?"'
exit=124
An alternative is ${=T} to force splitting, but that is zsh-only and would break bash, so the positional-parameter form seems preferable for a cross-shell agent.
Environment
- Plugin
fable-advisor@fable-advisor v4.0.0
- macOS (Darwin 27.0.0), Apple silicon, zsh as the default shell
- Codex CLI 0.146.0, coreutils installed via Homebrew (
gtimeout at /opt/homebrew/bin/gtimeout)
Thanks for the plugin. The architect-as-orchestrator doctrine has been working well for us otherwise; this was the only defect that surfaced across a full day of use.
Summary
The
codex-implementeragent's timeout guard is a no-op under zsh. The command dies with exit 127 beforecodex execever starts, so the advertised ten-minute cap silently never applies. Since zsh is the macOS default shell and the surrounding comment specifically targets macOS, this likely affects most users on the platform.Location
agents/codex-implementer.md, section "How you run codex", step 2 (v4.0.0, lines 73-76):The flag-discipline table at line 94 documents this as "Ten-minute wall clock when
timeout/gtimeoutexists".Cause
bash performs word splitting on the unquoted
${T:+$T 600}expansion, producing two arguments. zsh does not split unquoted parameter expansions, so the whole thing becomes a single word: a filename literally named/opt/homebrew/bin/gtimeout 600, which does not exist.Reproduction
Impact
Two failure modes, and the second is the concerning one:
codex execattempt fails outright with exit 127. Observed live on 2026-08-07: the agent caught the empty result and retried without the wrapper, so the task still completed, but the retry runs uncapped.Tis empty, the expansion vanishes, and codex runs uncapped. That path is intended and warned about, so it is fine.The net effect is that the cap never actually applies under zsh either way, while the documentation says it does. A hung codex run has no wall-clock bound and never returns
STATUS: timeout.Suggested fix
Positional parameters expand correctly in both shells and handle the empty case cleanly:
Verified four ways, bash and zsh, each with and without the binary present, all correct. Confirmed it actually caps:
$ zsh -c 'T=$(command -v gtimeout || true); if [ -n "$T" ]; then set -- "$T" 2; else set --; fi; "$@" sleep 10; echo "exit=$?"' exit=124An alternative is
${=T}to force splitting, but that is zsh-only and would break bash, so the positional-parameter form seems preferable for a cross-shell agent.Environment
fable-advisor@fable-advisorv4.0.0gtimeoutat/opt/homebrew/bin/gtimeout)Thanks for the plugin. The architect-as-orchestrator doctrine has been working well for us otherwise; this was the only defect that surfaced across a full day of use.