#!/bin/bash
# codex_overnight_review.sh — unattended overnight Codex code review.
#
# v1 (SAFE): runs `codex review` over recent engine changes and writes a
# timestamped report for MORNING TRIAGE. It does NOT auto-fix or auto-merge —
# Claude verifies the findings (adversarial loop) and files confirmed bugs to
# Linear; a human reviews PRs. This keeps unverified findings from turning into
# overnight slop PRs (the failure mode the consult flagged).
#
# Dispatch overnight via tmux (the /dispatch pattern), e.g.:
#   ssh studio 'tmux new -d -s codex-review "bash ~/CLAUDE_PROJECTS/recoil/pipeline/tools/codex_overnight_review.sh"'
# (Runs where Codex.app + auth exist — MacBook for now; Studio once it's flipped + has Codex.)
#
# Learnings baked in (from the 2026-06-01 REC-13 validation):
#   - codex exec/review MUST have stdin closed (</dev/null) or it hangs reading stdin.
#   - ripgrep must be on PATH (Codex search); brew installs /opt/homebrew/bin/rg.
#   - Work in the ISOLATED clone (~/Code/recoil-codex), never the live checkout.
set -uo pipefail

CODEX="/Applications/Codex.app/Contents/Resources/codex"
CLONE="$HOME/Code/recoil-codex"
REPORTDIR="$HOME/CLAUDE_PROJECTS/overnight-reviews"   # gitignore this dir
MARKER="$REPORTDIR/.last_reviewed"
export PATH="/opt/homebrew/bin:$PATH"                 # ensure ripgrep for Codex

mkdir -p "$REPORTDIR"

if [ ! -x "$CODEX" ]; then echo "FATAL: codex binary not found at $CODEX" >&2; exit 1; fi
if [ ! -d "$CLONE/.git" ]; then echo "FATAL: codex clone missing at $CLONE" >&2; exit 1; fi

cd "$CLONE" || exit 1

# Refresh the isolated clone to current origin/main (clean, no local cruft).
git fetch origin --quiet || { echo "FATAL: git fetch failed" >&2; exit 1; }
git checkout main --quiet 2>/dev/null || git checkout -B main origin/main --quiet
git reset --hard origin/main --quiet

# Review scope: arg overrides; else since last reviewed HEAD; else last 10 commits.
BASE="${1:-}"
if [ -z "$BASE" ]; then
  if [ -f "$MARKER" ]; then BASE="$(cat "$MARKER")"; else BASE="HEAD~10"; fi
fi

# Bail if nothing new to review (base == HEAD).
HEAD_SHA="$(git rev-parse HEAD)"
BASE_SHA="$(git rev-parse "$BASE" 2>/dev/null || echo "$BASE")"
if [ "$BASE_SHA" = "$HEAD_SHA" ]; then
  echo "Nothing new since last review ($HEAD_SHA). Skipping."
  exit 0
fi

STAMP="$(date +%Y%m%d-%H%M%S)"
REPORT="$REPORTDIR/review-$STAMP.md"
{
  echo "# Overnight Codex review — $STAMP"
  echo ""
  echo "- Base: \`$BASE\` ($BASE_SHA)"
  echo "- HEAD: \`$(git rev-parse --short HEAD)\`"
  echo "- Diffstat: $(git diff --shortstat "$BASE" HEAD 2>/dev/null)"
  echo ""
  echo "---"
  echo ""
} > "$REPORT"

# The review. stdin closed (</dev/null) is MANDATORY — else it hangs.
# NOTE (REC-40): the codex CLI rejects `--base` together with a positional [PROMPT]
# (and `-`/stdin is also a [PROMPT]), so the 4-axis prompt in codex_review_prompt.md
# cannot be passed alongside --base in this CLI version. Use the default reviewer with
# --base (it caught the real PR #11 P2); injecting the custom prompt is tracked in REC-40.
"$CODEX" review --base "$BASE" </dev/null >> "$REPORT" 2>&1
RC=$?

# Advance the marker only on a clean run.
if [ $RC -eq 0 ]; then git rev-parse HEAD > "$MARKER"; fi

echo "exit=$RC  report=$REPORT"
echo ""
echo "NEXT (morning triage): have Claude read $REPORT, adversarially verify each"
echo "finding against the live code, and file confirmed bugs to Linear (Recoil team)."
exit $RC
