feat: live status-board comment — per-model/per-lens review progress
Phase 3 of the gadfly-games build. With several models × several lenses reviewing a PR, all you'd see mid-run is a row of "⏳ Reviewing…" placeholders. Add ONE consolidated, live-updating status-board comment that aggregates every model's per-lens progress (queued → running → finished + verdict), so progress is visible at a glance and a watcher can tell when the whole swarm is done. - cmd/gadfly: opt-in statusWriter (GADFLY_STATUS_FILE) publishes this model's lenses to a JSON file, written atomically (temp+rename) as runSpecialists transitions each lens. Inert when unset — plain runs and tests are unaffected. - scripts/status-board.sh: background renderer that polls the status dir and upserts one marker comment every GADFLY_STATUS_POLL_SECS (default 12s), caching the comment id to PATCH in place. Advisory and best-effort; the per-model findings comments are untouched. - entrypoint.sh: pre-seeds every model as queued, launches the board, waits only on the review lanes, then signals .done for a final render. Default on; disable with GADFLY_STATUS_BOARD=0. - Docs: README config table + "Live status board" section, example stub note, CLAUDE.md architecture map. gofmt clean, go vet quiet, go build + go test -race green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+45
-2
@@ -155,6 +155,12 @@ DEFAULT_CONC="${GADFLY_CONCURRENCY:-1}"
|
||||
|
||||
provider_of() { case "$1" in */*) echo "${1%%/*}";; *) echo "${GADFLY_PROVIDER:-ollama-cloud}";; esac; }
|
||||
|
||||
# Per-model status file path for the live board. The model id can contain '/'
|
||||
# and ':' (e.g. m1/qwen3:14b), so sanitize to a flat filename; the JSON inside
|
||||
# carries the real model/provider, so this just needs to be unique per model.
|
||||
STATUS_DIR="${WORKDIR}/status"
|
||||
status_file_for() { echo "${STATUS_DIR}/$(echo "$1" | tr -c '[:alnum:]._-' '_').json"; }
|
||||
|
||||
provider_cap() { # provider -> concurrency (override map "p=N,...", else default)
|
||||
local p="$1" item k v
|
||||
IFS=',' read -ra _caps <<< "${GADFLY_PROVIDER_CONCURRENCY:-}"
|
||||
@@ -167,7 +173,10 @@ provider_cap() { # provider -> concurrency (override map "p=N,...", else default
|
||||
}
|
||||
|
||||
review_one() {
|
||||
local sf=""
|
||||
[ "${GADFLY_STATUS_BOARD:-1}" != "0" ] && sf="$(status_file_for "$1")"
|
||||
PROVIDER=ollama MODEL="$1" GADFLY_BIN="/usr/local/bin/gadfly" GADFLY_REPO_DIR="$REPO_DIR" \
|
||||
GADFLY_STATUS_FILE="$sf" \
|
||||
bash "${SCRIPTS_DIR}/run.sh" || log "model $1 failed (continuing)"
|
||||
}
|
||||
|
||||
@@ -197,10 +206,44 @@ run_lane() { # $1=provider: run its models, at most `cap` at a time
|
||||
wait
|
||||
}
|
||||
|
||||
# --- live status board (optional, default on) ------------------------------
|
||||
# Each model process publishes per-lens progress to STATUS_DIR/<model>.json; a
|
||||
# background renderer (status-board.sh) upserts ONE consolidated PR comment so
|
||||
# progress across all models/lenses is visible at a glance — and a watcher can
|
||||
# tell when the whole swarm is finished. Advisory/best-effort; the per-model
|
||||
# findings still land in each model's own comment. Disable with
|
||||
# GADFLY_STATUS_BOARD=0.
|
||||
BOARD_PID=""
|
||||
if [ "${GADFLY_STATUS_BOARD:-1}" != "0" ]; then
|
||||
rm -rf "$STATUS_DIR"; mkdir -p "$STATUS_DIR"
|
||||
# Pre-seed every model as queued so the board shows the full swarm from t=0,
|
||||
# even models still waiting on their provider lane's concurrency cap. Each
|
||||
# binary overwrites its own file with real per-lens detail once it starts.
|
||||
for m in "${MODEL_LIST[@]}"; do
|
||||
jq -n --arg model "$m" --arg provider "$(provider_of "$m")" \
|
||||
'{model:$model, provider:$provider, started:0, updated:0, done:false, lenses:[]}' \
|
||||
> "$(status_file_for "$m")" 2>/dev/null || true
|
||||
done
|
||||
GITEA_API="$GITEA_API" GITEA_TOKEN="$GITEA_TOKEN" PR="$PR" GADFLY_STATUS_DIR="$STATUS_DIR" \
|
||||
bash "${SCRIPTS_DIR}/status-board.sh" &
|
||||
BOARD_PID=$!
|
||||
log "status board started (pid ${BOARD_PID})"
|
||||
fi
|
||||
|
||||
log "providers: ${PROVIDERS:-none}"
|
||||
# Each provider lane runs in parallel; cap is enforced within each lane.
|
||||
# Each provider lane runs in parallel; cap is enforced within each lane. Track
|
||||
# the lane PIDs so we wait ONLY for the review work — not the status board,
|
||||
# which intentionally runs until we signal it below.
|
||||
LANE_PIDS=()
|
||||
for p in $PROVIDERS; do
|
||||
run_lane "$p" &
|
||||
LANE_PIDS+=("$!")
|
||||
done
|
||||
wait
|
||||
[ "${#LANE_PIDS[@]}" -gt 0 ] && wait "${LANE_PIDS[@]}"
|
||||
|
||||
# Reviews are done: signal the board to render the final state once and exit.
|
||||
if [ -n "$BOARD_PID" ]; then
|
||||
touch "${STATUS_DIR}/.done" 2>/dev/null || true
|
||||
wait "$BOARD_PID" 2>/dev/null || true
|
||||
fi
|
||||
log "done"
|
||||
|
||||
Reference in New Issue
Block a user