diff --git a/bin/gstack-codex-probe b/bin/gstack-codex-probe index 181aa85eb..a40e0df1a 100755 --- a/bin/gstack-codex-probe +++ b/bin/gstack-codex-probe @@ -877,7 +877,7 @@ _gstack_codex_effort_check() { *) echo "CODEX_EFFORT: invalid — '$_chosen' from $_source is not a Codex reasoning effort level. No Codex call was made." >&2 if [ -n "$_request" ]; then - echo "HINT: use one of minimal, low, medium, high, xhigh, max or ultra (e.g. /codex review --effort medium), or drop --effort to keep the configured effort." >&2 + echo "HINT: use one of minimal, low, medium, high, xhigh, max or ultra (e.g. /codex review --effort medium), or drop --effort to keep the configured effort. See docs/model-policy.md#reasoning-effort." >&2 else echo "HINT: use one of minimal, low, medium, high, xhigh, max or ultra (export GSTACK_CODEX_EFFORT= or gstack-config set codex_effort ), or unset it to keep each gate's own effort." >&2 fi diff --git a/bin/gstack-config b/bin/gstack-config index 059189272..53122a6be 100755 --- a/bin/gstack-config +++ b/bin/gstack-config @@ -185,7 +185,8 @@ CONFIG_HEADER='# gstack configuration — edit freely, changes take effect on ne # codex_effort: # Codex reasoning effort for outside reviews and /codex: # # minimal | low | medium | high | xhigh | max | ultra. # # Absent = every gate keeps its effort. GSTACK_CODEX_EFFORT -# # overrides it per run; /spec keeps medium (120 s budget). +# # overrides it per run, /codex --effort per request; +# # /spec keeps medium (120 s budget). # Docs: docs/model-policy.md # # ─── Advanced ──────────────────────────────────────────────────────── @@ -335,7 +336,7 @@ lookup_default() { plan_review_tier) echo "frontier" ;; # frontier | smart | host — lib/model-policy.ts reads it strictly implementation_tier) echo "smart" ;; # frontier | smart model_frontier_claude|model_frontier_openai|model_smart_claude|model_smart_openai) echo "" ;; # empty = shipped catalog ID (gstack-models list) - codex_effort) echo "" ;; # empty = each gate's own effort; GSTACK_CODEX_EFFORT wins (gstack-codex-probe run-with-timeout --effort) + codex_effort) echo "" ;; # empty = each gate's own effort; /codex --effort, then GSTACK_CODEX_EFFORT, win (gstack-codex-probe run-with-timeout --effort) ship_greptile_early) echo "true" ;; # true | false — /ship's early Greptile PR # Unknown key: exit non-zero instead of printing "". The fallback pattern # the preambles use, diff --git a/codex/SKILL.md b/codex/SKILL.md index 0f986da69..b812217af 100644 --- a/codex/SKILL.md +++ b/codex/SKILL.md @@ -486,8 +486,8 @@ probes and dispatches the policy model within its own timeout. [Policy setup](ht **Effort flags, read before the mode:** `--effort `, `--effort=` or `--xhigh` (= `--effort xhigh`) directly after `/codex` or its mode word, among its leading flags (`-m`, `--model`, `--role`) and before any prompt text, set `` (else `''`); -remove them before Step 1. A later `--effort` is prompt text. Missing value, not one -lowercase word, or conflicting flags: stop and say so. +remove them before Step 1. A later `--effort` is prompt text; say so in one line. Missing +value, not one lowercase word, or differing levels: stop, name the levels; nothing is sent. ```bash _CODEX_PROBE=~/.claude/skills/gstack/bin/gstack-codex-probe diff --git a/codex/SKILL.md.tmpl b/codex/SKILL.md.tmpl index 2e0aee42e..619b8977e 100644 --- a/codex/SKILL.md.tmpl +++ b/codex/SKILL.md.tmpl @@ -80,8 +80,8 @@ probes and dispatches the policy model within its own timeout. [Policy setup](ht **Effort flags, read before the mode:** `--effort `, `--effort=` or `--xhigh` (= `--effort xhigh`) directly after `/codex` or its mode word, among its leading flags (`-m`, `--model`, `--role`) and before any prompt text, set `` (else `''`); -remove them before Step 1. A later `--effort` is prompt text. Missing value, not one -lowercase word, or conflicting flags: stop and say so. +remove them before Step 1. A later `--effort` is prompt text; say so in one line. Missing +value, not one lowercase word, or differing levels: stop, name the levels; nothing is sent. ```bash _CODEX_PROBE=~/.claude/skills/gstack/bin/gstack-codex-probe diff --git a/docs/model-policy.md b/docs/model-policy.md index 92115b90e..b3ecd6e2c 100644 --- a/docs/model-policy.md +++ b/docs/model-policy.md @@ -123,11 +123,12 @@ explicit implementation model rather than delegating to a hidden native choice. ## Reasoning effort Codex calls carry a `model_reasoning_effort`. It follows the same shape as the -model: a per-run environment override, then a stored setting, then the default -each gate ships with. +model: a per-request flag on `/codex`, a per-run environment override, then a +stored setting, then the default each gate ships with. | Order | Source | Set it with | | --- | --- | --- | +| 0 | `/codex ... --effort ` (this request only; `/codex` alone) | `/codex review --effort medium` | | 1 | `GSTACK_CODEX_EFFORT` (this shell, this run) | `export GSTACK_CODEX_EFFORT=max` | | 2 | `codex_effort` in config.yaml | `gstack-config set codex_effort low` (`unset` restores the defaults) | | 3 | the gate's default | nothing: `high` for reviews, plan reviews and `/codex` review and challenge; `medium` for `/codex` consult and design direction | @@ -141,10 +142,11 @@ before anything is sent, naming the source to fix. `gstack-codex-probe check-effort ` prints the same resolution without a call. For one request, `/codex review --effort medium` (or `--effort=`; `--xhigh` is -shorthand for `--effort xhigh`) outranks all three, including an exported +shorthand for `--effort xhigh`) outranks the other three, including an exported `GSTACK_CODEX_EFFORT`, and changes nothing that persists. The flag counts only -directly after `/codex` or its mode word, before the prompt text; a later `--effort` -is part of the prompt. `/codex` passes the level to the probe as `--request-effort`, +directly after `/codex` or its mode word, among the leading flags (`-m`/`--model`, +`--role`, in any order) and before the prompt text; a later `--effort` is part of the +prompt, and `/codex` says so. A Consult follow-up resolves its effort afresh. `/codex` passes the level to the probe as `--request-effort`, so an unknown level stops before any Codex call, naming the flag. `/spec`'s quality gate keeps `medium` inside its 120-second budget whatever you diff --git a/lib/model-policy-cli.ts b/lib/model-policy-cli.ts index 7626693d6..81700082a 100644 --- a/lib/model-policy-cli.ts +++ b/lib/model-policy-cli.ts @@ -66,9 +66,10 @@ Settings (gstack-config set|unset ): codex_effort ${CODEX_EFFORT_LEVELS.join(' | ')} (default: each gate's own effort) -Codex reasoning effort: GSTACK_CODEX_EFFORT (per run), then codex_effort, then -the gate's default (high for reviews, medium for /codex consult and design -direction). /spec keeps medium inside its 120 s budget. +Codex reasoning effort: /codex --effort (one request), then +GSTACK_CODEX_EFFORT (per run), then codex_effort, then the gate's default (high +for reviews, medium for /codex consult and design direction). /spec keeps medium +inside its 120 s budget. A detected custom Anthropic or OpenAI endpoint (Bedrock, Vertex, Foundry, a base-URL override or a Codex model_provider) never receives a shipped public ID: set a diff --git a/test/codex-effort.test.ts b/test/codex-effort.test.ts index 4a8f04b9a..da5ca812a 100644 --- a/test/codex-effort.test.ts +++ b/test/codex-effort.test.ts @@ -43,7 +43,7 @@ describe('manual /codex modes (#3098)', () => { const flags = between(body, '**Effort flags, read before the mode:**', '```bash'); expectTokens(flags, ['--effort ', '--effort=', '--xhigh', '--effort xhigh', ''], 'codex effort flags'); expectMentions(flags, [['directly after', 'mode word', 'leading flags', '--model', '--role', 'before any prompt text'], ['later', 'prompt text'], ['remove', 'before Step 1'], - ['missing', 'lowercase word', 'conflicting', 'stop']], 'codex effort flags'); + ['missing', 'lowercase word', 'differing levels', 'stop'], ['later', 'say so']], 'codex effort flags'); const step1 = between(body, '## Step 1: Detect mode', '- Review (2A)'); expectMentions(step1, [['effort flags removed'], ['--request-effort', 'outranks']], 'codex Step 1'); }); @@ -61,7 +61,8 @@ describe('manual /codex modes (#3098)', () => { test('docs/model-policy.md documents the per-request --effort flag in its Reasoning effort section', () => { const doc = between(read('docs/model-policy.md'), '## Reasoning effort', '## Which workflows use the tiers?'); expectTokens(doc, ['/codex review --effort medium', '--effort=', '--xhigh', '--request-effort', 'GSTACK_CODEX_EFFORT'], 'model-policy effort'); - expectMentions(doc, [['outranks', 'exported'], ['directly after', 'mode word', 'prompt'], ['unknown level', 'before any Codex call']], 'model-policy effort'); + expectMentions(doc, [['outranks', 'exported'], ['directly after', 'mode word', 'leading flags', 'prompt'], ['unknown level', 'before any Codex call']], 'model-policy effort'); + expect(doc).toContain('| 0 | `/codex ... --effort `'); }); }); @@ -173,6 +174,7 @@ echo '{"type":"item.completed","item":{"type":"agent_message","text":"SCORE: 8"} expect(bad.stdout).toBe(''); expect(bad.stderr).toContain("'turbo' from /codex --effort"); expect(bad.stderr).not.toContain('export GSTACK_CODEX_EFFORT'); + expect(bad.stderr).toContain('docs/model-policy.md#reasoning-effort'); const unfilled = check(['high', '--request', '']); expect(unfilled.status).toBe(1); expect(unfilled.stderr).toContain("'' placeholder was left unfilled");