diff --git a/docs/runbooks/FLEET_DEVIN_LOCAL_RUN.md b/docs/runbooks/FLEET_DEVIN_LOCAL_RUN.md index a0ed0bc8..cd4b36f5 100644 --- a/docs/runbooks/FLEET_DEVIN_LOCAL_RUN.md +++ b/docs/runbooks/FLEET_DEVIN_LOCAL_RUN.md @@ -356,8 +356,22 @@ it **stops at the human merge gate**. ## 8. Observe progress +- **Factory/agent logs (the live Devin transcript):** use the helper + `scripts/fleet-logs.sh` (auto-finds the agent-queue logs; takes a full or partial job id, + defaults to the newest job): + ```bash + scripts/fleet-logs.sh ls # list jobs: slot + step count + scripts/fleet-logs.sh status 3c0586ce # steps count + slot + latest step + scripts/fleet-logs.sh steps 3c0586ce 20 # last 20 transcript steps + scripts/fleet-logs.sh watch 3c0586ce # live-refresh the tail + scripts/fleet-logs.sh tail 3c0586ce # follow the runner lifecycle .log + scripts/fleet-logs.sh full 3c0586ce # all agent messages in your pager + ``` + (Override the factory location with `AQ=/path/to/agent-queue`. Needs `jq` for the + transcript commands.) - **tracker-web:** `http://localhost:3003/dashboard/fleet/jobs/` — live event - stream (SSE), runs, PR link + state. + stream (SSE), runs, PR link + state. (Select the job's **product** in the UI first, or + it shows "job does not exist" — every call is scoped by `X-Product-Id`.) - **Events/API:** ```bash curl -s http://localhost:4003/api/fleet/jobs//events \ diff --git a/scripts/fleet-logs.sh b/scripts/fleet-logs.sh new file mode 100755 index 00000000..8290430c --- /dev/null +++ b/scripts/fleet-logs.sh @@ -0,0 +1,141 @@ +#!/usr/bin/env bash +# +# fleet-logs.sh — quickly inspect a fleet factory job's logs (agent-queue). +# +# The factory (learning_ai_devops_tools/agent-queue) writes two artifacts per job +# under queue/logs/: +# • .log — sparse runner lifecycle (launch, PR open, verify, errors) +# • .devin-export.json — the live Devin transcript (steps[]: source + message) +# where looks like 20260602-003202__fleet-. +# +# Usage: +# scripts/fleet-logs.sh [command] [job] +# +# Commands: +# status [job] steps count + slot (building/review/...) + latest step (default) +# tail [job] follow the runner .log (-f) +# steps [job] [N] last N transcript steps (default 12) +# watch [job] [N] live-refresh the last N steps every few seconds +# full [job] all agent messages through your pager ($PAGER/less) +# path [job] print the resolved log file paths +# ls list known jobs (newest first) with slot + step count +# +# `job` is a full or PARTIAL job id (e.g. `3c0586ce`). Omit it to use the most +# recent job. Override the factory location with AQ=/path/to/agent-queue. +# +set -euo pipefail + +# ── Locate the agent-queue checkout ─────────────────────────────────────────── +resolve_aq() { + if [[ -n "${AQ:-}" ]]; then printf '%s' "$AQ"; return; fi + local here parent + here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + parent="$(cd "$here/../.." 2>/dev/null && pwd || true)" # …/ + for cand in \ + "$parent/learning_ai_devops_tools/agent-queue" \ + "$HOME/code/mygh/learning_ai_devops_tools/agent-queue" \ + "$HOME/code/learning_ai_devops_tools/agent-queue"; do + [[ -d "$cand/queue/logs" ]] && { printf '%s' "$cand"; return; } + done + return 1 +} + +AQ_DIR="$(resolve_aq || true)" +if [[ -z "$AQ_DIR" || ! -d "$AQ_DIR/queue/logs" ]]; then + echo "error: agent-queue logs dir not found. Set AQ=/path/to/agent-queue." >&2 + exit 1 +fi +LOGDIR="$AQ_DIR/queue/logs" +QUEUE="$AQ_DIR/queue" + +have_jq() { command -v jq >/dev/null 2>&1; } +need_jq() { have_jq || { echo "error: 'jq' is required for this command (brew install jq)." >&2; exit 1; }; } + +# ── Resolve a job's export file by (optional) substring; default = newest ────── +resolve_export() { + local pat="${1:-}" f + if [[ -z "$pat" ]]; then + f="$(ls -t "$LOGDIR"/*.devin-export.json 2>/dev/null | head -1 || true)" + else + f="$(ls -t "$LOGDIR"/*"$pat"*.devin-export.json 2>/dev/null | head -1 || true)" + fi + [[ -n "$f" ]] || { echo "error: no transcript matching '${pat:-}' in $LOGDIR" >&2; return 1; } + printf '%s' "$f" +} + +base_of() { basename "$1" .devin-export.json; } # 20260602-…__fleet- +runner_log() { printf '%s/%s.log' "$LOGDIR" "$(base_of "$1")"; } + +slot_of() { + local base="$1" d + for d in building review testing shipped failed inbox; do + [[ -e "$QUEUE/$d/$base.md" ]] && { printf '%s' "$d"; return; } + done + printf 'unknown' +} + +steps_count() { have_jq && jq '.steps|length' "$1" 2>/dev/null || echo '?'; } + +print_steps() { + local exp="$1" n="${2:-12}" + need_jq + jq -r --argjson n "$n" '.steps[-$n:][] | "[\(.source)] \(.message[0:300])"' "$exp" 2>/dev/null \ + || echo "(transcript mid-write — re-run)" +} + +cmd="${1:-status}"; shift || true + +case "$cmd" in + ls) + printf '%-40s %-10s %-7s %s\n' "JOB (base)" "SLOT" "STEPS" "FILE" + ls -t "$LOGDIR"/*.devin-export.json 2>/dev/null | while read -r f; do + b="$(base_of "$f")" + printf '%-40s %-10s %-7s %s\n' "$b" "$(slot_of "$b")" "$(steps_count "$f")" "$f" + done + ;; + path) + exp="$(resolve_export "${1:-}")" + echo "transcript: $exp" + echo "runner log: $(runner_log "$exp")" + ;; + status) + exp="$(resolve_export "${1:-}")"; base="$(base_of "$exp")" + echo "job: $base" + echo "slot: $(slot_of "$base")" + echo "steps: $(steps_count "$exp")" + echo "transcript: $exp" + echo "--- latest step ---" + print_steps "$exp" 3 + ;; + tail) + exp="$(resolve_export "${1:-}")"; log="$(runner_log "$exp")" + echo "==> tail -f $log (Ctrl-C to stop)" + tail -n +1 -f "$log" + ;; + steps) + exp="$(resolve_export "${1:-}")" + print_steps "$exp" "${2:-12}" + ;; + watch) + exp="$(resolve_export "${1:-}")"; n="${2:-8}" + echo "==> live last $n steps of $(base_of "$exp") (Ctrl-C to stop)" + while true; do + clear 2>/dev/null || true + printf '== %s | slot=%s | steps=%s | %s ==\n\n' \ + "$(base_of "$exp")" "$(slot_of "$(base_of "$exp")")" "$(steps_count "$exp")" "$(date +%T)" + print_steps "$exp" "$n" + sleep 3 + done + ;; + full) + exp="$(resolve_export "${1:-}")"; need_jq + jq -r '.steps[] | "== \(.source) ==\n\(.message)\n"' "$exp" | "${PAGER:-less}" + ;; + -h|--help|help) + sed -n '2,33p' "$0" | sed 's/^# \{0,1\}//' + ;; + *) + echo "unknown command '$cmd' (try: status|tail|steps|watch|full|path|ls; -h for help)" >&2 + exit 1 + ;; +esac