From 0dad681a5d03228d98828832355238cf3d683a79 Mon Sep 17 00:00:00 2001 From: Joe Saunderson Date: Fri, 10 Jul 2026 13:31:18 +0100 Subject: [PATCH] Fix worker: skill not invoked headlessly; false success Two bugs stopped the sweep actually running: - `--setting-sources ""` disabled skill discovery, so `/babysit-prs` errored "Unknown command". Drop it; the tool allowlist still constrains, and the sweep is a skill so it must be discoverable. - exit 0 was treated as success even when nothing ran. Switch to `--output-format json` and gate success on is_error/subtype/result. Verified: headless dry-run now returns is_error=false, subtype=success, and a real classified sweep summary. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01HjC3rKLYTSqVqf6YG2taPc --- bin/babysit-prs-worker.sh | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/bin/babysit-prs-worker.sh b/bin/babysit-prs-worker.sh index a1bb7ba..9f9586a 100755 --- a/bin/babysit-prs-worker.sh +++ b/bin/babysit-prs-worker.sh @@ -52,8 +52,11 @@ if [[ "$PERMISSION_MODE" == "bypassPermissions" ]]; then log_warn "Running with bypassPermissions (operator opt-in): the approval gate is OFF." permission_args=(--permission-mode bypassPermissions) else - # Explicit allowlist, no settings files loaded. Anything unlisted is denied. - permission_args=(--permission-mode default --setting-sources "" \ + # Explicit allowlist. Do NOT pass --setting-sources "": that disables skill + # discovery, and the sweep itself is a skill (/babysit-prs), so the run would + # no-op with "Unknown command". The allowlist still constrains tools; any + # rules in ~/.claude/settings.json also apply. + permission_args=(--permission-mode default \ --allowedTools "${BABYSIT_ALLOWED_TOOLS[@]}") fi @@ -73,20 +76,37 @@ else fi set +e -"${run_prefix[@]}" \ +output="$("${run_prefix[@]}" \ claude --print \ --session-id "$SESSION_ID" \ "${permission_args[@]}" \ - --output-format text \ - "$PROMPT" + --output-format json \ + "$PROMPT")" exit_code=$? set -e if [[ $exit_code -eq 124 ]]; then log_error "Sweep timed out after ${BABYSIT_RUN_TIMEOUT_SECONDS}s (session ${SESSION_ID})" -elif [[ $exit_code -ne 0 ]]; then + exit 124 +fi + +# claude --print exits 0 even when it did nothing (e.g. an unknown slash +# command), so success is judged from the JSON envelope, not the exit code. +is_error="$(jq -r '.is_error // true' <<<"$output" 2>/dev/null || echo true)" +subtype="$(jq -r '.subtype // "unknown"' <<<"$output" 2>/dev/null || echo unknown)" +result="$(jq -r '.result // ""' <<<"$output" 2>/dev/null || echo "")" + +log_info "Sweep result: ${result}" + +if [[ $exit_code -ne 0 ]]; then log_error "Sweep failed with exit code ${exit_code} (resume: cd ${REPO_ROOT} && claude --resume ${SESSION_ID})" + exit "$exit_code" +elif [[ "$is_error" == "true" || "$subtype" != "success" ]]; then + log_error "Sweep did not complete cleanly (is_error=${is_error}, subtype=${subtype})." + exit 3 +elif [[ "$result" == *"Unknown command"* || -z "$result" ]]; then + log_error "Sweep produced no result; the skill was likely not invoked." + exit 3 else log_success "Sweep finished (session ${SESSION_ID})" fi -exit "$exit_code"