Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion codex/sections/challenge-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ if [ -z "$PYTHON_CMD" ]; then
fi
# Fix 1+2: wrap with timeout (gtimeout/timeout fallback chain via probe helper),
# capture stderr to $TMPERR for auth error detection (was: 2>/dev/null).
TMPERR=${TMPERR:-$(mktemp "$TMP_ROOT/codex-err-XXXXXX")}
TMPERR=${TMPERR:-$(mktemp "$TMP_ROOT/codex-err-XXXXXX")} || { echo "ERROR: mktemp failed — cannot capture codex stderr; refusing to run codex challenge unmonitored." >&2; exit 1; }
_gstack_codex_timeout_wrapper 600 codex exec "<prompt>" -C "$_REPO_ROOT" -s read-only -c "model=\"${GSTACK_CODEX_MODEL:-gpt-6-astra}\"" -c 'model_reasoning_effort="high"' -c 'web_search="cached"' --json < /dev/null 2>"$TMPERR" | PYTHONUNBUFFERED=1 "$PYTHON_CMD" -u -c "
import sys, json
turn_completed_count = 0
Expand Down
2 changes: 1 addition & 1 deletion codex/sections/challenge-mode.md.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ if [ -z "$PYTHON_CMD" ]; then
fi
# Fix 1+2: wrap with timeout (gtimeout/timeout fallback chain via probe helper),
# capture stderr to $TMPERR for auth error detection (was: 2>/dev/null).
TMPERR=${TMPERR:-$(mktemp "$TMP_ROOT/codex-err-XXXXXX")}
TMPERR=${TMPERR:-$(mktemp "$TMP_ROOT/codex-err-XXXXXX")} || { echo "ERROR: mktemp failed — cannot capture codex stderr; refusing to run codex challenge unmonitored." >&2; exit 1; }
_gstack_codex_timeout_wrapper 600 codex exec "<prompt>" -C "$_REPO_ROOT" -s read-only {{CODEX_MODEL_CONFIG_FLAG}} -c 'model_reasoning_effort="high"' {{CODEX_WEB_SEARCH_FLAG}} --json < /dev/null 2>"$TMPERR" | PYTHONUNBUFFERED=1 "$PYTHON_CMD" -u -c "
import sys, json
turn_completed_count = 0
Expand Down
4 changes: 2 additions & 2 deletions codex/sections/consult-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ B) Start a new conversation

2. Create temp files:
```bash
TMPRESP=$(mktemp "$TMP_ROOT/codex-resp-XXXXXX")
TMPERR=$(mktemp "$TMP_ROOT/codex-err-XXXXXX")
TMPRESP=$(mktemp "$TMP_ROOT/codex-resp-XXXXXX") || { echo "ERROR: mktemp failed — cannot capture codex's response." >&2; exit 1; }
TMPERR=$(mktemp "$TMP_ROOT/codex-err-XXXXXX") || { echo "ERROR: mktemp failed — cannot capture codex stderr; refusing to run codex consult unmonitored." >&2; exit 1; }
```

3. **Plan review auto-detection:** If the user's prompt is about reviewing a plan,
Expand Down
4 changes: 2 additions & 2 deletions codex/sections/consult-mode.md.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ B) Start a new conversation

2. Create temp files:
```bash
TMPRESP=$(mktemp "$TMP_ROOT/codex-resp-XXXXXX")
TMPERR=$(mktemp "$TMP_ROOT/codex-err-XXXXXX")
TMPRESP=$(mktemp "$TMP_ROOT/codex-resp-XXXXXX") || { echo "ERROR: mktemp failed — cannot capture codex's response." >&2; exit 1; }
TMPERR=$(mktemp "$TMP_ROOT/codex-err-XXXXXX") || { echo "ERROR: mktemp failed — cannot capture codex stderr; refusing to run codex consult unmonitored." >&2; exit 1; }
```

3. **Plan review auto-detection:** If the user's prompt is about reviewing a plan,
Expand Down
4 changes: 2 additions & 2 deletions codex/sections/review-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ instructions get their own path (below).

1. Create temp files for output capture:
```bash
TMPERR=$(mktemp "$TMP_ROOT/codex-err-XXXXXX")
TMPERR=$(mktemp "$TMP_ROOT/codex-err-XXXXXX") || { echo "ERROR: mktemp failed — cannot capture codex stderr; refusing to run codex review unmonitored." >&2; exit 1; }
```

2. Run the review. No prompt argument — scope comes from `--base` (or `--commit <sha>`
Expand Down Expand Up @@ -81,7 +81,7 @@ _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
_USER_INSTRUCTIONS="<everything after '/codex review ' in user input>"
_PROMPT_FILE=$(mktemp "$TMP_ROOT/codex-prompt-XXXXXX")
_PROMPT_FILE=$(mktemp "$TMP_ROOT/codex-prompt-XXXXXX") || { echo "ERROR: mktemp failed — cannot write the codex review prompt 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 modify agents/openai.yaml. Stay focused on repository code only."
printf '\nCustom focus: %s\n\n' "$_USER_INSTRUCTIONS"
Expand Down
4 changes: 2 additions & 2 deletions codex/sections/review-mode.md.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ instructions get their own path (below).

1. Create temp files for output capture:
```bash
TMPERR=$(mktemp "$TMP_ROOT/codex-err-XXXXXX")
TMPERR=$(mktemp "$TMP_ROOT/codex-err-XXXXXX") || { echo "ERROR: mktemp failed — cannot capture codex stderr; refusing to run codex review unmonitored." >&2; exit 1; }
```

2. Run the review. No prompt argument — scope comes from `--base` (or `--commit <sha>`
Expand Down Expand Up @@ -79,7 +79,7 @@ _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
_USER_INSTRUCTIONS="<everything after '/codex review ' in user input>"
_PROMPT_FILE=$(mktemp "$TMP_ROOT/codex-prompt-XXXXXX")
_PROMPT_FILE=$(mktemp "$TMP_ROOT/codex-prompt-XXXXXX") || { echo "ERROR: mktemp failed — cannot write the codex review prompt 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 modify agents/openai.yaml. Stay focused on repository code only."
printf '\nCustom focus: %s\n\n' "$_USER_INSTRUCTIONS"
Expand Down
42 changes: 42 additions & 0 deletions test/regression-pr1169-mktemp-fallbacks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,48 @@ echo "SHOULD NOT REACH: $TMP_DIR"`;
});
});

// #2881: the three codex mode templates create $TMP_ROOT-scoped temp files
// (stderr captures, prompt files) with no failure guard. bin/gstack-paths
// deliberately best-effort-creates $TMP_ROOT ("the caller will discover
// [failure] on their own write attempt" — bin/gstack-paths:78-79), so an
// unwritable $TMP_ROOT makes these mktemp calls silently yield an empty
// variable. The next command then redirects stderr to `2>""`, which fails
// before codex even runs, and the error is misattributed to codex instead of
// to the missing temp file. Guards must abort loudly, matching the shape
// already pinned above for the five other call sites in this file and in
// test/regression-pr1169-build-app-sed.test.ts.
describe("#2881: codex mode template mktemp guards", () => {
test("review-mode.md.tmpl guards both mktemp assignments (TMPERR, _PROMPT_FILE)", () => {
const body = readScript("codex/sections/review-mode.md.tmpl");
expect(body).toMatch(/TMPERR=\$\(mktemp "\$TMP_ROOT\/codex-err-XXXXXX"\)\s*\|\|\s*\{[^}]*exit 1/);
expect(body).toMatch(/_PROMPT_FILE=\$\(mktemp "\$TMP_ROOT\/codex-prompt-XXXXXX"\)\s*\|\|\s*\{[^}]*exit 1/);
});

test("consult-mode.md.tmpl guards both mktemp assignments (TMPRESP, TMPERR)", () => {
const body = readScript("codex/sections/consult-mode.md.tmpl");
expect(body).toMatch(/TMPRESP=\$\(mktemp "\$TMP_ROOT\/codex-resp-XXXXXX"\)\s*\|\|\s*\{[^}]*exit 1/);
expect(body).toMatch(/TMPERR=\$\(mktemp "\$TMP_ROOT\/codex-err-XXXXXX"\)\s*\|\|\s*\{[^}]*exit 1/);
});

test("challenge-mode.md.tmpl guards its mktemp assignment and preserves the ${TMPERR:-...} wrapper", () => {
const body = readScript("codex/sections/challenge-mode.md.tmpl");
expect(body).toMatch(
/TMPERR=\$\{TMPERR:-\$\(mktemp "\$TMP_ROOT\/codex-err-XXXXXX"\)\}\s*\|\|\s*\{[^}]*exit 1/,
);
});

test("runtime: the guarded assignment aborts when mktemp fails, without invoking the guarded command", () => {
const { spawnSync } = require("node:child_process") as typeof import("node:child_process");
const script = `mktemp() { return 1; }
TMPERR=$(mktemp "$TMP_ROOT/codex-err-XXXXXX") || { echo "ERROR: mktemp failed — cannot capture codex stderr; refusing to run codex review unmonitored." >&2; exit 1; }
echo "SHOULD NOT REACH: $TMPERR"`;
const r = spawnSync("bash", ["-c", script], { encoding: "utf-8", timeout: 10_000 });
expect(r.status).toBe(1);
expect(r.stderr).toContain("mktemp failed");
expect(r.stdout).not.toContain("SHOULD NOT REACH");
});
});

describe("PR #1169 bug #5: supabase/verify-rls.sh mktemp fallback", () => {
const SCRIPT = "supabase/verify-rls.sh";

Expand Down
Loading