mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-31 22:14:25 +01:00
v1.91.45.0 feat: configurable model tiers and advisory freshness (#3073)
* feat: add configurable model tiers and advisory freshness * fix: disclose role selections before Claude review execution * fix: reject malformed model policies and keep model pins portable * fix: separate readiness diagnostics and bound version checks * fix: preserve actionable model failures and validate review inputs before probes * fix: retry released probe locks and retain late-rejection repair context * feat: finalize configurable model tiers and verified release repairs * fix: preserve strict model policy reads on Windows * fix: keep fixture probes read-only and clarify pending CEO decisions * fix: verify native terminal closure before bounded descriptor settlement
This commit is contained in:
1 parent
52eec18c63
commit
92cfd07a79
96 files changed
+6539
-411
No files matched your search
+14
-9
@@ -470,12 +470,16 @@ and every outside review run as a command, one subcommand per check.
|
||||
Model order: a model the user names for this request, `GSTACK_CODEX_MODEL`, Codex
|
||||
`config.toml` `model` (`review_model` first for `codex review`; honors `$CODEX_HOME`),
|
||||
then `gpt-6-astra`. Each call prints `CODEX_MODEL: <model> (<kind>; source: ...)` first.
|
||||
For a named model, add `--model '<model>'` to every `select-model` and `probe-model`
|
||||
call, including the probe below. An invalid or unavailable choice stops with a repair
|
||||
message, never the default.
|
||||
For a named model, add `--model '<model>'` to every `select-model`, `probe-model` and
|
||||
`role-ready` call, including the probe below. An invalid or unavailable choice stops
|
||||
with a repair message, never the default; a named model also wins over a role.
|
||||
Only for an explicit `--role plan-review` (e.g. `/codex challenge --role plan-review`),
|
||||
set `_CODEX_ROLE='plan-review'` below and in the mode block; the mode block then
|
||||
probes and dispatches the policy model within its own timeout. [Policy setup](https://github.com/garrytan/gstack/blob/main/docs/model-policy.md).
|
||||
|
||||
```bash
|
||||
_CODEX_PROBE=~/.claude/skills/gstack/bin/gstack-codex-probe
|
||||
_CODEX_ROLE=''
|
||||
[ -x "$_CODEX_PROBE" ] || { echo "HELPER_UNAVAILABLE"; exit 1; }
|
||||
|
||||
# GSTACK_ACTIVE_HOST names the harness, never the model.
|
||||
@@ -491,7 +495,7 @@ fi
|
||||
if ! "$_CODEX_PROBE" check-auth >/dev/null; then
|
||||
"$_CODEX_PROBE" log-event codex_auth_failed
|
||||
echo "AUTH_FAILED"
|
||||
elif "$_CODEX_PROBE" check-sandbox; then # free; Linux only
|
||||
elif "$_CODEX_PROBE" check-sandbox && [ -z "$_CODEX_ROLE" ]; then # free; Linux only
|
||||
"$_CODEX_PROBE" probe-model exec # ~10s round trip on first run, cached 1h
|
||||
fi
|
||||
"$_CODEX_PROBE" check-version # warns if known-bad, non-blocking
|
||||
@@ -847,11 +851,12 @@ If token count is not available, display: `Tokens: unknown`
|
||||
None of these is an auth or network failure, and the auth probe cannot catch them.
|
||||
Recovery, in order:
|
||||
1. Read the `CODEX_MODEL:` line: it names the model and where it came from.
|
||||
2. Fix that source: name another model for this request, update
|
||||
`GSTACK_CODEX_MODEL`, or change `model` (or `review_model`) in the Codex
|
||||
`config.toml`. With none of these set, gstack uses `gpt-6-astra`; set any of
|
||||
them to a model the account can use.
|
||||
3. If Codex printed `[notice.model_migrations]`, use that replacement model.
|
||||
2. Fix that source and relay its HINT/Repair lines. With `--role plan-review`,
|
||||
tier pins outrank native settings; native `model`/`review_model` applies
|
||||
only in `host` mode. Without a role, request, `GSTACK_CODEX_MODEL`, then
|
||||
native settings choose the model, falling back to `gpt-6-astra`.
|
||||
3. If Codex printed `[notice.model_migrations]`, use that replacement for
|
||||
no-role calls. For a role, offer an explicit override; do not silently substitute.
|
||||
Never present this as a model stall or a PASS — it is a fail-closed gate result.
|
||||
- **`VERDICT: unavailable`:** the shared validator found the run did not execute (for
|
||||
example `Codex's sandbox could not start here`). Relay its line and fix verbatim; it is
|
||||
|
||||
+14
-9
@@ -70,19 +70,23 @@ and every outside review run as a command, one subcommand per check.
|
||||
Model order: a model the user names for this request, `GSTACK_CODEX_MODEL`, Codex
|
||||
`config.toml` `model` (`review_model` first for `codex review`; honors `$CODEX_HOME`),
|
||||
then `gpt-6-astra`. Each call prints `CODEX_MODEL: <model> (<kind>; source: ...)` first.
|
||||
For a named model, add `--model '<model>'` to every `select-model` and `probe-model`
|
||||
call, including the probe below. An invalid or unavailable choice stops with a repair
|
||||
message, never the default.
|
||||
For a named model, add `--model '<model>'` to every `select-model`, `probe-model` and
|
||||
`role-ready` call, including the probe below. An invalid or unavailable choice stops
|
||||
with a repair message, never the default; a named model also wins over a role.
|
||||
Only for an explicit `--role plan-review` (e.g. `/codex challenge --role plan-review`),
|
||||
set `_CODEX_ROLE='plan-review'` below and in the mode block; the mode block then
|
||||
probes and dispatches the policy model within its own timeout. [Policy setup](https://github.com/garrytan/gstack/blob/main/docs/model-policy.md).
|
||||
|
||||
```bash
|
||||
_CODEX_PROBE=~/.claude/skills/gstack/bin/gstack-codex-probe
|
||||
_CODEX_ROLE=''
|
||||
[ -x "$_CODEX_PROBE" ] || { echo "HELPER_UNAVAILABLE"; exit 1; }
|
||||
|
||||
{{OUTSIDE_SELF_GUARD:codex}}
|
||||
if ! "$_CODEX_PROBE" check-auth >/dev/null; then
|
||||
"$_CODEX_PROBE" log-event codex_auth_failed
|
||||
echo "AUTH_FAILED"
|
||||
elif "$_CODEX_PROBE" check-sandbox; then # free; Linux only
|
||||
elif "$_CODEX_PROBE" check-sandbox && [ -z "$_CODEX_ROLE" ]; then # free; Linux only
|
||||
"$_CODEX_PROBE" probe-model exec # ~10s round trip on first run, cached 1h
|
||||
fi
|
||||
"$_CODEX_PROBE" check-version # warns if known-bad, non-blocking
|
||||
@@ -313,11 +317,12 @@ If token count is not available, display: `Tokens: unknown`
|
||||
None of these is an auth or network failure, and the auth probe cannot catch them.
|
||||
Recovery, in order:
|
||||
1. Read the `CODEX_MODEL:` line: it names the model and where it came from.
|
||||
2. Fix that source: name another model for this request, update
|
||||
`GSTACK_CODEX_MODEL`, or change `model` (or `review_model`) in the Codex
|
||||
`config.toml`. With none of these set, gstack uses `gpt-6-astra`; set any of
|
||||
them to a model the account can use.
|
||||
3. If Codex printed `[notice.model_migrations]`, use that replacement model.
|
||||
2. Fix that source and relay its HINT/Repair lines. With `--role plan-review`,
|
||||
tier pins outrank native settings; native `model`/`review_model` applies
|
||||
only in `host` mode. Without a role, request, `GSTACK_CODEX_MODEL`, then
|
||||
native settings choose the model, falling back to `gpt-6-astra`.
|
||||
3. If Codex printed `[notice.model_migrations]`, use that replacement for
|
||||
no-role calls. For a role, offer an explicit override; do not silently substitute.
|
||||
Never present this as a model stall or a PASS — it is a fail-closed gate result.
|
||||
- **`VERDICT: unavailable`:** the shared validator found the run did not execute (for
|
||||
example `Codex's sandbox could not start here`). Relay its line and fix verbatim; it is
|
||||
|
||||
@@ -39,6 +39,8 @@ If the user passed `--xhigh`, use `"xhigh"` instead of `"high"`.
|
||||
|
||||
```bash
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
PROMPT_FILE="$(git rev-parse --show-toplevel 2>/dev/null || pwd)/.gstack/tmp/<prompt-file-name>"
|
||||
[ -s "$PROMPT_FILE" ] || { echo "Not run: $PROMPT_FILE is missing or empty, so the prompt was never written. Write it, then run by hand: codex exec - -C $_REPO_ROOT < $PROMPT_FILE" >&2; exit 1; }
|
||||
PYTHON_CMD=$(command -v python3 2>/dev/null || command -v python 2>/dev/null || true)
|
||||
if [ -z "$PYTHON_CMD" ]; then
|
||||
echo "ERROR: Python 3 is required to parse Codex JSON output. Install python3 or python and retry." >&2
|
||||
@@ -49,11 +51,11 @@ fi
|
||||
[ -n "${TMPERR:-}" ] || TMPERR=$(mktemp "$TMP_ROOT/codex-err-XXXXXX") || { echo "ERROR: mktemp failed in TMP_ROOT=$TMP_ROOT; not running codex without its temp file" >&2; exit 1; }
|
||||
TMPRESP=$(mktemp "$TMP_ROOT/codex-resp-XXXXXX") || { echo "ERROR: mktemp failed in TMP_ROOT=$TMP_ROOT; not running codex without its temp file" >&2; exit 1; }
|
||||
_CODEX_PROBE=~/.claude/skills/gstack/bin/gstack-codex-probe
|
||||
_CODEX_OUT=$("$_CODEX_PROBE" select-model exec) || exit 1
|
||||
_CODEX_ROLE=''
|
||||
if [ -n "$_CODEX_ROLE" ]; then export _CODEX_DEADLINE=$(($(date +%s)+540)); _CODEX_OUT=$("$_CODEX_PROBE" role-ready exec) || exit $?
|
||||
else _CODEX_OUT=$("$_CODEX_PROBE" select-model exec) || exit 1; fi
|
||||
_CODEX_SEL=$(echo "$_CODEX_OUT" | sed -n 's/^CODEX_SEL: //p')
|
||||
_CODEX_SANDBOX_MODE=$(echo "$_CODEX_OUT" | sed -n 's/^CODEX_SANDBOX: //p')
|
||||
PROMPT_FILE="$(git rev-parse --show-toplevel 2>/dev/null || pwd)/.gstack/tmp/<prompt-file-name>"
|
||||
[ -s "$PROMPT_FILE" ] || { echo "Not run: $PROMPT_FILE is missing or empty, so the prompt was never written. Write it, then run by hand: codex exec - -C $_REPO_ROOT < $PROMPT_FILE" >&2; exit 1; }
|
||||
"$_CODEX_PROBE" run-with-timeout 540 codex exec - -C "$_REPO_ROOT" -s "${_CODEX_SANDBOX_MODE:?}" -c "model=\"${_CODEX_SEL:?}\"" -c skills.include_instructions=false -c 'model_reasoning_effort="high"' -c 'web_search="cached"' --json -o "$TMPRESP" < "$PROMPT_FILE" 2>"$TMPERR" | tee "$TMPRESP.events" | PYTHONUNBUFFERED=1 "$PYTHON_CMD" -u -c "
|
||||
import sys, json
|
||||
turn_completed_count = 0
|
||||
|
||||
@@ -30,6 +30,8 @@ If the user passed `--xhigh`, use `"xhigh"` instead of `"high"`.
|
||||
|
||||
```bash
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
PROMPT_FILE="$(git rev-parse --show-toplevel 2>/dev/null || pwd)/.gstack/tmp/<prompt-file-name>"
|
||||
[ -s "$PROMPT_FILE" ] || { echo "Not run: $PROMPT_FILE is missing or empty, so the prompt was never written. Write it, then run by hand: codex exec - -C $_REPO_ROOT < $PROMPT_FILE" >&2; exit 1; }
|
||||
PYTHON_CMD=$(command -v python3 2>/dev/null || command -v python 2>/dev/null || true)
|
||||
if [ -z "$PYTHON_CMD" ]; then
|
||||
echo "ERROR: Python 3 is required to parse Codex JSON output. Install python3 or python and retry." >&2
|
||||
@@ -39,9 +41,7 @@ fi
|
||||
# capture stderr to $TMPERR for auth error detection (was: 2>/dev/null).
|
||||
[ -n "${TMPERR:-}" ] || TMPERR=$(mktemp "$TMP_ROOT/codex-err-XXXXXX") || { echo "ERROR: mktemp failed in TMP_ROOT=$TMP_ROOT; not running codex without its temp file" >&2; exit 1; }
|
||||
TMPRESP=$(mktemp "$TMP_ROOT/codex-resp-XXXXXX") || { echo "ERROR: mktemp failed in TMP_ROOT=$TMP_ROOT; not running codex without its temp file" >&2; exit 1; }
|
||||
{{CODEX_SELECT:exec}}
|
||||
PROMPT_FILE="$(git rev-parse --show-toplevel 2>/dev/null || pwd)/.gstack/tmp/<prompt-file-name>"
|
||||
[ -s "$PROMPT_FILE" ] || { echo "Not run: $PROMPT_FILE is missing or empty, so the prompt was never written. Write it, then run by hand: codex exec - -C $_REPO_ROOT < $PROMPT_FILE" >&2; exit 1; }
|
||||
{{CODEX_SELECT:exec:540}}
|
||||
"$_CODEX_PROBE" run-with-timeout 540 codex exec - -C "$_REPO_ROOT" -s "${_CODEX_SANDBOX_MODE:?}" {{CODEX_MODEL_CONFIG_FLAG}} -c 'model_reasoning_effort="high"' {{CODEX_WEB_SEARCH_FLAG}} --json -o "$TMPRESP" < "$PROMPT_FILE" 2>"$TMPERR" | tee "$TMPRESP.events" | PYTHONUNBUFFERED=1 "$PYTHON_CMD" -u -c "
|
||||
import sys, json
|
||||
turn_completed_count = 0
|
||||
|
||||
@@ -102,7 +102,9 @@ cd "$_REPO_ROOT" || exit 1
|
||||
PROMPT_FILE="$_REPO_ROOT/.gstack/tmp/<prompt-file-name>"
|
||||
[ -s "$PROMPT_FILE" ] || { echo "Not run: $PROMPT_FILE is missing or empty, so the prompt was never written. Write it, then run by hand: codex exec - -C $_REPO_ROOT < $PROMPT_FILE" >&2; exit 1; }
|
||||
_CODEX_PROBE=~/.claude/skills/gstack/bin/gstack-codex-probe
|
||||
_CODEX_OUT=$("$_CODEX_PROBE" select-model exec) || exit 1
|
||||
_CODEX_ROLE=''
|
||||
if [ -n "$_CODEX_ROLE" ]; then export _CODEX_DEADLINE=$(($(date +%s)+540)); _CODEX_OUT=$("$_CODEX_PROBE" role-ready exec) || exit $?
|
||||
else _CODEX_OUT=$("$_CODEX_PROBE" select-model exec) || exit 1; fi
|
||||
_CODEX_SEL=$(echo "$_CODEX_OUT" | sed -n 's/^CODEX_SEL: //p')
|
||||
_CODEX_SANDBOX_MODE=$(echo "$_CODEX_OUT" | sed -n 's/^CODEX_SANDBOX: //p')
|
||||
_SID=""
|
||||
|
||||
@@ -92,7 +92,7 @@ fi
|
||||
cd "$_REPO_ROOT" || exit 1
|
||||
PROMPT_FILE="$_REPO_ROOT/.gstack/tmp/<prompt-file-name>"
|
||||
[ -s "$PROMPT_FILE" ] || { echo "Not run: $PROMPT_FILE is missing or empty, so the prompt was never written. Write it, then run by hand: codex exec - -C $_REPO_ROOT < $PROMPT_FILE" >&2; exit 1; }
|
||||
{{CODEX_SELECT:exec}}
|
||||
{{CODEX_SELECT:exec:540}}
|
||||
_SID=""
|
||||
if [ "$_CODEX_MODE" = "resume" ]; then
|
||||
_SID=$(cat .context/codex-session-id 2>/dev/null)
|
||||
|
||||
@@ -48,7 +48,9 @@ _REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo"
|
||||
cd "$_REPO_ROOT"
|
||||
~/.claude/skills/gstack/bin/gstack-review-log --start codex-review
|
||||
_CODEX_PROBE=~/.claude/skills/gstack/bin/gstack-codex-probe
|
||||
_CODEX_OUT=$("$_CODEX_PROBE" select-model review) || exit 1
|
||||
_CODEX_ROLE=''
|
||||
if [ -n "$_CODEX_ROLE" ]; then export _CODEX_DEADLINE=$(($(date +%s)+330)); _CODEX_OUT=$("$_CODEX_PROBE" role-ready review) || exit $?
|
||||
else _CODEX_OUT=$("$_CODEX_PROBE" select-model review) || exit 1; fi
|
||||
_CODEX_SEL=$(echo "$_CODEX_OUT" | sed -n 's/^CODEX_SEL: //p')
|
||||
_CODEX_SANDBOX_MODE=$(echo "$_CODEX_OUT" | sed -n 's/^CODEX_SANDBOX: //p')
|
||||
# The 330s wrapper sits BELOW the 360s Bash gate so the wrapper fires FIRST
|
||||
@@ -106,7 +108,9 @@ FOCUS_FILE="$_REPO_ROOT/.gstack/tmp/<focus-file-name>"
|
||||
[ -s "$FOCUS_FILE" ] || { echo "Not run: $FOCUS_FILE is missing or empty, so the focus text was never written. Write it, then run this block again." >&2; exit 1; }
|
||||
~/.claude/skills/gstack/bin/gstack-review-log --start codex-review
|
||||
_CODEX_PROBE=~/.claude/skills/gstack/bin/gstack-codex-probe
|
||||
_CODEX_OUT=$("$_CODEX_PROBE" select-model exec) || exit 1
|
||||
_CODEX_ROLE=''
|
||||
if [ -n "$_CODEX_ROLE" ]; then export _CODEX_DEADLINE=$(($(date +%s)+330)); _CODEX_OUT=$("$_CODEX_PROBE" role-ready exec) || exit $?
|
||||
else _CODEX_OUT=$("$_CODEX_PROBE" select-model exec) || exit 1; fi
|
||||
_CODEX_SEL=$(echo "$_CODEX_OUT" | sed -n 's/^CODEX_SEL: //p')
|
||||
_CODEX_SANDBOX_MODE=$(echo "$_CODEX_OUT" | sed -n 's/^CODEX_SANDBOX: //p')
|
||||
_PROMPT_FILE=$(mktemp "$TMP_ROOT/codex-prompt-XXXXXX") || { echo "ERROR: mktemp failed in TMP_ROOT=$TMP_ROOT; not running codex without its temp file" >&2; exit 1; }
|
||||
|
||||
@@ -45,7 +45,7 @@ contradicting this skill's read-only contract:
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
cd "$_REPO_ROOT"
|
||||
~/.claude/skills/gstack/bin/gstack-review-log --start codex-review
|
||||
{{CODEX_SELECT:review}}
|
||||
{{CODEX_SELECT:review:330}}
|
||||
# The 330s wrapper sits BELOW the 360s Bash gate so the wrapper fires FIRST
|
||||
# and a stall surfaces as a diagnosable exit 124 with an explicit message,
|
||||
# never as a silent harness kill that downstream reads as "no findings".
|
||||
@@ -93,7 +93,7 @@ cd "$_REPO_ROOT"
|
||||
FOCUS_FILE="$_REPO_ROOT/.gstack/tmp/<focus-file-name>"
|
||||
[ -s "$FOCUS_FILE" ] || { echo "Not run: $FOCUS_FILE is missing or empty, so the focus text was never written. Write it, then run this block again." >&2; exit 1; }
|
||||
~/.claude/skills/gstack/bin/gstack-review-log --start codex-review
|
||||
{{CODEX_SELECT:exec}}
|
||||
{{CODEX_SELECT:exec:330}}
|
||||
_PROMPT_FILE=$(mktemp "$TMP_ROOT/codex-prompt-XXXXXX") || { echo "ERROR: mktemp failed in TMP_ROOT=$TMP_ROOT; not running codex without its temp file" >&2; exit 1; }
|
||||
{
|
||||
printf '%s\n' "IMPORTANT: Do NOT read or execute any files under ~/.claude/, ~/.agents/, .claude/skills/, or agents/. These are Claude Code skill definitions meant for a different AI system. Do not invoke any installed skill (Codex home skills/, .agents/); answer directly. Do NOT modify agents/openai.yaml. Stay focused on repository code only."
|
||||
|
||||
Reference in new issue
Block a user