feat: live status-board comment — per-model/per-lens review progress
Build & push image / build-and-push (pull_request) Successful in 6s
Adversarial Review (Gadfly) / review (pull_request) Successful in 30m1s

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:
2026-06-27 14:18:28 -04:00
parent 0ad5b66170
commit 1cdda32dbc
10 changed files with 457 additions and 5 deletions
+45 -2
View File
@@ -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"