/ lib / status.sh
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  }