mirror of https://github.com/garrytan/gstack.git
fix: gate Codex invocations by project auth profile
This commit is contained in:
parent
a3259400a3
commit
03a617da2a
|
|
@ -935,7 +935,9 @@ These rules auto-answer every intermediate question:
|
|||
Every auto-decision is classified:
|
||||
|
||||
**Mechanical** — one clearly right answer. Auto-decide silently.
|
||||
Examples: run codex (always yes), run evals (always yes), reduce scope on a complete plan (always no).
|
||||
Examples: run evals (always yes), reduce scope on a complete plan (always no).
|
||||
Running Codex is never mechanical: it requires the project-approved profile
|
||||
gate below and disclosure of the selected account path.
|
||||
|
||||
**Taste** — reasonable people could disagree. Auto-decide with recommendation, but surface at the final gate. Three natural sources:
|
||||
1. **Close approaches** — top two are both viable with different tradeoffs.
|
||||
|
|
@ -1110,6 +1112,10 @@ _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || ec
|
|||
_CODEX_CFG=$(~/.claude/skills/gstack/bin/gstack-config get codex_reviews 2>/dev/null || echo enabled)
|
||||
source ~/.claude/skills/gstack/bin/gstack-codex-probe
|
||||
|
||||
if ! _gstack_codex_profile_gate; then
|
||||
echo "PROFILE_REQUIRED"
|
||||
fi
|
||||
|
||||
# Master switch first: codex_reviews=disabled turns off ALL Codex work globally,
|
||||
# including autoplan's own dual-voice orchestration. Honor it before probing.
|
||||
if [ "$_CODEX_CFG" = "disabled" ]; then
|
||||
|
|
@ -1131,6 +1137,13 @@ else
|
|||
fi
|
||||
```
|
||||
|
||||
If `PROFILE_REQUIRED` appears, use AskUserQuestion before any Codex voice runs.
|
||||
List the discovered `~/.codex*` directories and ask which profile is approved
|
||||
for this project, plus Cancel. Save the selected absolute path as
|
||||
`codex_home: <path>` in `~/.gstack/projects/$SLUG/codex.yaml`, rerun the
|
||||
preflight, and disclose the selected path. Cancel means Claude-only voices; do
|
||||
not invoke Codex. `GSTACK_CODEX_SKIP_GATE=1` is reserved for isolated evals.
|
||||
|
||||
If `_CODEX_AVAILABLE=false`, all Phase 1-3.5 Codex voices below degrade to
|
||||
`[codex-unavailable]` in the degradation matrix. /autoplan completes with
|
||||
Claude subagent only — saves token spend on Codex prompts we can't use.
|
||||
|
|
|
|||
|
|
@ -71,7 +71,9 @@ These rules auto-answer every intermediate question:
|
|||
Every auto-decision is classified:
|
||||
|
||||
**Mechanical** — one clearly right answer. Auto-decide silently.
|
||||
Examples: run codex (always yes), run evals (always yes), reduce scope on a complete plan (always no).
|
||||
Examples: run evals (always yes), reduce scope on a complete plan (always no).
|
||||
Running Codex is never mechanical: it requires the project-approved profile
|
||||
gate below and disclosure of the selected account path.
|
||||
|
||||
**Taste** — reasonable people could disagree. Auto-decide with recommendation, but surface at the final gate. Three natural sources:
|
||||
1. **Close approaches** — top two are both viable with different tradeoffs.
|
||||
|
|
@ -246,6 +248,10 @@ _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || ec
|
|||
_CODEX_CFG=$(~/.claude/skills/gstack/bin/gstack-config get codex_reviews 2>/dev/null || echo enabled)
|
||||
source ~/.claude/skills/gstack/bin/gstack-codex-probe
|
||||
|
||||
if ! _gstack_codex_profile_gate; then
|
||||
echo "PROFILE_REQUIRED"
|
||||
fi
|
||||
|
||||
# Master switch first: codex_reviews=disabled turns off ALL Codex work globally,
|
||||
# including autoplan's own dual-voice orchestration. Honor it before probing.
|
||||
if [ "$_CODEX_CFG" = "disabled" ]; then
|
||||
|
|
@ -267,6 +273,13 @@ else
|
|||
fi
|
||||
```
|
||||
|
||||
If `PROFILE_REQUIRED` appears, use AskUserQuestion before any Codex voice runs.
|
||||
List the discovered `~/.codex*` directories and ask which profile is approved
|
||||
for this project, plus Cancel. Save the selected absolute path as
|
||||
`codex_home: <path>` in `~/.gstack/projects/$SLUG/codex.yaml`, rerun the
|
||||
preflight, and disclose the selected path. Cancel means Claude-only voices; do
|
||||
not invoke Codex. `GSTACK_CODEX_SKIP_GATE=1` is reserved for isolated evals.
|
||||
|
||||
If `_CODEX_AVAILABLE=false`, all Phase 1-3.5 Codex voices below degrade to
|
||||
`[codex-unavailable]` in the degradation matrix. /autoplan completes with
|
||||
Claude subagent only — saves token spend on Codex prompts we can't use.
|
||||
|
|
|
|||
|
|
@ -14,6 +14,41 @@
|
|||
# - All functions prefix with _gstack_codex_.
|
||||
# - No command execution at source time (only function defs).
|
||||
|
||||
# --- Per-project profile gate ----------------------------------------------
|
||||
|
||||
_gstack_codex_profile_gate() {
|
||||
[ "${GSTACK_CODEX_SKIP_GATE:-}" = "1" ] && return 0
|
||||
|
||||
local _GSTACK_CODEX_BIN_DIR _GSTACK_CODEX_SLUG_OUTPUT _GSTACK_CODEX_SLUG
|
||||
local _GSTACK_CODEX_STATE_ROOT _GSTACK_CODEX_CONFIG _GSTACK_CODEX_HOME
|
||||
_GSTACK_CODEX_BIN_DIR=$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")" 2>/dev/null && pwd)
|
||||
_GSTACK_CODEX_SLUG_OUTPUT=$("$_GSTACK_CODEX_BIN_DIR/gstack-slug" 2>/dev/null || true)
|
||||
_GSTACK_CODEX_SLUG=$(printf '%s\n' "$_GSTACK_CODEX_SLUG_OUTPUT" | sed -n 's/^SLUG=//p' | head -1)
|
||||
[ -z "$_GSTACK_CODEX_SLUG" ] && _GSTACK_CODEX_SLUG=$(basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)" | tr -cs '[:alnum:]._' '-')
|
||||
_GSTACK_CODEX_STATE_ROOT=${GSTACK_HOME:-$HOME/.gstack}
|
||||
_GSTACK_CODEX_CONFIG="$_GSTACK_CODEX_STATE_ROOT/projects/$_GSTACK_CODEX_SLUG/codex.yaml"
|
||||
|
||||
if [ ! -f "$_GSTACK_CODEX_CONFIG" ]; then
|
||||
echo "CODEX_PROFILE_REQUIRED: approve a Codex home for project $_GSTACK_CODEX_SLUG before Codex runs"
|
||||
find "$HOME" -maxdepth 1 -type d -name '.codex*' -print 2>/dev/null | sort
|
||||
echo "Save the selection as: $_GSTACK_CODEX_CONFIG"
|
||||
return 78
|
||||
fi
|
||||
|
||||
_GSTACK_CODEX_HOME=$(sed -n 's/^[[:space:]]*codex_home:[[:space:]]*//p' "$_GSTACK_CODEX_CONFIG" | head -1)
|
||||
_GSTACK_CODEX_HOME=${_GSTACK_CODEX_HOME#\"}; _GSTACK_CODEX_HOME=${_GSTACK_CODEX_HOME%\"}
|
||||
_GSTACK_CODEX_HOME=${_GSTACK_CODEX_HOME#\'}; _GSTACK_CODEX_HOME=${_GSTACK_CODEX_HOME%\'}
|
||||
case "$_GSTACK_CODEX_HOME" in
|
||||
'~/'*) _GSTACK_CODEX_HOME="$HOME/${_GSTACK_CODEX_HOME#\~/}" ;;
|
||||
esac
|
||||
if [ -z "$_GSTACK_CODEX_HOME" ] || [ ! -d "$_GSTACK_CODEX_HOME" ]; then
|
||||
echo "CODEX_PROFILE_REQUIRED: configured Codex home is missing or invalid: $_GSTACK_CODEX_CONFIG"
|
||||
return 78
|
||||
fi
|
||||
export CODEX_HOME="$_GSTACK_CODEX_HOME"
|
||||
echo "CODEX_PROFILE: $CODEX_HOME"
|
||||
}
|
||||
|
||||
# --- Auth probe -------------------------------------------------------------
|
||||
|
||||
_gstack_codex_auth_probe() {
|
||||
|
|
@ -57,6 +92,9 @@ _gstack_codex_timeout_wrapper() {
|
|||
# duration in seconds; rest is the command to run.
|
||||
local _duration="$1"
|
||||
shift
|
||||
if [ "${1##*/}" = "codex" ]; then
|
||||
_gstack_codex_profile_gate || return $?
|
||||
fi
|
||||
local _to
|
||||
_to=$(command -v gtimeout 2>/dev/null || command -v timeout 2>/dev/null || echo "")
|
||||
if [ -n "$_to" ]; then
|
||||
|
|
|
|||
|
|
@ -876,6 +876,10 @@ shared helpers that both `/codex` and `/autoplan` use.
|
|||
_TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || echo off)
|
||||
source ~/.claude/skills/gstack/bin/gstack-codex-probe
|
||||
|
||||
if ! _gstack_codex_profile_gate; then
|
||||
echo "PROFILE_REQUIRED"
|
||||
fi
|
||||
|
||||
if ! _gstack_codex_auth_probe >/dev/null; then
|
||||
_gstack_codex_log_event "codex_auth_failed"
|
||||
echo "AUTH_FAILED"
|
||||
|
|
@ -883,6 +887,18 @@ fi
|
|||
_gstack_codex_version_check # warns if known-bad, non-blocking
|
||||
```
|
||||
|
||||
If the output contains `PROFILE_REQUIRED`, use AskUserQuestion before doing any
|
||||
Codex work. List the discovered `~/.codex*` directories and ask which Codex
|
||||
profile is approved for this project, plus Cancel. On selection, resolve `SLUG`
|
||||
with `gstack-slug`, create `~/.gstack/projects/$SLUG/`, and write `codex.yaml`
|
||||
containing `codex_home: <selected absolute path>`. Then rerun this step and tell
|
||||
the user `Codex will use <path> for this project.` If they cancel, stop without
|
||||
invoking Codex. Never infer a choice from whichever profile was used most
|
||||
recently.
|
||||
|
||||
`GSTACK_CODEX_SKIP_GATE=1` is only for isolated eval harnesses; normal skill
|
||||
runs must not set it.
|
||||
|
||||
If the output contains `AUTH_FAILED`, stop and tell the user:
|
||||
"No Codex authentication found. Run `codex login` or set `$CODEX_API_KEY` / `$OPENAI_API_KEY`, then re-run this skill."
|
||||
|
||||
|
|
|
|||
|
|
@ -67,6 +67,10 @@ shared helpers that both `/codex` and `/autoplan` use.
|
|||
_TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || echo off)
|
||||
source ~/.claude/skills/gstack/bin/gstack-codex-probe
|
||||
|
||||
if ! _gstack_codex_profile_gate; then
|
||||
echo "PROFILE_REQUIRED"
|
||||
fi
|
||||
|
||||
if ! _gstack_codex_auth_probe >/dev/null; then
|
||||
_gstack_codex_log_event "codex_auth_failed"
|
||||
echo "AUTH_FAILED"
|
||||
|
|
@ -74,6 +78,18 @@ fi
|
|||
_gstack_codex_version_check # warns if known-bad, non-blocking
|
||||
```
|
||||
|
||||
If the output contains `PROFILE_REQUIRED`, use AskUserQuestion before doing any
|
||||
Codex work. List the discovered `~/.codex*` directories and ask which Codex
|
||||
profile is approved for this project, plus Cancel. On selection, resolve `SLUG`
|
||||
with `gstack-slug`, create `~/.gstack/projects/$SLUG/`, and write `codex.yaml`
|
||||
containing `codex_home: <selected absolute path>`. Then rerun this step and tell
|
||||
the user `Codex will use <path> for this project.` If they cancel, stop without
|
||||
invoking Codex. Never infer a choice from whichever profile was used most
|
||||
recently.
|
||||
|
||||
`GSTACK_CODEX_SKIP_GATE=1` is only for isolated eval harnesses; normal skill
|
||||
runs must not set it.
|
||||
|
||||
If the output contains `AUTH_FAILED`, stop and tell the user:
|
||||
"No Codex authentication found. Run `codex login` or set `$CODEX_API_KEY` / `$OPENAI_API_KEY`, then re-run this skill."
|
||||
|
||||
|
|
|
|||
|
|
@ -1144,7 +1144,7 @@ command -v codex >/dev/null 2>&1 && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AV
|
|||
```bash
|
||||
TMPERR_DESIGN=$(mktemp /tmp/codex-design-XXXXXXXX)
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
codex exec "Given this product context, propose a complete design direction:
|
||||
source ~/.claude/skills/gstack/bin/gstack-codex-probe && _gstack_codex_timeout_wrapper 600 codex exec "Given this product context, propose a complete design direction:
|
||||
- Visual thesis: one sentence describing mood, material, and energy
|
||||
- Typography: specific font names (not defaults — no Inter/Roboto/Arial/system) + hex colors
|
||||
- Color system: CSS variables for background, surface, primary text, muted text, accent
|
||||
|
|
|
|||
|
|
@ -1731,7 +1731,7 @@ command -v codex >/dev/null 2>&1 && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AV
|
|||
```bash
|
||||
TMPERR_DESIGN=$(mktemp /tmp/codex-design-XXXXXXXX)
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
codex exec "Review the frontend source code in this repo. Evaluate against these design hard rules:
|
||||
source ~/.claude/skills/gstack/bin/gstack-codex-probe && _gstack_codex_timeout_wrapper 600 codex exec "Review the frontend source code in this repo. Evaluate against these design hard rules:
|
||||
- Spacing: systematic (design tokens / CSS variables) or magic numbers?
|
||||
- Typography: expressive purposeful fonts or default stacks?
|
||||
- Color: CSS variables with defined system, or hardcoded hex scattered?
|
||||
|
|
|
|||
|
|
@ -379,6 +379,8 @@ if [ "$_CODEX_CFG" = "disabled" ]; then
|
|||
_CODEX_MODE="disabled"
|
||||
elif ! command -v codex >/dev/null 2>&1; then
|
||||
_CODEX_MODE="not_installed"; _gstack_codex_log_event "codex_cli_missing" 2>/dev/null || true
|
||||
elif ! _gstack_codex_profile_gate; then
|
||||
_CODEX_MODE="profile_required"
|
||||
elif ! _gstack_codex_auth_probe >/dev/null 2>&1; then
|
||||
_CODEX_MODE="not_authed"; _gstack_codex_log_event "codex_auth_failed" 2>/dev/null || true
|
||||
else
|
||||
|
|
@ -390,6 +392,7 @@ echo "CODEX_MODE: $_CODEX_MODE"
|
|||
Branch on the echoed `CODEX_MODE`:
|
||||
- **`disabled`** — the user turned Codex reviews off (`codex_reviews=disabled`). Skip this section entirely; do NOT fall back to a Claude subagent — disabled means no extra review step. Print: "Codex review skipped (codex_reviews disabled). Re-enable: `gstack-config set codex_reviews enabled`."
|
||||
- **`not_installed`** — Codex CLI absent. Print: "Codex not installed — using Claude subagent. Install for cross-model coverage: `npm install -g @openai/codex`." Fall back to the Claude subagent path.
|
||||
- **`profile_required`** — Before invoking Codex, use AskUserQuestion to list the discovered `~/.codex*` directories and ask which profile is approved for this project, plus Cancel. Save the selected absolute path as `codex_home: <path>` in `~/.gstack/projects/$SLUG/codex.yaml`, then rerun this preflight and disclose the path. Cancel skips Codex. Never silently choose the last-used profile.
|
||||
- **`not_authed`** — installed but no credentials. Print: "Codex installed but not authenticated — using Claude subagent. Run `codex login` or set `$CODEX_API_KEY`." Fall back to the Claude subagent path.
|
||||
- **`ready`** — run the Codex pass below.
|
||||
|
||||
|
|
@ -428,7 +431,7 @@ THE DOCS AND DIFF: <list the touched doc paths>"
|
|||
```bash
|
||||
TMPERR_DOC=$(mktemp /tmp/codex-docreview-XXXXXXXX)
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
codex exec "<prompt>" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR_DOC"
|
||||
source ~/.claude/skills/gstack/bin/gstack-codex-probe && _gstack_codex_timeout_wrapper 600 codex exec "<prompt>" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR_DOC"
|
||||
```
|
||||
|
||||
Use a 5-minute timeout (`timeout: 300000`). After the command completes, read stderr:
|
||||
|
|
|
|||
|
|
@ -1345,7 +1345,7 @@ Then add the context block and mode-appropriate instructions:
|
|||
```bash
|
||||
TMPERR_OH=$(mktemp /tmp/codex-oh-err-XXXXXXXX)
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
codex exec "$(cat "$CODEX_PROMPT_FILE")" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR_OH"
|
||||
source ~/.claude/skills/gstack/bin/gstack-codex-probe && _gstack_codex_timeout_wrapper 600 codex exec "$(cat "$CODEX_PROMPT_FILE")" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR_OH"
|
||||
```
|
||||
|
||||
Use a 5-minute timeout (`timeout: 300000`). After the command completes, read stderr:
|
||||
|
|
@ -1595,7 +1595,7 @@ If user chooses A, launch both voices simultaneously:
|
|||
```bash
|
||||
TMPERR_SKETCH=$(mktemp /tmp/codex-sketch-XXXXXXXX)
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
codex exec "For this product approach, provide: a visual thesis (one sentence — mood, material, energy), a content plan (hero → support → detail → CTA), and 2 interaction ideas that change page feel. Apply beautiful defaults: composition-first, brand-first, cardless, poster not document. Be opinionated." -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="medium"' --enable web_search_cached < /dev/null 2>"$TMPERR_SKETCH"
|
||||
source ~/.claude/skills/gstack/bin/gstack-codex-probe && _gstack_codex_timeout_wrapper 600 codex exec "For this product approach, provide: a visual thesis (one sentence — mood, material, energy), a content plan (hero → support → detail → CTA), and 2 interaction ideas that change page feel. Apply beautiful defaults: composition-first, brand-first, cardless, poster not document. Be opinionated." -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="medium"' --enable web_search_cached < /dev/null 2>"$TMPERR_SKETCH"
|
||||
```
|
||||
Use a 5-minute timeout (`timeout: 300000`). After completion: `cat "$TMPERR_SKETCH" && rm -f "$TMPERR_SKETCH"`
|
||||
|
||||
|
|
|
|||
|
|
@ -272,6 +272,8 @@ if [ "$_CODEX_CFG" = "disabled" ]; then
|
|||
_CODEX_MODE="disabled"
|
||||
elif ! command -v codex >/dev/null 2>&1; then
|
||||
_CODEX_MODE="not_installed"; _gstack_codex_log_event "codex_cli_missing" 2>/dev/null || true
|
||||
elif ! _gstack_codex_profile_gate; then
|
||||
_CODEX_MODE="profile_required"
|
||||
elif ! _gstack_codex_auth_probe >/dev/null 2>&1; then
|
||||
_CODEX_MODE="not_authed"; _gstack_codex_log_event "codex_auth_failed" 2>/dev/null || true
|
||||
else
|
||||
|
|
@ -283,6 +285,7 @@ echo "CODEX_MODE: $_CODEX_MODE"
|
|||
Branch on the echoed `CODEX_MODE`:
|
||||
- **`disabled`** — the user turned Codex reviews off (`codex_reviews=disabled`). Skip this section entirely; do NOT fall back to a Claude subagent — disabled means no extra review step. Print: "Codex review skipped (codex_reviews disabled). Re-enable: `gstack-config set codex_reviews enabled`."
|
||||
- **`not_installed`** — Codex CLI absent. Print: "Codex not installed — using Claude subagent. Install for cross-model coverage: `npm install -g @openai/codex`." Fall back to the Claude subagent path.
|
||||
- **`profile_required`** — Before invoking Codex, use AskUserQuestion to list the discovered `~/.codex*` directories and ask which profile is approved for this project, plus Cancel. Save the selected absolute path as `codex_home: <path>` in `~/.gstack/projects/$SLUG/codex.yaml`, then rerun this preflight and disclose the path. Cancel skips Codex. Never silently choose the last-used profile.
|
||||
- **`not_authed`** — installed but no credentials. Print: "Codex installed but not authenticated — using Claude subagent. Run `codex login` or set `$CODEX_API_KEY`." Fall back to the Claude subagent path.
|
||||
- **`ready`** — run the Codex pass below.
|
||||
|
||||
|
|
@ -315,7 +318,7 @@ THE PLAN:
|
|||
```bash
|
||||
TMPERR_PV=$(mktemp /tmp/codex-planreview-XXXXXXXX)
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
codex exec "<prompt>" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR_PV"
|
||||
source ~/.claude/skills/gstack/bin/gstack-codex-probe && _gstack_codex_timeout_wrapper 600 codex exec "<prompt>" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR_PV"
|
||||
```
|
||||
|
||||
Use a 5-minute timeout (`timeout: 300000`). After the command completes, read stderr:
|
||||
|
|
|
|||
|
|
@ -1360,7 +1360,7 @@ command -v codex >/dev/null 2>&1 && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AV
|
|||
```bash
|
||||
TMPERR_DESIGN=$(mktemp /tmp/codex-design-XXXXXXXX)
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
codex exec "Read the plan file at [plan-file-path]. Evaluate this plan's UI/UX design against these criteria.
|
||||
source ~/.claude/skills/gstack/bin/gstack-codex-probe && _gstack_codex_timeout_wrapper 600 codex exec "Read the plan file at [plan-file-path]. Evaluate this plan's UI/UX design against these criteria.
|
||||
|
||||
HARD REJECTION — flag if ANY apply:
|
||||
1. Generic SaaS card grid as first impression
|
||||
|
|
|
|||
|
|
@ -258,6 +258,8 @@ if [ "$_CODEX_CFG" = "disabled" ]; then
|
|||
_CODEX_MODE="disabled"
|
||||
elif ! command -v codex >/dev/null 2>&1; then
|
||||
_CODEX_MODE="not_installed"; _gstack_codex_log_event "codex_cli_missing" 2>/dev/null || true
|
||||
elif ! _gstack_codex_profile_gate; then
|
||||
_CODEX_MODE="profile_required"
|
||||
elif ! _gstack_codex_auth_probe >/dev/null 2>&1; then
|
||||
_CODEX_MODE="not_authed"; _gstack_codex_log_event "codex_auth_failed" 2>/dev/null || true
|
||||
else
|
||||
|
|
@ -269,6 +271,7 @@ echo "CODEX_MODE: $_CODEX_MODE"
|
|||
Branch on the echoed `CODEX_MODE`:
|
||||
- **`disabled`** — the user turned Codex reviews off (`codex_reviews=disabled`). Skip this section entirely; do NOT fall back to a Claude subagent — disabled means no extra review step. Print: "Codex review skipped (codex_reviews disabled). Re-enable: `gstack-config set codex_reviews enabled`."
|
||||
- **`not_installed`** — Codex CLI absent. Print: "Codex not installed — using Claude subagent. Install for cross-model coverage: `npm install -g @openai/codex`." Fall back to the Claude subagent path.
|
||||
- **`profile_required`** — Before invoking Codex, use AskUserQuestion to list the discovered `~/.codex*` directories and ask which profile is approved for this project, plus Cancel. Save the selected absolute path as `codex_home: <path>` in `~/.gstack/projects/$SLUG/codex.yaml`, then rerun this preflight and disclose the path. Cancel skips Codex. Never silently choose the last-used profile.
|
||||
- **`not_authed`** — installed but no credentials. Print: "Codex installed but not authenticated — using Claude subagent. Run `codex login` or set `$CODEX_API_KEY`." Fall back to the Claude subagent path.
|
||||
- **`ready`** — run the Codex pass below.
|
||||
|
||||
|
|
@ -301,7 +304,7 @@ THE PLAN:
|
|||
```bash
|
||||
TMPERR_PV=$(mktemp /tmp/codex-planreview-XXXXXXXX)
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
codex exec "<prompt>" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR_PV"
|
||||
source ~/.claude/skills/gstack/bin/gstack-codex-probe && _gstack_codex_timeout_wrapper 600 codex exec "<prompt>" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR_PV"
|
||||
```
|
||||
|
||||
Use a 5-minute timeout (`timeout: 300000`). After the command completes, read stderr:
|
||||
|
|
|
|||
|
|
@ -348,6 +348,8 @@ if [ "$_CODEX_CFG" = "disabled" ]; then
|
|||
_CODEX_MODE="disabled"
|
||||
elif ! command -v codex >/dev/null 2>&1; then
|
||||
_CODEX_MODE="not_installed"; _gstack_codex_log_event "codex_cli_missing" 2>/dev/null || true
|
||||
elif ! _gstack_codex_profile_gate; then
|
||||
_CODEX_MODE="profile_required"
|
||||
elif ! _gstack_codex_auth_probe >/dev/null 2>&1; then
|
||||
_CODEX_MODE="not_authed"; _gstack_codex_log_event "codex_auth_failed" 2>/dev/null || true
|
||||
else
|
||||
|
|
@ -359,6 +361,7 @@ echo "CODEX_MODE: $_CODEX_MODE"
|
|||
Branch on the echoed `CODEX_MODE`:
|
||||
- **`disabled`** — the user turned Codex reviews off (`codex_reviews=disabled`). Skip this section entirely; do NOT fall back to a Claude subagent — disabled means no extra review step. Print: "Codex review skipped (codex_reviews disabled). Re-enable: `gstack-config set codex_reviews enabled`."
|
||||
- **`not_installed`** — Codex CLI absent. Print: "Codex not installed — using Claude subagent. Install for cross-model coverage: `npm install -g @openai/codex`." Fall back to the Claude subagent path.
|
||||
- **`profile_required`** — Before invoking Codex, use AskUserQuestion to list the discovered `~/.codex*` directories and ask which profile is approved for this project, plus Cancel. Save the selected absolute path as `codex_home: <path>` in `~/.gstack/projects/$SLUG/codex.yaml`, then rerun this preflight and disclose the path. Cancel skips Codex. Never silently choose the last-used profile.
|
||||
- **`not_authed`** — installed but no credentials. Print: "Codex installed but not authenticated — using Claude subagent. Run `codex login` or set `$CODEX_API_KEY`." Fall back to the Claude subagent path.
|
||||
- **`ready`** — run the Codex pass below.
|
||||
|
||||
|
|
@ -391,7 +394,7 @@ THE PLAN:
|
|||
```bash
|
||||
TMPERR_PV=$(mktemp /tmp/codex-planreview-XXXXXXXX)
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
codex exec "<prompt>" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR_PV"
|
||||
source ~/.claude/skills/gstack/bin/gstack-codex-probe && _gstack_codex_timeout_wrapper 600 codex exec "<prompt>" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR_PV"
|
||||
```
|
||||
|
||||
Use a 5-minute timeout (`timeout: 300000`). After the command completes, read stderr:
|
||||
|
|
|
|||
|
|
@ -1665,6 +1665,8 @@ if [ "$_CODEX_CFG" = "disabled" ]; then
|
|||
_CODEX_MODE="disabled"
|
||||
elif ! command -v codex >/dev/null 2>&1; then
|
||||
_CODEX_MODE="not_installed"; _gstack_codex_log_event "codex_cli_missing" 2>/dev/null || true
|
||||
elif ! _gstack_codex_profile_gate; then
|
||||
_CODEX_MODE="profile_required"
|
||||
elif ! _gstack_codex_auth_probe >/dev/null 2>&1; then
|
||||
_CODEX_MODE="not_authed"; _gstack_codex_log_event "codex_auth_failed" 2>/dev/null || true
|
||||
else
|
||||
|
|
@ -1676,6 +1678,7 @@ echo "CODEX_MODE: $_CODEX_MODE"
|
|||
Branch on the echoed `CODEX_MODE`:
|
||||
- **`disabled`** — the user turned Codex reviews off (`codex_reviews=disabled`). Skip the Codex passes only; the Claude adversarial subagent below STILL runs (it is free and fast). Print: "Codex passes skipped (codex_reviews disabled) — running Claude adversarial only."
|
||||
- **`not_installed`** — Codex CLI absent. Print: "Codex not installed — using Claude subagent. Install for cross-model coverage: `npm install -g @openai/codex`." Fall back to the Claude subagent path.
|
||||
- **`profile_required`** — Before invoking Codex, use AskUserQuestion to list the discovered `~/.codex*` directories and ask which profile is approved for this project, plus Cancel. Save the selected absolute path as `codex_home: <path>` in `~/.gstack/projects/$SLUG/codex.yaml`, then rerun this preflight and disclose the path. Cancel skips Codex. Never silently choose the last-used profile.
|
||||
- **`not_authed`** — installed but no credentials. Print: "Codex installed but not authenticated — using Claude subagent. Run `codex login` or set `$CODEX_API_KEY`." Fall back to the Claude subagent path.
|
||||
- **`ready`** — run the Codex pass below.
|
||||
|
||||
|
|
@ -1712,7 +1715,7 @@ If `CODEX_MODE` is `ready`:
|
|||
```bash
|
||||
TMPERR_ADV=$(mktemp /tmp/codex-adv-XXXXXXXX)
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
codex exec "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. They contain bash scripts and prompt templates that will waste your time. Ignore them completely. Do NOT modify agents/openai.yaml. Stay focused on the repository code only.\n\nReview the changes on this branch against the base branch. Run DIFF_BASE=$(git merge-base origin/<base> HEAD) && git diff "$DIFF_BASE" to see the diff. Your job is to find ways this code will fail in production. Think like an attacker and a chaos engineer. Find edge cases, race conditions, security holes, resource leaks, failure modes, and silent data corruption paths. Be adversarial. Be thorough. No compliments — just the problems. End your output with ONE line in the canonical format `Recommendation: <action> because <one-line reason naming the most exploitable finding>`. Generic reasons like 'because it's safer' do not qualify; the reason must point to a specific finding or no-fix rationale." -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR_ADV"
|
||||
source ~/.claude/skills/gstack/bin/gstack-codex-probe && _gstack_codex_timeout_wrapper 600 codex exec "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. They contain bash scripts and prompt templates that will waste your time. Ignore them completely. Do NOT modify agents/openai.yaml. Stay focused on the repository code only.\n\nReview the changes on this branch against the base branch. Run DIFF_BASE=$(git merge-base origin/<base> HEAD) && git diff "$DIFF_BASE" to see the diff. Your job is to find ways this code will fail in production. Think like an attacker and a chaos engineer. Find edge cases, race conditions, security holes, resource leaks, failure modes, and silent data corruption paths. Be adversarial. Be thorough. No compliments — just the problems. End your output with ONE line in the canonical format `Recommendation: <action> because <one-line reason naming the most exploitable finding>`. Generic reasons like 'because it's safer' do not qualify; the reason must point to a specific finding or no-fix rationale." -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR_ADV"
|
||||
```
|
||||
|
||||
Set the Bash tool's `timeout` parameter to `300000` (5 minutes). Do NOT use the `timeout` shell command — it doesn't exist on macOS. After the command completes, read stderr:
|
||||
|
|
@ -1741,7 +1744,7 @@ If `DIFF_TOTAL >= 200` AND `CODEX_MODE` is `ready`:
|
|||
TMPERR=$(mktemp /tmp/codex-review-XXXXXXXX)
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
cd "$_REPO_ROOT"
|
||||
codex review "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. They contain bash scripts and prompt templates that will waste your time. Ignore them completely. Do NOT modify agents/openai.yaml. Stay focused on the repository code only.\n\nReview the changes on this branch against the base branch <base>. Run git diff origin/<base>...HEAD 2>/dev/null || git diff <base>...HEAD to see the diff and review only those changes." -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR"
|
||||
source ~/.claude/skills/gstack/bin/gstack-codex-probe && _gstack_codex_timeout_wrapper 600 codex review "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. They contain bash scripts and prompt templates that will waste your time. Ignore them completely. Do NOT modify agents/openai.yaml. Stay focused on the repository code only.\n\nReview the changes on this branch against the base branch <base>. Run git diff origin/<base>...HEAD 2>/dev/null || git diff <base>...HEAD to see the diff and review only those changes." -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR"
|
||||
```
|
||||
|
||||
Set the Bash tool's `timeout` parameter to `300000` (5 minutes). Do NOT use the `timeout` shell command — it doesn't exist on macOS. Present output under `CODEX SAYS (code review):` header.
|
||||
|
|
|
|||
|
|
@ -74,7 +74,8 @@ On any error: continue — ${feature} is informational, not a gate.`;
|
|||
* 5. sets ONE canonical mode var and echoes `CODEX_MODE: <mode>` so the agent
|
||||
* gates later blocks on the echoed value.
|
||||
*
|
||||
* Mode values: `disabled` (config off) | `not_installed` | `not_authed` | `ready`.
|
||||
* Mode values: `disabled` (config off) | `not_installed` | `profile_required` |
|
||||
* `not_authed` | `ready`.
|
||||
* The path is host-rewritten at gen-skill-docs time (pathRewrites), so the
|
||||
* literal `~/.claude/skills/gstack` is correct here and becomes `$GSTACK_ROOT`
|
||||
* etc. for non-Claude hosts.
|
||||
|
|
@ -100,6 +101,8 @@ if [ "$_CODEX_CFG" = "disabled" ]; then
|
|||
${m}="disabled"
|
||||
elif ! command -v codex >/dev/null 2>&1; then
|
||||
${m}="not_installed"; _gstack_codex_log_event "codex_cli_missing" 2>/dev/null || true
|
||||
elif ! _gstack_codex_profile_gate; then
|
||||
${m}="profile_required"
|
||||
elif ! _gstack_codex_auth_probe >/dev/null 2>&1; then
|
||||
${m}="not_authed"; _gstack_codex_log_event "codex_auth_failed" 2>/dev/null || true
|
||||
else
|
||||
|
|
@ -111,6 +114,7 @@ echo "CODEX_MODE: $${m}"
|
|||
Branch on the echoed \`CODEX_MODE\`:
|
||||
- **\`disabled\`** — the user turned Codex reviews off (\`codex_reviews=disabled\`). ${disabledLine}
|
||||
- **\`not_installed\`** — Codex CLI absent. Print: "Codex not installed — using Claude subagent. Install for cross-model coverage: \`npm install -g @openai/codex\`." Fall back to the Claude subagent path.
|
||||
- **\`profile_required\`** — Before invoking Codex, use AskUserQuestion to list the discovered \`~/.codex*\` directories and ask which profile is approved for this project, plus Cancel. Save the selected absolute path as \`codex_home: <path>\` in \`~/.gstack/projects/$SLUG/codex.yaml\`, then rerun this preflight and disclose the path. Cancel skips Codex. Never silently choose the last-used profile.
|
||||
- **\`not_authed\`** — installed but no credentials. Print: "Codex installed but not authenticated — using Claude subagent. Run \`codex login\` or set \`$CODEX_API_KEY\`." Fall back to the Claude subagent path.
|
||||
- **\`ready\`** — run the Codex pass below.`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ If Codex is available, run a lightweight design check on the diff:
|
|||
\`\`\`bash
|
||||
TMPERR_DRL=$(mktemp /tmp/codex-drl-XXXXXXXX)
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
codex exec "Review the git diff on this branch. Run 7 litmus checks (YES/NO each): ${litmusList} Flag any hard rejections: ${rejectionList} 5 most important design findings only. Reference file:line." -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR_DRL"
|
||||
source ~/.claude/skills/gstack/bin/gstack-codex-probe && _gstack_codex_timeout_wrapper 600 codex exec "Review the git diff on this branch. Run 7 litmus checks (YES/NO each): ${litmusList} Flag any hard rejections: ${rejectionList} 5 most important design findings only. Reference file:line." -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR_DRL"
|
||||
\`\`\`
|
||||
|
||||
Use a 5-minute timeout (\`timeout: 300000\`). After the command completes, read stderr:
|
||||
|
|
@ -527,7 +527,7 @@ If user chooses A, launch both voices simultaneously:
|
|||
\`\`\`bash
|
||||
TMPERR_SKETCH=$(mktemp /tmp/codex-sketch-XXXXXXXX)
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
codex exec "For this product approach, provide: a visual thesis (one sentence — mood, material, energy), a content plan (hero → support → detail → CTA), and 2 interaction ideas that change page feel. Apply beautiful defaults: composition-first, brand-first, cardless, poster not document. Be opinionated." -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="medium"' --enable web_search_cached < /dev/null 2>"$TMPERR_SKETCH"
|
||||
source ~/.claude/skills/gstack/bin/gstack-codex-probe && _gstack_codex_timeout_wrapper 600 codex exec "For this product approach, provide: a visual thesis (one sentence — mood, material, energy), a content plan (hero → support → detail → CTA), and 2 interaction ideas that change page feel. Apply beautiful defaults: composition-first, brand-first, cardless, poster not document. Be opinionated." -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="medium"' --enable web_search_cached < /dev/null 2>"$TMPERR_SKETCH"
|
||||
\`\`\`
|
||||
Use a 5-minute timeout (\`timeout: 300000\`). After completion: \`cat "$TMPERR_SKETCH" && rm -f "$TMPERR_SKETCH"\`
|
||||
|
||||
|
|
@ -697,7 +697,7 @@ command -v codex >/dev/null 2>&1 && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AV
|
|||
\`\`\`bash
|
||||
TMPERR_DESIGN=$(mktemp /tmp/codex-design-XXXXXXXX)
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
codex exec "${escapedCodexPrompt}" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="${reasoningEffort}"' --enable web_search_cached < /dev/null 2>"$TMPERR_DESIGN"
|
||||
source ~/.claude/skills/gstack/bin/gstack-codex-probe && _gstack_codex_timeout_wrapper 600 codex exec "${escapedCodexPrompt}" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="${reasoningEffort}"' --enable web_search_cached < /dev/null 2>"$TMPERR_DESIGN"
|
||||
\`\`\`
|
||||
Use a 5-minute timeout (\`timeout: 300000\`). After the command completes, read stderr:
|
||||
\`\`\`bash
|
||||
|
|
|
|||
|
|
@ -367,7 +367,7 @@ Then add the context block and mode-appropriate instructions:
|
|||
\`\`\`bash
|
||||
TMPERR_OH=$(mktemp /tmp/codex-oh-err-XXXXXXXX)
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
codex exec "$(cat "$CODEX_PROMPT_FILE")" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR_OH"
|
||||
source ~/.claude/skills/gstack/bin/gstack-codex-probe && _gstack_codex_timeout_wrapper 600 codex exec "$(cat "$CODEX_PROMPT_FILE")" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR_OH"
|
||||
\`\`\`
|
||||
|
||||
Use a 5-minute timeout (\`timeout: 300000\`). After the command completes, read stderr:
|
||||
|
|
@ -527,7 +527,7 @@ If \`CODEX_MODE\` is \`ready\`:
|
|||
\`\`\`bash
|
||||
TMPERR_ADV=$(mktemp /tmp/codex-adv-XXXXXXXX)
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
codex exec "${CODEX_BOUNDARY}Review the changes on this branch against the base branch. Run DIFF_BASE=$(git merge-base origin/<base> HEAD) && git diff "$DIFF_BASE" to see the diff. Your job is to find ways this code will fail in production. Think like an attacker and a chaos engineer. Find edge cases, race conditions, security holes, resource leaks, failure modes, and silent data corruption paths. Be adversarial. Be thorough. No compliments — just the problems. End your output with ONE line in the canonical format \`Recommendation: <action> because <one-line reason naming the most exploitable finding>\`. Generic reasons like 'because it's safer' do not qualify; the reason must point to a specific finding or no-fix rationale." -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR_ADV"
|
||||
source ~/.claude/skills/gstack/bin/gstack-codex-probe && _gstack_codex_timeout_wrapper 600 codex exec "${CODEX_BOUNDARY}Review the changes on this branch against the base branch. Run DIFF_BASE=$(git merge-base origin/<base> HEAD) && git diff "$DIFF_BASE" to see the diff. Your job is to find ways this code will fail in production. Think like an attacker and a chaos engineer. Find edge cases, race conditions, security holes, resource leaks, failure modes, and silent data corruption paths. Be adversarial. Be thorough. No compliments — just the problems. End your output with ONE line in the canonical format \`Recommendation: <action> because <one-line reason naming the most exploitable finding>\`. Generic reasons like 'because it's safer' do not qualify; the reason must point to a specific finding or no-fix rationale." -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR_ADV"
|
||||
\`\`\`
|
||||
|
||||
Set the Bash tool's \`timeout\` parameter to \`300000\` (5 minutes). Do NOT use the \`timeout\` shell command — it doesn't exist on macOS. After the command completes, read stderr:
|
||||
|
|
@ -556,7 +556,7 @@ If \`DIFF_TOTAL >= 200\` AND \`CODEX_MODE\` is \`ready\`:
|
|||
TMPERR=$(mktemp /tmp/codex-review-XXXXXXXX)
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
cd "$_REPO_ROOT"
|
||||
codex review "${CODEX_BOUNDARY}Review the changes on this branch against the base branch <base>. Run git diff origin/<base>...HEAD 2>/dev/null || git diff <base>...HEAD to see the diff and review only those changes." -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR"
|
||||
source ~/.claude/skills/gstack/bin/gstack-codex-probe && _gstack_codex_timeout_wrapper 600 codex review "${CODEX_BOUNDARY}Review the changes on this branch against the base branch <base>. Run git diff origin/<base>...HEAD 2>/dev/null || git diff <base>...HEAD to see the diff and review only those changes." -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR"
|
||||
\`\`\`
|
||||
|
||||
Set the Bash tool's \`timeout\` parameter to \`300000\` (5 minutes). Do NOT use the \`timeout\` shell command — it doesn't exist on macOS. Present output under \`CODEX SAYS (code review):\` header.
|
||||
|
|
@ -655,7 +655,7 @@ THE PLAN:
|
|||
\`\`\`bash
|
||||
TMPERR_PV=$(mktemp /tmp/codex-planreview-XXXXXXXX)
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
codex exec "<prompt>" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR_PV"
|
||||
source ~/.claude/skills/gstack/bin/gstack-codex-probe && _gstack_codex_timeout_wrapper 600 codex exec "<prompt>" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR_PV"
|
||||
\`\`\`
|
||||
|
||||
Use a 5-minute timeout (\`timeout: 300000\`). After the command completes, read stderr:
|
||||
|
|
@ -790,7 +790,7 @@ THE DOCS AND DIFF: <list the touched doc paths>"
|
|||
\`\`\`bash
|
||||
TMPERR_DOC=$(mktemp /tmp/codex-docreview-XXXXXXXX)
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
codex exec "<prompt>" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR_DOC"
|
||||
source ~/.claude/skills/gstack/bin/gstack-codex-probe && _gstack_codex_timeout_wrapper 600 codex exec "<prompt>" -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR_DOC"
|
||||
\`\`\`
|
||||
|
||||
Use a 5-minute timeout (\`timeout: 300000\`). After the command completes, read stderr:
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ if [ "$_CODEX_CFG" = "disabled" ]; then
|
|||
_CODEX_MODE="disabled"
|
||||
elif ! command -v codex >/dev/null 2>&1; then
|
||||
_CODEX_MODE="not_installed"; _gstack_codex_log_event "codex_cli_missing" 2>/dev/null || true
|
||||
elif ! _gstack_codex_profile_gate; then
|
||||
_CODEX_MODE="profile_required"
|
||||
elif ! _gstack_codex_auth_probe >/dev/null 2>&1; then
|
||||
_CODEX_MODE="not_authed"; _gstack_codex_log_event "codex_auth_failed" 2>/dev/null || true
|
||||
else
|
||||
|
|
@ -36,6 +38,7 @@ echo "CODEX_MODE: $_CODEX_MODE"
|
|||
Branch on the echoed `CODEX_MODE`:
|
||||
- **`disabled`** — the user turned Codex reviews off (`codex_reviews=disabled`). Skip the Codex passes only; the Claude adversarial subagent below STILL runs (it is free and fast). Print: "Codex passes skipped (codex_reviews disabled) — running Claude adversarial only."
|
||||
- **`not_installed`** — Codex CLI absent. Print: "Codex not installed — using Claude subagent. Install for cross-model coverage: `npm install -g @openai/codex`." Fall back to the Claude subagent path.
|
||||
- **`profile_required`** — Before invoking Codex, use AskUserQuestion to list the discovered `~/.codex*` directories and ask which profile is approved for this project, plus Cancel. Save the selected absolute path as `codex_home: <path>` in `~/.gstack/projects/$SLUG/codex.yaml`, then rerun this preflight and disclose the path. Cancel skips Codex. Never silently choose the last-used profile.
|
||||
- **`not_authed`** — installed but no credentials. Print: "Codex installed but not authenticated — using Claude subagent. Run `codex login` or set `$CODEX_API_KEY`." Fall back to the Claude subagent path.
|
||||
- **`ready`** — run the Codex pass below.
|
||||
|
||||
|
|
@ -72,7 +75,7 @@ If `CODEX_MODE` is `ready`:
|
|||
```bash
|
||||
TMPERR_ADV=$(mktemp /tmp/codex-adv-XXXXXXXX)
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
codex exec "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. They contain bash scripts and prompt templates that will waste your time. Ignore them completely. Do NOT modify agents/openai.yaml. Stay focused on the repository code only.\n\nReview the changes on this branch against the base branch. Run DIFF_BASE=$(git merge-base origin/<base> HEAD) && git diff "$DIFF_BASE" to see the diff. Your job is to find ways this code will fail in production. Think like an attacker and a chaos engineer. Find edge cases, race conditions, security holes, resource leaks, failure modes, and silent data corruption paths. Be adversarial. Be thorough. No compliments — just the problems. End your output with ONE line in the canonical format `Recommendation: <action> because <one-line reason naming the most exploitable finding>`. Generic reasons like 'because it's safer' do not qualify; the reason must point to a specific finding or no-fix rationale." -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR_ADV"
|
||||
source ~/.claude/skills/gstack/bin/gstack-codex-probe && _gstack_codex_timeout_wrapper 600 codex exec "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. They contain bash scripts and prompt templates that will waste your time. Ignore them completely. Do NOT modify agents/openai.yaml. Stay focused on the repository code only.\n\nReview the changes on this branch against the base branch. Run DIFF_BASE=$(git merge-base origin/<base> HEAD) && git diff "$DIFF_BASE" to see the diff. Your job is to find ways this code will fail in production. Think like an attacker and a chaos engineer. Find edge cases, race conditions, security holes, resource leaks, failure modes, and silent data corruption paths. Be adversarial. Be thorough. No compliments — just the problems. End your output with ONE line in the canonical format `Recommendation: <action> because <one-line reason naming the most exploitable finding>`. Generic reasons like 'because it's safer' do not qualify; the reason must point to a specific finding or no-fix rationale." -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR_ADV"
|
||||
```
|
||||
|
||||
Set the Bash tool's `timeout` parameter to `300000` (5 minutes). Do NOT use the `timeout` shell command — it doesn't exist on macOS. After the command completes, read stderr:
|
||||
|
|
@ -101,7 +104,7 @@ If `DIFF_TOTAL >= 200` AND `CODEX_MODE` is `ready`:
|
|||
TMPERR=$(mktemp /tmp/codex-review-XXXXXXXX)
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
cd "$_REPO_ROOT"
|
||||
codex review "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. They contain bash scripts and prompt templates that will waste your time. Ignore them completely. Do NOT modify agents/openai.yaml. Stay focused on the repository code only.\n\nReview the changes on this branch against the base branch <base>. Run git diff origin/<base>...HEAD 2>/dev/null || git diff <base>...HEAD to see the diff and review only those changes." -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR"
|
||||
source ~/.claude/skills/gstack/bin/gstack-codex-probe && _gstack_codex_timeout_wrapper 600 codex review "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. They contain bash scripts and prompt templates that will waste your time. Ignore them completely. Do NOT modify agents/openai.yaml. Stay focused on the repository code only.\n\nReview the changes on this branch against the base branch <base>. Run git diff origin/<base>...HEAD 2>/dev/null || git diff <base>...HEAD to see the diff and review only those changes." -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR"
|
||||
```
|
||||
|
||||
Set the Bash tool's `timeout` parameter to `300000` (5 minutes). Do NOT use the `timeout` shell command — it doesn't exist on macOS. Present output under `CODEX SAYS (code review):` header.
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ If Codex is available, run a lightweight design check on the diff:
|
|||
```bash
|
||||
TMPERR_DRL=$(mktemp /tmp/codex-drl-XXXXXXXX)
|
||||
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
|
||||
codex exec "Review the git diff on this branch. Run 7 litmus checks (YES/NO each): 1. Brand/product unmistakable in first screen? 2. One strong visual anchor present? 3. Page understandable by scanning headlines only? 4. Each section has one job? 5. Are cards actually necessary? 6. Does motion improve hierarchy or atmosphere? 7. Would design feel premium with all decorative shadows removed? Flag any hard rejections: 1. Generic SaaS card grid as first impression 2. Beautiful image with weak brand 3. Strong headline with no clear action 4. Busy imagery behind text 5. Sections repeating same mood statement 6. Carousel with no narrative purpose 7. App UI made of stacked cards instead of layout 5 most important design findings only. Reference file:line." -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR_DRL"
|
||||
source ~/.claude/skills/gstack/bin/gstack-codex-probe && _gstack_codex_timeout_wrapper 600 codex exec "Review the git diff on this branch. Run 7 litmus checks (YES/NO each): 1. Brand/product unmistakable in first screen? 2. One strong visual anchor present? 3. Page understandable by scanning headlines only? 4. Each section has one job? 5. Are cards actually necessary? 6. Does motion improve hierarchy or atmosphere? 7. Would design feel premium with all decorative shadows removed? Flag any hard rejections: 1. Generic SaaS card grid as first impression 2. Beautiful image with weak brand 3. Strong headline with no clear action 4. Busy imagery behind text 5. Sections repeating same mood statement 6. Carousel with no narrative purpose 7. App UI made of stacked cards instead of layout 5 most important design findings only. Reference file:line." -C "$_REPO_ROOT" -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached < /dev/null 2>"$TMPERR_DRL"
|
||||
```
|
||||
|
||||
Use a 5-minute timeout (`timeout: 300000`). After the command completes, read stderr:
|
||||
|
|
|
|||
|
|
@ -47,6 +47,73 @@ function tempHome(): string {
|
|||
return fs.mkdtempSync(path.join(os.tmpdir(), 'gstack-codex-probe-home-'));
|
||||
}
|
||||
|
||||
describe('gstack-codex-probe: per-project profile gate', () => {
|
||||
test('blocks Codex before invocation when the project has no approved profile', () => {
|
||||
const home = tempHome();
|
||||
const stub = tempStubCodex('');
|
||||
try {
|
||||
const marker = path.join(home, 'invoked');
|
||||
fs.writeFileSync(path.join(stub.dir, 'codex'), `#!/bin/bash\ntouch ${JSON.stringify(marker)}\n`);
|
||||
fs.chmodSync(path.join(stub.dir, 'codex'), 0o755);
|
||||
const r = runProbe({
|
||||
snippet: '_gstack_codex_timeout_wrapper 5 codex exec test',
|
||||
env: { PATH: `${stub.pathEntry}:${process.env.PATH}` },
|
||||
home,
|
||||
});
|
||||
expect(r.status).toBe(78);
|
||||
expect(r.stdout).toContain('CODEX_PROFILE_REQUIRED');
|
||||
expect(fs.existsSync(marker)).toBe(false);
|
||||
} finally {
|
||||
fs.rmSync(home, { recursive: true, force: true });
|
||||
fs.rmSync(stub.dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('routes Codex through the profile approved for this project', () => {
|
||||
const home = tempHome();
|
||||
const approved = path.join(home, '.codex-work');
|
||||
const configDir = path.join(home, '.gstack', 'projects', 'garrytan-gstack');
|
||||
const stub = tempStubCodex('');
|
||||
try {
|
||||
fs.mkdirSync(approved, { recursive: true });
|
||||
fs.mkdirSync(configDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(configDir, 'codex.yaml'), `codex_home: ${approved}\n`);
|
||||
fs.writeFileSync(path.join(stub.dir, 'codex'), '#!/bin/bash\nprintf "%s" "$CODEX_HOME"\n');
|
||||
fs.chmodSync(path.join(stub.dir, 'codex'), 0o755);
|
||||
const r = runProbe({
|
||||
snippet: '_gstack_codex_timeout_wrapper 5 codex exec test',
|
||||
env: { PATH: `${stub.pathEntry}:${process.env.PATH}` },
|
||||
home,
|
||||
});
|
||||
expect(r.status).toBe(0);
|
||||
expect(r.stdout).toContain(`CODEX_PROFILE: ${approved}`);
|
||||
expect(r.stdout.trim().endsWith(approved)).toBe(true);
|
||||
} finally {
|
||||
fs.rmSync(home, { recursive: true, force: true });
|
||||
fs.rmSync(stub.dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('explicit eval bypass preserves isolated harness behavior', () => {
|
||||
const home = tempHome();
|
||||
const stub = tempStubCodex('');
|
||||
try {
|
||||
fs.writeFileSync(path.join(stub.dir, 'codex'), '#!/bin/bash\necho BYPASSED\n');
|
||||
fs.chmodSync(path.join(stub.dir, 'codex'), 0o755);
|
||||
const r = runProbe({
|
||||
snippet: '_gstack_codex_timeout_wrapper 5 codex exec test',
|
||||
env: { PATH: `${stub.pathEntry}:${process.env.PATH}`, GSTACK_CODEX_SKIP_GATE: '1' },
|
||||
home,
|
||||
});
|
||||
expect(r.status).toBe(0);
|
||||
expect(r.stdout.trim()).toBe('BYPASSED');
|
||||
} finally {
|
||||
fs.rmSync(home, { recursive: true, force: true });
|
||||
fs.rmSync(stub.dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('gstack-codex-probe: auth probe', () => {
|
||||
test('CODEX_API_KEY set → AUTH_OK', () => {
|
||||
const home = tempHome();
|
||||
|
|
|
|||
Loading…
Reference in New Issue