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
+2 -2
View File
@@ -55,7 +55,7 @@ do_migrate() {
# Run migration in a temp file, then atomic rename.
local TMPOUT
TMPOUT=$(mktemp "$GSTACK_HOME/developer-profile.json.XXXXXX.tmp")
TMPOUT=$(mktemp "$GSTACK_HOME/developer-profile.json.tmp.XXXXXX")
trap 'rm -f "$TMPOUT"' EXIT
cat "$LEGACY_FILE" | bun -e "
@@ -182,7 +182,7 @@ do_log_session() {
ensure_profile
local TMPOUT
TMPOUT=$(mktemp "$GSTACK_HOME/developer-profile.json.XXXXXX.tmp")
TMPOUT=$(mktemp "$GSTACK_HOME/developer-profile.json.tmp.XXXXXX")
trap 'rm -f "$TMPOUT"' EXIT
PROFILE_FILE_PATH="$PROFILE_FILE" RECORD_INPUT="$INPUT" TMPOUT_PATH="$TMPOUT" bun -e "