Skip to content
106 changes: 106 additions & 0 deletions bin/fm-nm-run-is-live.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#!/usr/bin/env bash
# fm-nm-run-is-live.sh - check if a specific no-mistakes run is genuinely live (not hung)
#
# Designed for use by fm-crew-state.sh: given a run ID, determine if it's genuinely
# progressing or has silently hung. This is a simpler facade over
# fm-no-mistakes-liveness.sh tuned for single-run checks during supervision.
#
# Usage:
# fm-nm-run-is-live.sh <run-id>
#
# Exit codes:
# 0 - run is live/progressing (or not a run-step that can hang)
# 1 - run is hung (step inactive for > FM_NM_LIVENESS_STALE seconds)
# 2 - usage error or precondition failed
#
set -u

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=bin/fm-no-mistakes-liveness.sh
. "$SCRIPT_DIR/fm-no-mistakes-liveness.sh"

# Check if a run is live (not hung) - simplified version for single-run checks
check_run_liveness() { # <run-id>
local run_id=$1
local run_out outcome awaiting_msg elapsed

run_out=$(nm_run axi status --run "$run_id") || return 2

[ -n "$run_out" ] || return 2

outcome=$(strip_quotes "$(nm_field "$run_out" outcome)")

# Terminal outcomes are not hung
if [ -n "$outcome" ]; then
return 0
fi

# Check if awaiting_agent (parked at gate) - not hung, deliberately waiting
awaiting_msg=$(printf '%s\n' "$run_out" | grep -E '^[[:space:]]*awaiting_agent:' | head -1)
if [ -n "$awaiting_msg" ]; then
return 0
fi

# Check active_steps table first (more accurate than steps table)
local active_steps_section active_step_line last_activity_field
active_steps_section=$(printf '%s\n' "$run_out" | sed -n '/^[[:space:]]*active_steps\[/,/^[^[:space:]]/p')

if [ -n "$active_steps_section" ]; then
active_step_line=$(printf '%s\n' "$active_steps_section" | grep -E '^[[:space:]]*[^,]+,(running|fixing),' | head -1 | sed 's/^[[:space:]]*//')

if [ -n "$active_step_line" ]; then
# Found an active running/fixing step. Extract last_activity.
# Format: step,status,active_for,"time ago: message","pid",round
if [[ "$active_step_line" =~ ^[^,]*,[^,]*,[^,]*,\"([^\"]*) ]]; then
last_activity_field="${BASH_REMATCH[1]}"
if [ -n "$last_activity_field" ]; then
elapsed=$(parse_active_steps_time "$last_activity_field")
if [ "$elapsed" -gt "$STALE_THRESHOLD" ]; then
return 1 # Hung
fi
return 0 # Live
fi
fi
fi
fi

# Fallback: check steps table if no active_steps found
local step_line step_status duration
step_line=$(printf '%s\n' "$run_out" | grep -E '^[[:space:]]*[^,]+,[[:space:]]*"?(running|fixing)"?[[:space:]]*,' | head -1 | sed 's/^ *//')

if [ -z "$step_line" ]; then
# No running/fixing step - not hung
return 0
fi

# Extract step status and duration
step_status="${step_line#*,}"
step_status="${step_status%,*}"
step_status=$(strip_quotes "$(trim "$step_status")")

# Skip findings count (third field) and get duration
local rest="${step_line#*,}"
rest="${rest#*,}"
rest="${rest#*,}"
duration=$(trim "$rest")

case "$step_status" in
running|fixing)
if [ -n "$duration" ] && [[ "$duration" =~ ^[0-9]+$ ]]; then
elapsed=$((duration / 1000))
if [ "$elapsed" -gt "$STALE_THRESHOLD" ]; then
return 1 # Hung
fi
fi
;;
esac

return 0 # Live
}
Comment on lines +23 to +99

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Duplicated check_run_liveness with diverging thresholds. bin/fm-nm-run-is-live.sh sources bin/fm-no-mistakes-liveness.sh and then redefines check_run_liveness. The two copies of the steps-table fallback disagree, so the same run can be reported live by one script and hung by the other. docs/configuration.md line 406 documents FM_NM_LIVENESS_STALE as the hung threshold, and the base copy ignores it.

  • bin/fm-nm-run-is-live.sh#L23-L99: delete this redefinition and call the shared function from bin/fm-no-mistakes-liveness.sh. If the facade needs exit code 2 for a query failure, map the shared function's result instead of reimplementing the decision path.
  • bin/fm-no-mistakes-liveness.sh#L271-L305: replace the hardcoded 1800 with STALE_THRESHOLD, and treat running as hung-eligible in the fallback, so the single shared implementation honors FM_NM_LIVENESS_STALE for both step statuses.
📍 Affects 2 files
  • bin/fm-nm-run-is-live.sh#L23-L99 (this comment)
  • bin/fm-no-mistakes-liveness.sh#L271-L305
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@bin/fm-nm-run-is-live.sh` around lines 23 - 99, Remove the duplicate
check_run_liveness definition in bin/fm-nm-run-is-live.sh lines 23-99 and invoke
the shared check_run_liveness from bin/fm-no-mistakes-liveness.sh, mapping its
result to exit code 2 for query failures if needed. In
bin/fm-no-mistakes-liveness.sh lines 271-305, replace the fallback’s hardcoded
1800 threshold with STALE_THRESHOLD and include running alongside fixing as
hung-eligible, so the shared implementation applies FM_NM_LIVENESS_STALE
consistently.


# --- main

run_id=${1:-}
[ -n "$run_id" ] || { echo "usage: fm-nm-run-is-live.sh <run-id>" >&2; exit 2; }

check_run_liveness "$run_id"
Loading
Loading