status.sh
1 #!/usr/bin/env bash 2 # status.sh — Show experiments with linked checkpoints and datasets 3 # 4 # Joins rad experiment list + rad-artifact list by commit OID to display 5 # a unified view of experiments, their checkpoints, and dataset state. 6 7 cmd_status() { 8 local json_output=false 9 10 while [ $# -gt 0 ]; do 11 case "$1" in 12 --json) json_output=true; shift ;; 13 -h|--help) 14 echo "Usage: cc-ml status [--json]" 15 echo "" 16 echo "Show experiments with linked checkpoint and dataset artifacts." 17 exit 0 18 ;; 19 *) die "unknown flag: $1" ;; 20 esac 21 done 22 23 require_cmds git jq 24 25 local dir 26 dir=$(pwd) 27 28 # --- Gather experiment data --- 29 30 local experiments="[]" 31 if command -v rad-experiment >/dev/null 2>&1; then 32 experiments=$(rad experiment list --json 2>/dev/null | jq -s '.' 2>/dev/null || echo "[]") 33 fi 34 35 # --- Gather artifact data --- 36 37 local releases="[]" 38 if command -v rad-artifact >/dev/null 2>&1; then 39 releases=$(rad-artifact list --json 2>/dev/null || echo "[]") 40 fi 41 42 # --- Gather tape data (local session) --- 43 44 local tape_data="[]" 45 local session_name="" 46 local session_root 47 session_root=$(find_session_root "$dir" 2>/dev/null || echo "") 48 49 if [ -n "$session_root" ]; then 50 local tape="$session_root/.community-computer/tape.jsonl" 51 if [ -f "$tape" ]; then 52 session_name=$(tape_session_name "$tape") 53 54 local config_line 55 config_line=$(grep -n '"type":[[:space:]]*"config"' "$tape" | tail -1 | cut -d: -f1 || true) 56 if [ -n "$config_line" ]; then 57 tape_data=$(tail -n +"$((config_line + 1))" "$tape" | jq -s '.' 2>/dev/null || echo "[]") 58 fi 59 fi 60 fi 61 62 # --- Read dataset info --- 63 64 local yaml_file="$dir/autoresearch.yaml" 65 local datasets="[]" 66 if [ -f "$yaml_file" ] && command -v python3 >/dev/null 2>&1; then 67 datasets=$(yaml_read_datasets "$yaml_file") 68 fi 69 70 # --- JSON output --- 71 72 if $json_output; then 73 jq -n \ 74 --argjson experiments "$experiments" \ 75 --argjson releases "$releases" \ 76 --argjson tape "$tape_data" \ 77 --argjson datasets "$datasets" \ 78 --arg session "$session_name" \ 79 '{ 80 session: $session, 81 experiments: $experiments, 82 releases: $releases, 83 tape: $tape, 84 datasets: $datasets 85 }' 86 return 0 87 fi 88 89 # --- Pretty output --- 90 91 local tape_count 92 tape_count=$(echo "$tape_data" | jq 'length') 93 local exp_count 94 exp_count=$(echo "$experiments" | jq 'length') 95 96 # Header. 97 if [ -n "$session_name" ]; then 98 local slug 99 slug=$(slugify "$session_name") 100 echo "Session: $session_name (experiments/$slug)" 101 else 102 echo "Session: (no active session)" 103 fi 104 105 # Metric info from tape config. 106 if [ -n "$session_root" ]; then 107 local tape="$session_root/.community-computer/tape.jsonl" 108 if [ -f "$tape" ]; then 109 local config_line 110 config_line=$(grep -n '"type":[[:space:]]*"config"' "$tape" | tail -1 | cut -d: -f1 || true) 111 if [ -n "$config_line" ]; then 112 local metric_name metric_unit direction 113 metric_name=$(sed -n "${config_line}p" "$tape" | jq -r '.metricName // "metric"') 114 metric_unit=$(sed -n "${config_line}p" "$tape" | jq -r '.metricUnit // ""') 115 direction=$(sed -n "${config_line}p" "$tape" | jq -r '.bestDirection // "lower"') 116 echo " Metric: $metric_name ($metric_unit, ${direction} is better)" 117 fi 118 fi 119 fi 120 121 # Datasets. 122 local ds_count 123 ds_count=$(echo "$datasets" | jq 'length') 124 if [ "$ds_count" -gt 0 ]; then 125 echo "" 126 echo " Datasets:" 127 for i in $(seq 0 $((ds_count - 1))); do 128 local ds_name ds_path ds_cid 129 ds_name=$(echo "$datasets" | jq -r ".[$i].name") 130 ds_path=$(echo "$datasets" | jq -r ".[$i].path") 131 ds_cid=$(echo "$datasets" | jq -r ".[$i].cid // \"no CID\"") 132 local cid_short="${ds_cid:0:16}" 133 [ ${#ds_cid} -gt 16 ] && cid_short="${cid_short}..." 134 echo " $ds_name ($ds_path) — $cid_short" 135 done 136 fi 137 138 echo "" 139 140 # Tape results table. 141 if [ "$tape_count" -gt 0 ]; then 142 printf " %-4s %-9s %-8s %-12s %-10s %s\n" "#" "Commit" "Status" "Metric" "Delta" "Checkpoint" 143 printf " %-4s %-9s %-8s %-12s %-10s %s\n" "---" "-------" "------" "----------" "--------" "----------" 144 145 local baseline_val="" 146 local run_num=0 147 148 for i in $(seq 0 $((tape_count - 1))); do 149 run_num=$((run_num + 1)) 150 local commit status metric description 151 commit=$(echo "$tape_data" | jq -r ".[$i].commit") 152 status=$(echo "$tape_data" | jq -r ".[$i].status") 153 metric=$(echo "$tape_data" | jq -r ".[$i].metric") 154 description=$(echo "$tape_data" | jq -r ".[$i].description // \"\"") 155 156 # Delta calculation. 157 local delta="—" 158 if [ -z "$baseline_val" ] || [ "$baseline_val" = "null" ]; then 159 baseline_val="$metric" 160 delta="baseline" 161 elif [ "$metric" != "0" ] && [ "$metric" != "null" ]; then 162 delta=$(python3 -c " 163 b = float('$baseline_val') 164 m = float('$metric') 165 if b != 0: 166 d = ((m - b) / b) * 100 167 print(f'{d:+.1f}%') 168 else: 169 print('—') 170 " 2>/dev/null || echo "—") 171 fi 172 173 # Check for checkpoint artifact at this commit. 174 local ckpt_indicator="—" 175 local commit_short 176 commit_short=$(short_sha "$commit") 177 178 if [ "$status" = "keep" ]; then 179 # Look in releases for an artifact matching this commit. 180 local has_artifact 181 has_artifact=$(echo "$releases" | jq --arg c "$commit" ' 182 if type == "array" then 183 [.[] | select(.oid // .commit | startswith($c))] | length 184 else 0 end 185 ' 2>/dev/null || echo "0") 186 if [ "$has_artifact" -gt 0 ]; then 187 ckpt_indicator="yes" 188 fi 189 fi 190 191 # Format status with indicator. 192 local status_fmt="$status" 193 194 printf " %-4s %-9s %-8s %-12s %-10s %s\n" \ 195 "$run_num" "$commit_short" "$status_fmt" "$metric" "$delta" "$ckpt_indicator" 196 done 197 elif [ "$exp_count" -gt 0 ]; then 198 info "no local tape — showing published experiments only" 199 echo "" 200 echo "$experiments" | jq -r '.[] | " \(.oid[:7]) \(.description // "—")"' 2>/dev/null 201 else 202 info "no experiments found (local or published)" 203 fi 204 205 echo "" 206 }