From 597e483dd138c9e7351d257e6d9770ffe5e6b686 Mon Sep 17 00:00:00 2001 From: garrytan Date: Tue, 6 Oct 2026 18:09:47 +0000 Subject: [PATCH] fix: a Codex usage limit is its own state, not 'inconclusive; proceeding' (#3051) The model probe matched only model and broken-install errors, so a usage or rate limit fell through to MODEL_PROBE_INCONCLUSIVE: Codex's own message (with the reset time) was dropped, the hint pointed at model config, and every later skill re-probed for 30 s. The probe now returns MODEL_QUOTA_EXHAUSTED (exit 4) on a failed, non-timeout call that names a usage limit, rate limit, insufficient_quota or 429, relays Codex's line verbatim and caches it for 15 minutes. Preflights map it to CODEX_MODE: quota_exhausted (missing coverage, no further Codex calls), and classifyOutsideReview reports a mid-run limit as unavailable (quota) with its own troubleshooting anchor. Co-authored-by: venkatareddyg --- autoplan/SKILL.md | 3 ++ bin/gstack-codex-probe | 26 +++++++++- codex/SKILL.md | 5 ++ codex/SKILL.md.tmpl | 5 ++ docs/troubleshooting.md | 20 ++++++++ document-release/sections/release-body.md | 3 ++ lib/gate-outcomes.ts | 5 ++ lib/outside-review-result.ts | 8 +++ plan-ceo-review/sections/review-sections.md | 3 ++ plan-devex-review/sections/review-sections.md | 3 ++ plan-eng-review/sections/review-sections.md | 3 ++ review/sections/adversarial.md | 3 ++ scripts/resolvers/constants.ts | 5 +- ship/sections/adversarial.md | 3 ++ test/codex-model-probe.test.ts | 49 +++++++++++++++++++ test/outside-review-result.test.ts | 30 ++++++++++++ 16 files changed, 172 insertions(+), 2 deletions(-) diff --git a/autoplan/SKILL.md b/autoplan/SKILL.md index 4bba21fe2..8fb5117ca 100644 --- a/autoplan/SKILL.md +++ b/autoplan/SKILL.md @@ -773,6 +773,8 @@ else _CODEX_MODE="sandbox_unavailable" elif [ "$_CODEX_MP" -eq 2 ]; then _CODEX_MODE="broken_install" + elif [ "$_CODEX_MP" -eq 4 ]; then + _CODEX_MODE="quota_exhausted" elif [ "$_CODEX_MP" -ne 0 ]; then _CODEX_MODE="model_unusable" elif [ "${_GSTACK_CODEX_PROBE_STATE:-}" = inconclusive ]; then @@ -791,6 +793,7 @@ Branch on the echoed `CODEX_MODE`: - **`not_authed`** — installed but no credentials. Print: "Codex not authenticated; outside coverage unavailable. Run `codex login` or set `$CODEX_API_KEY`." Keep the required Claude adversarial pass; do not dispatch a duplicate. - **`broken_install`** — the CLI is on PATH but cannot execute (spawn ENOENT, non-executable binary, missing vendor payload). Print: "Codex is installed but its binary cannot run — Codex passes skipped. Reinstall: `npm install -g @openai/codex`." Relay the probe's HINT lines. Keep the required Claude adversarial pass; do not dispatch a duplicate. - **`model_unusable`** — the selected model (see `CODEX_MODEL:`) is invalid or unavailable to the account (HTTP 400 on every call). Relay the probe's HINT lines and the fix (`GSTACK_CODEX_MODEL=` or config.toml `model`); never substitute a model. Keep the required Claude adversarial pass; do not dispatch a duplicate. The ~10s round trip is cached for 1h. +- **`quota_exhausted`** — Codex usage/rate limit: relay its line verbatim (reset time), no more Codex calls. Keep the required Claude adversarial pass; do not dispatch a duplicate. - **`sandbox_unavailable`** — Codex's sandbox cannot start here (containers without user namespaces); the probe printed the reason and fix. No paid call ran; outside coverage is unavailable. Keep the required Claude adversarial pass; do not dispatch a duplicate. - **`ready`** or **`unverified`** — run the Codex pass below. `unverified` means the model check timed out; say so, and let the pass's own verdict decide. diff --git a/bin/gstack-codex-probe b/bin/gstack-codex-probe index 151415f2c..772d2487e 100755 --- a/bin/gstack-codex-probe +++ b/bin/gstack-codex-probe @@ -8,7 +8,8 @@ # _gstack_codex_sandbox_mode — read-only, or full access only for GSTACK_CODEX_NO_SANDBOX=1 # _gstack_codex_sandbox_preflight — free `codex sandbox` check before paid calls (Linux) # _gstack_codex_first_use_notice — once per machine: provider, account, how to turn off -# _gstack_codex_model_probe — round-trip probe of the selected model (#2477) +# _gstack_codex_model_probe — round-trip probe of the selected model (#2477); +# exit 4 = the account's Codex usage limit (#3051) # _gstack_codex_version_check — warn on known-bad Codex CLI versions # _gstack_codex_timeout_wrapper — gtimeout -> timeout -> bash-native watchdog # _gstack_codex_log_event — telemetry emission to ~/.gstack/analytics/ @@ -256,6 +257,12 @@ _gstack_codex_model_probe() { echo "MODEL_OK (cached)" return 0 fi + if [ "$_c_status" = "MODEL_QUOTA_EXHAUSTED" ] && [ "$_c_sig" = "$_sig" ] && [ $((_now - _c_ts)) -lt 900 ]; then + echo "MODEL_QUOTA_EXHAUSTED (cached)" + sed -n '2,4p' "$_cache" 2>/dev/null + echo "HINT: Codex refused this account's calls for its usage limit within the last 15 minutes; outside coverage is unavailable until the reset time above. No Codex call was made." + return 4 + fi if [ "$_c_status" = "MODEL_UNUSABLE" ] && [ "$_c_sig" = "$_sig" ] && [ $((_now - _c_ts)) -lt 900 ]; then echo "MODEL_UNUSABLE (cached)" echo "HINT: gstack requested model '$_model' (source: $_GSTACK_CODEX_SEL_SRC)." @@ -271,6 +278,23 @@ _gstack_codex_model_probe() { echo "MODEL_OK" return 0 fi + # A usage limit or rate limit is the account, not the model or the network + # (#3051): Codex says so plainly ("You've hit your usage limit ... try again + # at