#!/usr/bin/env bash set -euo pipefail # CASAN Level 5 fallback runner. # Usage: # model-fallback.sh --primary '' --fallback '' OUTPUT_FILE="${1:-}" shift || true PRIMARY_CMD="" FALLBACK_CMD="" while [[ "$#" -gt 0 ]]; do case "$1" in --primary) PRIMARY_CMD="${2:-}" shift 2 ;; --fallback) FALLBACK_CMD="${2:-}" shift 2 ;; *) echo "Unknown argument: $1" >&2 exit 64 ;; esac done if [[ -z "$OUTPUT_FILE" || -z "$PRIMARY_CMD" || -z "$FALLBACK_CMD" ]]; then echo "Usage: model-fallback.sh --primary '' --fallback ''" >&2 exit 64 fi SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/casan-paths.sh" PROJECT_ROOT="$CASAN_APP_ROOT" LOG_DIR="$CASAN_STATE_ROOT/logs/level5" mkdir -p "$LOG_DIR" "$(dirname "$OUTPUT_FILE")" FALLBACK_LOG="$LOG_DIR/fallback.jsonl" TRACE_ID="$(uuidgen 2>/dev/null | tr '[:upper:]' '[:lower:]' || printf 'fallback-%s-%s' "$(date +%s)" "$$")" TIMESTAMP="$(date -u +"%Y-%m-%dT%H:%M:%SZ")" TMP_PRIMARY="$(mktemp)" TMP_FALLBACK="$(mktemp)" set +e bash -c "$PRIMARY_CMD" > "$TMP_PRIMARY" 2>&1 PRIMARY_RC=$? set -e ROUTE="primary" FINAL_RC="$PRIMARY_RC" if [[ "$PRIMARY_RC" -eq 0 && -s "$TMP_PRIMARY" ]]; then cp "$TMP_PRIMARY" "$OUTPUT_FILE" else ROUTE="fallback" set +e bash -c "$FALLBACK_CMD" > "$TMP_FALLBACK" 2>&1 FALLBACK_RC=$? set -e FINAL_RC="$FALLBACK_RC" cp "$TMP_FALLBACK" "$OUTPUT_FILE" fi printf '{"timestamp":"%s","trace_id":"%s","harness":"L5-model-fallback","primary_exit":%s,"route":"%s","final_exit":%s,"output":"%s"}\n' \ "$TIMESTAMP" "$TRACE_ID" "$PRIMARY_RC" "$ROUTE" "$FINAL_RC" "$OUTPUT_FILE" >> "$FALLBACK_LOG" echo "FALLBACK_ROUTE route=$ROUTE primary_exit=$PRIMARY_RC final_exit=$FINAL_RC output=$OUTPUT_FILE" exit "$FINAL_RC"