#!/usr/bin/env bash
# Validation gate for harness_orchestrator.sh Final-Step-A (REC-74 / REC-27).
# Static + behavioral assertions on the auto-PR end step. Pins the
# safety-critical properties so a later edit can't silently weaken them:
#   - success-gated (no PR on a failed/dry build)
#   - idempotent (gh pr view before gh pr create)
#   - non-fatal (never flips a green build red)
#   - folds in the session_workspace metadata block
#   - does not launch the retired detached review runner
# Self-contained: no real gh / GitHub needed (static source checks + local git remotes).
set -uo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
ORCH="$HERE/../harness_orchestrator.sh"
PASS=0; FAIL=0
ok(){ echo "  OK: $1"; PASS=$((PASS+1)); }
no(){ echo "  FAIL: $1"; FAIL=$((FAIL+1)); }
remote_head(){ git --git-dir="$1" rev-parse "refs/heads/$2" 2>/dev/null; }

test -f "$ORCH" || { echo "FATAL: orchestrator not found at $ORCH"; exit 1; }

# 1. syntax
bash -n "$ORCH" && ok "orchestrator bash -n clean" || no "orchestrator syntax error"

# Extract just the Final-Step-A block (from its banner to the exit-logic comment).
BLOCK="$(awk '/Final Step \(A\)/{f=1} f{print} /Exit non-zero if the build did not complete/{f=0}' "$ORCH")"
test -n "$BLOCK" && ok "Final-Step-A block present" || no "Final-Step-A block missing"

# 2. success-gated: the PR step only runs on a completed, non-dry build.
printf '%s\n' "$BLOCK" | grep -q 'if \$all_done && ! \$DRY_RUN' \
  && ok "Final-Step-A gated on \$all_done && !\$DRY_RUN" || no "Final-Step-A not success/dry gated"

# 3. never PRs main.
printf '%s\n' "$BLOCK" | grep -q '"\$push_branch" != "main"' \
  && ok "Final-Step-A refuses to PR main" || no "Final-Step-A could PR main"

# 4. idempotent: checks for an existing PR before creating one.
printf '%s\n' "$BLOCK" | grep -q 'gh pr view' \
  && printf '%s\n' "$BLOCK" | grep -q 'gh pr create' \
  && ok "Final-Step-A is idempotent (gh pr view before create)" || no "Final-Step-A missing idempotency guard"

# 5. non-fatal: the block must not contain an `exit` (a green build stays green).
printf '%s\n' "$BLOCK" | grep -qE '(^|[^_])exit ' \
  && no "Final-Step-A contains an exit (could fail a green build)" \
  || ok "Final-Step-A is non-fatal (no exit in the block)"

# 6. folds in the session_workspace handoff-metadata block.
printf '%s\n' "$BLOCK" | grep -q 'pr-metadata' \
  && ok "Final-Step-A folds in session_workspace pr-metadata" || no "Final-Step-A missing pr-metadata"

# 7. the retired detached review runner must stay absent; convergence is in-orchestrator.
printf '%s\n' "$BLOCK" | grep -qE 'nohup|codex-eob-run\.sh' \
  && no "Final-Step-A still launches the retired async review runner" \
  || ok "Final-Step-A does NOT launch the retired async review runner"

# 8. behavioral: a --dry-run parse must NOT emit a Final-Step-A line.
SBX="$(mktemp -d)"; trap 'rm -rf "$SBX"' EXIT
cat > "$SBX/BUILD_SPEC.md" <<'SPEC'
# BUILD_SPEC — fsa-test

## Phase 1: First
engine: codex
Do a thing.
Validation command: `true`

## Phase 2: Second
engine: codex
Do another thing.
Validation command: `true`
SPEC
OUT="$(cd "$SBX" && bash "$ORCH" --dry-run "$SBX/BUILD_SPEC.md" 2>&1)"
printf '%s\n' "$OUT" | grep -qi 'Parsed 2 phases' && ok "dry-run parses the spec" || no "dry-run did not parse"
printf '%s\n' "$OUT" | grep -qi 'Final-Step-A' && no "dry-run wrongly emitted Final-Step-A" || ok "dry-run does NOT fire Final-Step-A"

# 9. behavioral regression: a reused issue-keyed PR branch may already exist on
# origin at a stale divergent commit. Final-Step-A must update that branch to this
# build's local HEAD before reusing the existing PR, while never force-pushing main.
REAL="$SBX/real-final-step-a"
BIN="$REAL/bin"
ORIGIN="$REAL/origin.git"
WORK="$REAL/work"
STALE="$REAL/stale"
BRANCH="codex/REC-72-prompt-authoring"
mkdir -p "$BIN"
git init -q --bare "$ORIGIN"
git init -q "$WORK"
git -C "$WORK" config user.email "final-step-a@example.invalid"
git -C "$WORK" config user.name "Final Step A"
printf '[project]\nname = "final-step-a-test"\nversion = "0.0.0"\n' > "$WORK/pyproject.toml"
printf 'initial\n' > "$WORK/README.md"
cat > "$WORK/BUILD_SPEC.md" <<'SPEC'
# BUILD_SPEC — Final-Step-A regression

## Phase 1: First
engine: claude
depends_on: none

### Validation
```bash
test -f README.md
```

## Phase 2: Second
engine: claude
depends_on: Phase 1

### Validation
```bash
test -f README.md
```
SPEC
git -C "$WORK" add pyproject.toml README.md BUILD_SPEC.md
git -C "$WORK" commit -qm "init"
git -C "$WORK" branch -M main
git -C "$WORK" remote add origin "$ORIGIN"
git -C "$WORK" push -q -u origin main
git -C "$WORK" checkout -q -b "$BRANCH"
printf 'converged\n' > "$WORK/converged.txt"
git -C "$WORK" add converged.txt
git -C "$WORK" commit -qm "converged build head"
CONVERGED_SHA="$(git -C "$WORK" rev-parse HEAD)"
git clone -q "$ORIGIN" "$STALE"
git -C "$STALE" config user.email "stale@example.invalid"
git -C "$STALE" config user.name "Stale Dispatch"
git -C "$STALE" checkout -q -b "$BRANCH" origin/main
printf 'stale\n' > "$STALE/stale.txt"
git -C "$STALE" add stale.txt
git -C "$STALE" commit -qm "stale redispatch head"
git -C "$STALE" push -q origin "HEAD:refs/heads/$BRANCH"
STALE_SHA="$(remote_head "$ORIGIN" "$BRANCH")"

cat > "$BIN/claude" <<'STUB'
#!/usr/bin/env bash
printf 'stub claude invoked\n'
exit 0
STUB
cat > "$BIN/codex" <<'STUB'
#!/usr/bin/env bash
printf 'VERDICT: CONVERGED\n'
exit 0
STUB
cat > "$BIN/gh" <<'STUB'
#!/usr/bin/env bash
if [ "${1:-}" = "pr" ] && [ "${2:-}" = "view" ]; then
    case "$*" in
        *"--json"*) printf 'https://example.invalid/pull/32\n' ;;
    esac
    exit 0
fi
if [ "${1:-}" = "pr" ] && [ "${2:-}" = "create" ]; then
    printf 'https://example.invalid/pull/32\n'
    exit 0
fi
exit 1
STUB
chmod +x "$BIN/claude" "$BIN/codex" "$BIN/gh"

REAL_OUT="$REAL/reused-branch.out"
PATH="$BIN:$PATH" \
HARNESS_CODEX_BIN="$BIN/codex" \
CONVERGE_MAX_ROUNDS=1 \
    bash "$ORCH" --coder claude --dir "$WORK" --no-codex-spec-review "$WORK/BUILD_SPEC.md" >"$REAL_OUT" 2>&1
REAL_RC=$?
[ "$REAL_RC" -eq 0 ] && ok "reused-branch harness run exits zero" || no "reused-branch harness run exited $REAL_RC"
UPDATED_SHA="$(remote_head "$ORIGIN" "$BRANCH")"
[ "$STALE_SHA" != "$CONVERGED_SHA" ] && ok "test fixture has divergent stale remote branch" || no "test fixture did not diverge remote branch"
[ "$UPDATED_SHA" = "$CONVERGED_SHA" ] \
  && ok "Final-Step-A updates reused PR branch to converged HEAD" \
  || no "Final-Step-A left reused PR branch at $UPDATED_SHA instead of $CONVERGED_SHA"
grep -q 'git push failed — commits are local only' "$REAL_OUT" \
  && no "Final-Step-A orphaned converged HEAD behind local-only push warning" \
  || ok "Final-Step-A does NOT leave converged HEAD local-only"
grep -q 'Final-Step-A: PR already open' "$REAL_OUT" \
  && ok "Final-Step-A reuses existing PR after updating branch" \
  || no "Final-Step-A did not exercise existing-PR reuse path"

MAIN_ROOT="$SBX/main-force-guard"
MAIN_ORIGIN="$MAIN_ROOT/origin.git"
MAIN_WORK="$MAIN_ROOT/work"
MAIN_REMOTE="$MAIN_ROOT/remote-writer"
mkdir -p "$MAIN_ROOT"
git init -q --bare "$MAIN_ORIGIN"
git init -q "$MAIN_WORK"
git -C "$MAIN_WORK" config user.email "main-guard@example.invalid"
git -C "$MAIN_WORK" config user.name "Main Guard"
printf '[project]\nname = "main-force-guard"\nversion = "0.0.0"\n' > "$MAIN_WORK/pyproject.toml"
printf 'initial\n' > "$MAIN_WORK/README.md"
cp "$WORK/BUILD_SPEC.md" "$MAIN_WORK/BUILD_SPEC.md"
git -C "$MAIN_WORK" add pyproject.toml README.md BUILD_SPEC.md
git -C "$MAIN_WORK" commit -qm "init"
git -C "$MAIN_WORK" branch -M main
git -C "$MAIN_WORK" remote add origin "$MAIN_ORIGIN"
git -C "$MAIN_WORK" push -q -u origin main
printf 'local main\n' > "$MAIN_WORK/local-main.txt"
git -C "$MAIN_WORK" add local-main.txt
git -C "$MAIN_WORK" commit -qm "local main head"
LOCAL_MAIN_SHA="$(git -C "$MAIN_WORK" rev-parse HEAD)"
git clone -q "$MAIN_ORIGIN" "$MAIN_REMOTE"
git -C "$MAIN_REMOTE" config user.email "remote-main@example.invalid"
git -C "$MAIN_REMOTE" config user.name "Remote Main"
printf 'remote main\n' > "$MAIN_REMOTE/remote-main.txt"
git -C "$MAIN_REMOTE" add remote-main.txt
git -C "$MAIN_REMOTE" commit -qm "remote main head"
git -C "$MAIN_REMOTE" push -q origin HEAD:main
REMOTE_MAIN_SHA="$(remote_head "$MAIN_ORIGIN" main)"
MAIN_OUT="$MAIN_ROOT/main-guard.out"
PATH="$BIN:$PATH" \
HARNESS_CODEX_BIN="$BIN/codex" \
CONVERGE_MAX_ROUNDS=1 \
    bash "$ORCH" --coder claude --dir "$MAIN_WORK" --no-codex-spec-review "$MAIN_WORK/BUILD_SPEC.md" >"$MAIN_OUT" 2>&1
MAIN_RC=$?
[ "$MAIN_RC" -eq 0 ] && ok "main-branch harness run exits zero" || no "main-branch harness run exited $MAIN_RC"
AFTER_MAIN_SHA="$(remote_head "$MAIN_ORIGIN" main)"
[ "$LOCAL_MAIN_SHA" != "$REMOTE_MAIN_SHA" ] && ok "main force-guard fixture is divergent" || no "main force-guard fixture did not diverge"
[ "$AFTER_MAIN_SHA" = "$REMOTE_MAIN_SHA" ] \
  && ok "main is not force-pushed when remote diverges" \
  || no "main was overwritten by Final-Step-A push"

echo "--------"; echo "PASS=$PASS  FAIL=$FAIL"
[ "$FAIL" -eq 0 ]
