#!/usr/bin/env bash set -eu HERE=$(cd "$(dirname "$0")" && pwd) RUN=$HERE/../scripts/codex-run.sh TMP=$(mktemp -d) trap 'rm -rf "$TMP"' EXIT # 1. happy path -> status ok, thread_id captured, exit 0 mkdir -p "$TMP/ok"; echo "hello" > "$TMP/ok/prompt.md" CODEX_BIN=$HERE/stub-ok/codex "$RUN" "$TMP/ok" exec [[ $(cat "$TMP/ok/status") == ok ]] [[ $(cat "$TMP/ok/thread_id") == test-thread-123 ]] [[ $(cat "$TMP/ok/last.md") == "stub answer|args="*" -|hello" ]] # 2. stall -> watchdog kills, status stalled, exit nonzero mkdir -p "$TMP/hang"; echo "hello" > "$TMP/hang/prompt.md" if CODEX_BIN=$HERE/stub-hang/codex CODEX_STALL_TIMEOUT=4 CODEX_WATCH_INTERVAL=2 \ "$RUN" "$TMP/hang" exec; then echo "FAIL: stall run must exit nonzero"; exit 1 fi [[ $(cat "$TMP/hang/status") == stalled ]] [[ $(cat "$TMP/hang/thread_id") == hang-thread-456 ]] # 3. prompt.md via stdin path mkdir -p "$TMP/stdin"; echo "prompt from file" > "$TMP/stdin/prompt.md" CODEX_BIN=$HERE/stub-ok/codex "$RUN" "$TMP/stdin" exec [[ $(cat "$TMP/stdin/status") == ok ]] [[ $(cat "$TMP/stdin/last.md") == "stub answer|args="*" -|prompt from file" ]] # 4. prompt.md + codex flags -> stdin still delivered (regression: options ate the prompt) mkdir -p "$TMP/flags"; echo "prompt with flags" > "$TMP/flags/prompt.md" CODEX_BIN=$HERE/stub-ok/codex "$RUN" "$TMP/flags" exec -s read-only [[ $(cat "$TMP/flags/status") == ok ]] [[ $(cat "$TMP/flags/last.md") == *"-s read-only exec"* ]] [[ $(cat "$TMP/flags/last.md") == *" -|prompt with flags" ]] # 5. review + scope + prompt.md -> scope translated into prompt text, no scope flag in args mkdir -p "$TMP/rev"; echo "focus: correctness" > "$TMP/rev/prompt.md" CODEX_BIN=$HERE/stub-ok/codex "$RUN" "$TMP/rev" review --base main [[ $(cat "$TMP/rev/status") == ok ]] LAST5=$(cat "$TMP/rev/last.md") [[ $LAST5 == "stub answer|args=exec review"* ]] [[ $LAST5 != *"--base"* ]] [[ $LAST5 == *"against base ref 'main'"* ]] [[ $LAST5 == *"git diff main...HEAD"* ]] [[ $LAST5 == *"focus: correctness" ]] # 6. review + scope, no prompt.md -> native path keeps the scope flag mkdir -p "$TMP/rev-native" CODEX_BIN=$HERE/stub-ok/codex "$RUN" "$TMP/rev-native" review --uncommitted [[ $(cat "$TMP/rev-native/status") == ok ]] [[ $(cat "$TMP/rev-native/last.md") == "stub answer|args=exec review"* ]] [[ $(cat "$TMP/rev-native/last.md") == *"--uncommitted"* ]] # 7. review + prompt.md, no scope -> custom instructions pass through untouched mkdir -p "$TMP/rev-custom"; echo "review whatever you find" > "$TMP/rev-custom/prompt.md" CODEX_BIN=$HERE/stub-ok/codex "$RUN" "$TMP/rev-custom" review [[ $(cat "$TMP/rev-custom/status") == ok ]] [[ $(cat "$TMP/rev-custom/last.md") == "stub answer|args=exec review"*"-|review whatever you find" ]] # 8. exec -s read-only then resume reuses -s read-only before exec (hoisted) mkdir -p "$TMP/persist"; echo "task persist" > "$TMP/persist/prompt.md" CODEX_BIN=$HERE/stub-ok/codex "$RUN" "$TMP/persist" exec -s read-only [[ $(cat "$TMP/persist/status") == ok ]] [[ $(cat "$TMP/persist/last.md") == *"-s read-only exec"* ]] # resume should hoist saved flags before exec CODEX_BIN=$HERE/stub-ok/codex "$RUN" "$TMP/persist" resume "$(cat "$TMP/persist/thread_id")" [[ $(cat "$TMP/persist/status") == ok ]] RESUME8=$(cat "$TMP/persist/last.md") [[ $RESUME8 == *"-s read-only exec resume"* ]] || { echo "FAIL case 8 hoist: $RESUME8"; exit 1; } [[ $RESUME8 == *" -|task persist"* ]] || { echo "FAIL case 8 prompt: $RESUME8"; exit 1; } # 9. exec -m gpt-5.5 --add-dir "/tmp/with spaces" NUL preserves space; resume proves persistence mkdir -p "$TMP/spaces"; echo "task spaces" > "$TMP/spaces/prompt.md" CODEX_BIN=$HERE/stub-ok/codex "$RUN" "$TMP/spaces" exec -m gpt-5.5 --add-dir "/tmp/with spaces" [[ $(cat "$TMP/spaces/status") == ok ]] [[ $(cat "$TMP/spaces/last.md") == *"-m gpt-5.5 --add-dir /tmp/with spaces"* ]] # verify NUL storage preserves space as single entry mapfile -d '' -t CHK < "$TMP/spaces/codex-flags" [[ ${#CHK[@]} -eq 4 ]] || { echo "FAIL case 9 NUL count ${#CHK[@]}: ${CHK[*]}"; exit 1; } [[ ${CHK[0]} == "-m" ]] || { echo "FAIL case 9 flag0 ${CHK[0]}"; exit 1; } [[ ${CHK[2]} == "--add-dir" ]] || { echo "FAIL case 9 flag2 ${CHK[2]}"; exit 1; } [[ ${CHK[3]} == "/tmp/with spaces" ]] || { echo "FAIL case 9 space preserved got '${CHK[3]}'"; exit 1; } # resume should contain same spaced arg as single entry CODEX_BIN=$HERE/stub-ok/codex "$RUN" "$TMP/spaces" resume "$(cat "$TMP/spaces/thread_id")" [[ $(cat "$TMP/spaces/status") == ok ]] RESUME9=$(cat "$TMP/spaces/last.md") [[ $RESUME9 == *"-m gpt-5.5 --add-dir /tmp/with spaces exec resume"* ]] || { echo "FAIL case 9 resume hoist: $RESUME9"; exit 1; } # 10. review --base main -s read-only then resume keeps -s read-only before exec mkdir -p "$TMP/rev-persist"; echo "review persist" > "$TMP/rev-persist/prompt.md" CODEX_BIN=$HERE/stub-ok/codex "$RUN" "$TMP/rev-persist" review --base main -s read-only [[ $(cat "$TMP/rev-persist/status") == ok ]] LAST10=$(cat "$TMP/rev-persist/last.md") [[ $LAST10 == *"-s read-only exec review"* ]] || { echo "FAIL case 10 exec hoist: $LAST10"; exit 1; } [[ $LAST10 == *"against base ref 'main'"* ]] || { echo "FAIL case 10 scope text: $LAST10"; exit 1; } # flags file should contain only -s read-only, scope translated to prompt mapfile -d '' -t CHK10 < "$TMP/rev-persist/codex-flags" [[ ${#CHK10[@]} -eq 2 ]] || { echo "FAIL case 10 flags count ${#CHK10[@]}: ${CHK10[*]}"; exit 1; } [[ ${CHK10[0]} == "-s" && ${CHK10[1]} == "read-only" ]] || { echo "FAIL case 10 flags ${CHK10[*]}"; exit 1; } # resume keeps flag CODEX_BIN=$HERE/stub-ok/codex "$RUN" "$TMP/rev-persist" resume "$(cat "$TMP/rev-persist/thread_id")" [[ $(cat "$TMP/rev-persist/status") == ok ]] RESUME10=$(cat "$TMP/rev-persist/last.md") [[ $RESUME10 == *"-s read-only exec resume"* ]] || { echo "FAIL case 10 resume: $RESUME10"; exit 1; } echo "ALL PASS"