mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-12 07:59:02 +02:00
fix(codex): a CLI that cannot execute no longer reports CODEX_MODE: ready
Follow-up to #2477. The model probe it added does a real round trip, but its
final branch is the `else` of a "model 400" grep, so it swallowed spawn ENOENT,
non-executable binaries and missing vendor payloads alongside genuine network
timeouts. All three are deterministic — retrying never helps — yet they landed
in the fail-open bucket and resolved to `ready`, so every Codex pass was
skipped in silence and the review reported itself complete.
Observed live: @openai/codex was on PATH with an empty
vendor/aarch64-apple-darwin/codex/ directory. gstack said `ready` for two
months while no Codex pass ran.
Three changes:
- `_gstack_codex_model_probe` classifies deterministic install failures (exit
126/127, or stderr matching ENOENT/ENOEXEC/EACCES/"cannot execute binary
file") as MODEL_UNUSABLE_INSTALL, exit 2, never cached — a reinstall is
picked up on the next probe. Exit 124 and genuine transients still fail open,
which is what #2477 intended.
- The preflight chain captures the probe's code instead of testing it for
truthiness, so exit 2 routes to a new `broken_install` mode whose remedy is
`npm install -g @openai/codex` rather than "check your model pin". A missing
binary and an unusable model are different problems with different fixes.
- `_gstack_codex_version_check` no longer reads a broken CLI as healthy. It ran
`codex --version 2>/dev/null | head -1`, which captures head's status, not
codex's — and 2>/dev/null discarded the one diagnostic available. It now
captures the real exit code and warns on non-zero. Empty-but-successful
output stays silent, per the existing "empty output → OK" case.
Tests: 6 added to test/codex-hardening.test.ts covering both broken-install
shapes, the exit-2 contract, no caching, the transient still failing open, the
model 400 still classifying as MODEL_UNUSABLE, and the version-check warning.
845 pass / 0 fail across all 8 suites touching the changed files.
Closes #2742
Wave-amended: autoplan hand-maintained preflight chain completed (tmpl+render); install-signature grep gated on failed spawn only; goldens regenerated against the wave tree (author's golden commit 5797d326 superseded); +2 tests
This commit is contained in:
+14
-5
@@ -793,12 +793,21 @@ elif ! _gstack_codex_auth_probe >/dev/null; then
|
||||
# Round-trip model probe (#2477): auth can pass while the account's configured
|
||||
# model is rejected with an HTTP 400 (stale `model =` pin in ~/.codex/config.toml).
|
||||
# ~10s on first run, cached 1h; timeouts fail open (probe returns 0).
|
||||
elif ! _gstack_codex_model_probe; then
|
||||
echo "[codex-unavailable: configured model rejected] — proceeding with Claude subagent only. Fix the \`model =\` pin in ~/.codex/config.toml (see [notice.model_migrations] there for the replacement)."
|
||||
_CODEX_AVAILABLE=false
|
||||
# Exit 2 = broken install (#2742: spawn ENOENT / non-executable binary /
|
||||
# missing vendor payload) — a different problem with a different fix, so
|
||||
# capture the code instead of testing truthiness.
|
||||
else
|
||||
_gstack_codex_version_check # non-blocking warn if known-bad
|
||||
_CODEX_AVAILABLE=true
|
||||
_gstack_codex_model_probe; _CODEX_MP=$?
|
||||
if [ "$_CODEX_MP" -eq 2 ]; then
|
||||
echo "[codex-unavailable: binary cannot run] — proceeding with Claude subagent only. Reinstall: \`npm install -g @openai/codex\` (#2742)."
|
||||
_CODEX_AVAILABLE=false
|
||||
elif [ "$_CODEX_MP" -ne 0 ]; then
|
||||
echo "[codex-unavailable: configured model rejected] — proceeding with Claude subagent only. Fix the \`model =\` pin in ~/.codex/config.toml (see [notice.model_migrations] there for the replacement)."
|
||||
_CODEX_AVAILABLE=false
|
||||
else
|
||||
_gstack_codex_version_check # non-blocking warn if known-bad
|
||||
_CODEX_AVAILABLE=true
|
||||
fi
|
||||
fi
|
||||
```
|
||||
|
||||
|
||||
+14
-5
@@ -278,12 +278,21 @@ elif ! _gstack_codex_auth_probe >/dev/null; then
|
||||
# Round-trip model probe (#2477): auth can pass while the account's configured
|
||||
# model is rejected with an HTTP 400 (stale `model =` pin in ~/.codex/config.toml).
|
||||
# ~10s on first run, cached 1h; timeouts fail open (probe returns 0).
|
||||
elif ! _gstack_codex_model_probe; then
|
||||
echo "[codex-unavailable: configured model rejected] — proceeding with Claude subagent only. Fix the \`model =\` pin in ~/.codex/config.toml (see [notice.model_migrations] there for the replacement)."
|
||||
_CODEX_AVAILABLE=false
|
||||
# Exit 2 = broken install (#2742: spawn ENOENT / non-executable binary /
|
||||
# missing vendor payload) — a different problem with a different fix, so
|
||||
# capture the code instead of testing truthiness.
|
||||
else
|
||||
_gstack_codex_version_check # non-blocking warn if known-bad
|
||||
_CODEX_AVAILABLE=true
|
||||
_gstack_codex_model_probe; _CODEX_MP=$?
|
||||
if [ "$_CODEX_MP" -eq 2 ]; then
|
||||
echo "[codex-unavailable: binary cannot run] — proceeding with Claude subagent only. Reinstall: \`npm install -g @openai/codex\` (#2742)."
|
||||
_CODEX_AVAILABLE=false
|
||||
elif [ "$_CODEX_MP" -ne 0 ]; then
|
||||
echo "[codex-unavailable: configured model rejected] — proceeding with Claude subagent only. Fix the \`model =\` pin in ~/.codex/config.toml (see [notice.model_migrations] there for the replacement)."
|
||||
_CODEX_AVAILABLE=false
|
||||
else
|
||||
_gstack_codex_version_check # non-blocking warn if known-bad
|
||||
_CODEX_AVAILABLE=true
|
||||
fi
|
||||
fi
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user