fix(mktemp): move the X-run to the end of every temp-file template (BSD/busybox safe)

BSD mktemp (macOS) does not substitute an X-run that has a suffix after it:
`mktemp "$TMP_ROOT/codex-err-XXXXXX.txt"` creates a LITERAL
codex-err-XXXXXX.txt on the first call (exit 0) and every later call fails
with `mkstemp failed: File exists` — so /codex breaks from the SECOND run on
every Mac, masquerading as a model stall. busybox mktemp (Alpine) rejects the
template on the first run. Fixes #2091, #2370.

Union of both community fixes, compared at the diff level:
- PR #2372: all 11 source sites with a suffix after the X-run — codex
  SKILL.md.tmpl (5), claude SKILL.md.tmpl (3), bin/gstack-developer-profile
  (2, suffix folded into the prefix: .json.tmp.XXXXXX), and the office-hours
  codex pass in scripts/resolvers/review.ts (1).
- PR #2103: the second half of #2091 — bin/gstack-paths now strips the
  trailing slash from TMP_ROOT at the source (macOS $TMPDIR ends in `/`),
  plus runtime tests pinning that normalization.

New repo-wide tripwire in test/regression-issue2091-bsd-mktemp.test.ts:
every .tmpl, every SKILL.md, and every scripts/resolvers/*.ts is swept —
no mktemp template may carry a suffix after the X-run, with a self-test so
the detector can't be quietly blinded. Generated SKILL.md files regenerated
via gen:skill-docs in this commit.

Contributed by @ShuratCode (PR #2103) and @noron12234 (PR #2372); PR #2285 by @cathrynlavery covered a subset.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-08-14 20:20:54 -07:00
co-authored by Claude Fable 5
parent a4daa352d6
commit 7a8e39d2cb
8 changed files with 191 additions and 17 deletions
+3 -3
View File
@@ -95,8 +95,8 @@ Create temp files:
```bash
PROMPT_FILE=$(mktemp /tmp/gstack-claude-prompt-XXXXXX)
RESP_FILE=$(mktemp /tmp/gstack-claude-response-XXXXXX.json)
ERR_FILE=$(mktemp /tmp/gstack-claude-error-XXXXXX.txt)
RESP_FILE=$(mktemp /tmp/gstack-claude-response-XXXXXX)
ERR_FILE=$(mktemp /tmp/gstack-claude-error-XXXXXX)
```
Cleanup at the end of every mode:
@@ -151,7 +151,7 @@ Review the current branch diff with nested Claude in tool-less mode.
```bash
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
cd "$_REPO_ROOT"
DIFF_FILE=$(mktemp /tmp/gstack-claude-diff-XXXXXX.patch)
DIFF_FILE=$(mktemp /tmp/gstack-claude-diff-XXXXXX)
git fetch origin <base> --quiet 2>/dev/null || true
git diff "origin/<base>" > "$DIFF_FILE" 2>/dev/null || git diff "<base>" > "$DIFF_FILE"
```