Skip to content

fix: validate decision field after extract_verdict_json in single-review loop - #153

Merged
don-petry merged 4 commits into
mainfrom
fix/single-review-empty-decision
May 17, 2026
Merged

don-petry merged 4 commits into
mainfrom
fix/single-review-empty-decision

Conversation

@don-petry

@don-petry don-petry commented May 13, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

Job 75706589647 failed reviewing petry-projects/.github#266 with:

ERROR: invalid decision ''
##[error]Review failed for https://github.com/petry-projects/.github/pull/266 (exit code 1)

Root cause

The claude-opus-4-7 single-review confirmation ran successfully and extract_verdict_json extracted valid JSON — but the JSON had "decision": "" (empty string). This happens when the model writes a partial/placeholder JSON object to $OUTPUT_FILE via a Bash tool call before it has finished reasoning, and extract_verdict_json Style 1 accepts it (only checks jq empty — syntactic validity, not semantic content).

The empty decision then propagated to post-pr-review.sh which correctly rejects it with exit code 1, crashing the whole batch.

Fix

After extract_verdict_json succeeds, validate that .decision is one of approve | escalate | skip before setting single_ok=true. If it is not, fall through to the existing retry logic with a clear diagnostic message. This turns a hard crash into a graceful retry (up to $SINGLE_REVIEW_MAX_RETRIES attempts), matching the behavior for malformed JSON.

Change

scripts/review-one-pr.sh — single-review retry loop (lines 344–356)

-    if [ "$single_rc" -eq 0 ] && extract_verdict_json "$VERDICT_JSON.raw" "$VERDICT_JSON"; then
-      single_ok=true
-      break
-    fi
-
-    echo "    [approve] single-review attempt $single_attempt/$SINGLE_REVIEW_MAX_RETRIES produced no valid JSON (exit $single_rc)"
+    if [ "$single_rc" -eq 0 ] && extract_verdict_json "$VERDICT_JSON.raw" "$VERDICT_JSON"; then
+      EXTRACTED_DECISION=$(jq -r '.decision // ""' "$VERDICT_JSON" 2>/dev/null || true)
+      case "$EXTRACTED_DECISION" in
+        approve|escalate|skip)
+          single_ok=true
+          break
+          ;;
+        *)
+          echo "    [approve] single-review attempt $single_attempt/$SINGLE_REVIEW_MAX_RETRIES produced invalid decision '$EXTRACTED_DECISION' — will retry"
+          ;;
+      esac
+    else
+      echo "    [approve] single-review attempt $single_attempt/$SINGLE_REVIEW_MAX_RETRIES produced no valid JSON (exit $single_rc)"
+    fi

Summary by CodeRabbit

  • Bug Fixes
    • Improved automated review validation: decision values are now explicitly checked against allowed outcomes (approve, escalate, skip) before being accepted. Non-allowed decisions trigger retries up to the retry limit, reducing false positives from malformed or unexpected responses and ensuring only verified verdicts are treated as successful.

Review Change Stack

Copilot AI review requested due to automatic review settings May 13, 2026 02:58
@coderabbitai

coderabbitai Bot commented May 13, 2026 •

Copy link
Copy Markdown

Warning

Rate limit exceeded

@don-petry has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 25 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: caa655f6-751d-4d73-9903-4b9f0836fe89

📥 Commits

Reviewing files that changed from the base of the PR and between 704c60a and c09cfbd.

📒 Files selected for processing (1)
  • scripts/review-one-pr.sh
📝 Walkthrough

Walkthrough

The triage-approved single-review flow now validates the extracted verdict's decision against an allowlist of approve, escalate, or skip before accepting the attempt, retrying on invalid decisions.

Changes

Verdict Decision Validation

Layer / File(s) Summary
Decision allowlist validation in retry loop
scripts/review-one-pr.sh
The triage-approved confirmation loop extracts .decision from the parsed verdict and explicitly validates it against approve|escalate|skip; non-matching decisions trigger retries instead of immediate acceptance.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Possibly related PRs

  • petry-projects/.github-private#133: Both PRs modify scripts/review-one-pr.sh's single-review flow around extract_verdict_json, with the retrieved PR adding bounded retries and this PR adding allowlist validation for the parsed verdict's .decision.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately describes the main change: validating the decision field after JSON extraction in the single-review loop, which matches the core modification in scripts/review-one-pr.sh.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/single-review-empty-decision

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request updates the PR review script to validate the 'decision' field from the JSON output, ensuring it is one of the allowed values before proceeding. The reviewer suggested trimming whitespace from the extracted decision for better robustness and adding rate-limit detection to handle engine fallbacks correctly.

Comment thread scripts/review-one-pr.sh
if [ "$single_rc" -eq 0 ] && extract_verdict_json "$VERDICT_JSON.raw" "$VERDICT_JSON"; then
single_ok=true
break
EXTRACTED_DECISION=$(jq -r '.decision // ""' "$VERDICT_JSON" 2>/dev/null || true)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

To improve robustness against potential LLM formatting variability (such as unexpected leading or trailing whitespace in the JSON string), it is recommended to trim the extracted decision value before validation. This ensures that values like " approve" are correctly matched by the case statement.

Additionally, since this script implements a retry loop for LLM-based tasks, ensure that rate limits are detected immediately by checking exit codes and scanning output for rate-limit indicators. Per repository standards, if a rate limit is detected, the script should exit with code 2 to enable engine fallback mechanisms in the calling process.

For a more complete fix, consider centralizing this validation, sanitization, and rate-limit detection within the extract_verdict_json function in scripts/engine.sh so that all tiers benefit.

Suggested change
EXTRACTED_DECISION=$(jq -r '.decision // ""' "$VERDICT_JSON" 2>/dev/null || true)
EXTRACTED_DECISION=$(jq -r '.decision // ""' "$VERDICT_JSON" 2>/dev/null | tr -d '[:space:]' || true)
References
  1. In shell scripts implementing retry loops for LLM-based tasks, detect rate limits immediately and exit with code 2 to enable engine fallback mechanisms.

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 13, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens the single-review confirmation path in scripts/review-one-pr.sh by adding semantic validation of the extracted verdict JSON. It prevents batch failures when extract_verdict_json successfully extracts syntactically valid JSON that still contains an empty/invalid "decision" field (e.g., from a premature tool-written placeholder), converting what was previously a hard crash downstream into an in-loop retry.

Changes:

  • After extract_verdict_json succeeds, parse .decision and validate it is one of approve | escalate | skip.
  • If .decision is invalid/empty, emit a clear diagnostic and fall through to the existing retry behavior.
  • Preserve the existing “no valid JSON” handling path for malformed/unextractable output.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

donpetry-bot
donpetry-bot previously approved these changes May 14, 2026

@donpetry-bot donpetry-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated review — APPROVED ✓

Risk: LOW
Reviewed commit: 704c60a4cc12374e03c171ae3c461a7096944063
Review mode: triage-approved (single reviewer)

Summary

This PR hardens the triage-approved single-review retry loop in scripts/review-one-pr.sh by validating that the .decision field extracted from the verdict JSON is one of approve|escalate|skip before accepting the attempt. Previously, extract_verdict_json only checked syntactic validity (jq empty), so a model emitting partial/placeholder JSON with "decision": "" would slip through and crash post-pr-review.sh with ERROR: invalid decision '' (exit 1), failing the entire batch. The fix converts that hard crash into a graceful retry up to $SINGLE_REVIEW_MAX_RETRIES, matching how malformed JSON is already handled.

Linked issue analysis

No issue is linked, but the PR body cites the concrete reproducer: job 75706589647 failing on combsmith/.github#266. The root cause description (Bash-tool partial write of the verdict JSON before the model finishes reasoning) matches the code path being patched, and the diff directly addresses that gap — adding a semantic check after the existing syntactic one.

Findings

  • Correctness: The case statement covers exactly approve|escalate|skip (the values post-pr-review.sh accepts), and the wildcard *) path emits a diagnostic without setting single_ok=true, so the loop continues to its existing retry/diagnostic block and either retries or fails after $SINGLE_REVIEW_MAX_RETRIES. The restructured if/else correctly distinguishes "extraction succeeded but decision invalid" from "extraction itself failed," preserving the prior diagnostic for the latter.
  • Risk surface: Diff is 12 additions / 4 deletions in a single bash file on a non-hot path (the retry loop runs only after triage decides to confirm). No auth, secrets, migrations, schema, dependencies, or GitHub Actions security surface are touched.
  • Minor (non-blocking) observations: Gemini's drive-by suggestions (trim whitespace on the extracted decision; reuse rate-limit detection) are nice-to-haves but not required — jq -r strips surrounding JSON quoting, and rate-limit handling already runs earlier at lines 354–358 before this block, so it is unaffected. The 2>/dev/null || true on the jq call is appropriate defensive belt-and-suspenders since the JSON was already validated by extract_verdict_json.

CI status

All green: CodeQL Analyze (actions) SUCCESS, CodeQL SUCCESS, CodeRabbit SUCCESS, SonarQube quality gate passed (0 new issues). CodeRabbit submitted an APPROVED review on the head commit 704c60a4.


Reviewed automatically by the PR-review agent (single-reviewer mode: opus 4.7). Reply if you need a human review.

@donpetry-bot
donpetry-bot enabled auto-merge (squash) May 14, 2026 04:45
donpetry-bot
donpetry-bot previously approved these changes May 14, 2026

@donpetry-bot donpetry-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated review — APPROVED ✓

Risk: LOW
Reviewed commit: 5b6140f6640cc55207379bcd5be3a20c4ca7d0b3
Review mode: triage-approved (single reviewer)

Summary

Confirmation review of a small (+12/-4) hardening change in scripts/review-one-pr.sh. After extract_verdict_json succeeds, the retry loop now extracts .decision and validates it against the allowlist approve|escalate|skip before setting single_ok=true; non-allowlisted values (including the empty string that triggered the original incident on combsmith/.github#266) fall through to the existing retry with a clear diagnostic instead of crashing post-pr-review.sh with ERROR: invalid decision ''. This PR was already approved at SHA 704c60a4 by donpetry-bot; the new commits since then are merges from main only, so the substantive change is unchanged.

Linked issue analysis

No linked issue is declared, but the PR body cites a concrete reproducer (job 75706589647, PR combsmith/.github#266) and the root cause matches the patched code path — a model writing partial JSON via the Bash tool before reasoning completes. The fix directly closes that gap by adding the semantic check the previous code lacked.

Findings

  • Correctness: The case allowlist exactly matches the values post-pr-review.sh accepts. The wildcard *) branch correctly emits a diagnostic without setting single_ok=true, so control falls through to the existing retry/bail block. The restructured if/else properly distinguishes "extraction succeeded but decision invalid" from "extraction itself failed," preserving the previous diagnostic message in the latter case.
  • Delta since prior review: git compare 704c60a4...5b6140f6 shows only merge-from-main commits (dependabot bumps for reusable workflows, .gitignore push-protection baseline, code-quality ruleset). None touch scripts/review-one-pr.sh. The full PR diff is identical to what was approved at 704c60a.
  • Risk surface: A single bash file on a non-hot path (only runs after triage decides to confirm). No auth, secrets, migrations, schema, dependencies, or Actions security surface touched.
  • Minor (non-blocking): Gemini's drive-by suggestions (whitespace trim, rate-limit detection) are not required — jq -r strips JSON quoting, and rate-limit handling runs earlier in the loop and is unaffected by this block.

CI status

All green on 5b6140f6: CodeQL Analyze (actions) SUCCESS, CodeQL SUCCESS, CodeRabbit SUCCESS (APPROVED), agent-shield SUCCESS, dependency-audit SUCCESS, SonarCloud SUCCESS (Quality Gate passed, 0 new issues), unit-tests SUCCESS. Skipped checks are conditional and expected.


Reviewed automatically by the PR-review agent (single-reviewer mode: opus 4.7). Reply if you need a human review.

@donpetry-bot
donpetry-bot dismissed their stale review May 14, 2026 20:16

Superseded by automated re-review at 5b6140f.

donpetry-bot
donpetry-bot previously approved these changes May 14, 2026

@donpetry-bot donpetry-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated review — APPROVED ✓

Risk: LOW
Reviewed commit: 2059a3ce81d5be44ac203cb7d3826ca5fe910f9f
Review mode: triage-approved (single reviewer)

Summary

Small defensive fix in scripts/review-one-pr.sh (+12/-4, single file). After extract_verdict_json succeeds, the triage-approved single-review loop now validates .decision against the allowlist approve|escalate|skip before accepting the attempt; an empty/invalid decision falls through to the existing retry path with a clear diagnostic instead of crashing the batch downstream in post-pr-review.sh. Triage classified this PR as low-risk; confirming.

Linked issue analysis

No linked issue, which is acceptable here — the PR body cites the concrete failed job (run 25775325448, job 75706589647) and the exact ERROR: invalid decision '' symptom, then walks through the root cause (model wrote a partial JSON object that was syntactically valid but had decision: ""). The diff matches the description exactly.

Findings

  • Code change matches description. The diff applied is exactly the one shown in the PR body. Logic: case on EXTRACTED_DECISION with approve|escalate|skip → single_ok=true; break; everything else logs and continues the retry loop. jq -r '.decision // ""' with 2>/dev/null || true safely tolerates missing/non-object JSON.
  • No new risk surface. Pure validation; no new external calls, no secrets handling, no permission/auth changes.
  • Incremental delta since prior approval (5b6140f) is benign. Only merge commits from main bringing in .github/workflows/pr-review.yml changes (alias GEMINI_API_KEY from GOOGLE_API_KEY, raise CANDIDATE_LIMIT 100→250). The scripts/review-one-pr.sh change itself has not changed since the prior approval.
  • No unresolved human-reviewer threads. CodeRabbit approved with no actionable comments; gemini-code-assist and Copilot left informational comments only.

CI status

All required checks pass: agent-shield, claude-code / claude, Analyze (actions), dependency-audit / Detect ecosystems, SonarCloud, unit-tests, CodeQL, CodeRabbit, SonarCloud Code Analysis. Skipped checks (dependabot-automerge, conditional claude-code jobs, language-specific dep audits) are expected for this change set. mergeable: MERGEABLE; mergeStateStatus: BLOCKED is the standard required-review gate this approval is intended to satisfy.


Reviewed automatically by the PR-review agent (claude-opus-4-7). Reply if you need a human review.

@donpetry-bot
donpetry-bot dismissed their stale review May 14, 2026 20:38

Superseded by automated re-review at 2059a3c.

@don-petry
don-petry force-pushed the fix/single-review-empty-decision branch 3 times, most recently from 041751a to 1a0a357 Compare May 15, 2026 16:53
@don-petry
don-petry dismissed stale reviews from coderabbitai[bot] and donpetry-bot May 16, 2026 19:15

The merge-base changed after approval.

@don-petry

Copy link
Copy Markdown
Collaborator Author

Auto-rebase failed — merge conflict — this branch has conflicts with main that must be resolved.

Claude will attempt to resolve this automatically. If it cannot, a follow-up comment will explain what needs manual attention.

To resolve manually instead:

git fetch origin
git merge origin/main
# resolve conflicts, then:
git add .
git commit
git push

@don-petry

Copy link
Copy Markdown
Collaborator Author

Auto-rebase failed — merge conflict — this branch has conflicts with main that must be resolved.

Claude will attempt to resolve this automatically. If it cannot, a follow-up comment will explain what needs manual attention.

To resolve manually instead:

git fetch origin
git merge origin/main
# resolve conflicts, then:
git add .
git commit
git push

@don-petry

Copy link
Copy Markdown
Collaborator Author

Auto-rebase failed — merge conflict — this branch has conflicts with main that must be resolved.

Claude will attempt to resolve this automatically. If it cannot, a follow-up comment will explain what needs manual attention.

To resolve manually instead:

git fetch origin
git merge origin/main
# resolve conflicts, then:
git add .
git commit
git push

@don-petry

Copy link
Copy Markdown
Collaborator Author

Auto-rebase failed — merge conflict — this branch has conflicts with main that must be resolved.

Claude will attempt to resolve this automatically. If it cannot, a follow-up comment will explain what needs manual attention.

To resolve manually instead:

git fetch origin
git merge origin/main
# resolve conflicts, then:
git add .
git commit
git push

@don-petry

Copy link
Copy Markdown
Collaborator Author

Auto-rebase failed — merge conflict — this branch has conflicts with main that must be resolved.

Claude will attempt to resolve this automatically. If it cannot, a follow-up comment will explain what needs manual attention.

To resolve manually instead:

git fetch origin
git merge origin/main
# resolve conflicts, then:
git add .
git commit
git push

@don-petry

Copy link
Copy Markdown
Collaborator Author

Auto-rebase failed — merge conflict — this branch has conflicts with main that must be resolved.

Claude will attempt to resolve this automatically. If it cannot, a follow-up comment will explain what needs manual attention.

To resolve manually instead:

git fetch origin
git merge origin/main
# resolve conflicts, then:
git add .
git commit
git push

@don-petry

Copy link
Copy Markdown
Collaborator Author

Auto-rebase failed — merge conflict — this branch has conflicts with main that must be resolved.

Claude will attempt to resolve this automatically. If it cannot, a follow-up comment will explain what needs manual attention.

To resolve manually instead:

git fetch origin
git merge origin/main
# resolve conflicts, then:
git add .
git commit
git push

@don-petry

Copy link
Copy Markdown
Collaborator Author

Auto-rebase failed — merge conflict — this branch has conflicts with main that must be resolved.

Claude will attempt to resolve this automatically. If it cannot, a follow-up comment will explain what needs manual attention.

To resolve manually instead:

git fetch origin
git merge origin/main
# resolve conflicts, then:
git add .
git commit
git push

@don-petry

Copy link
Copy Markdown
Collaborator Author

Auto-rebase failed — merge conflict — this branch has conflicts with main that must be resolved.

Claude will attempt to resolve this automatically. If it cannot, a follow-up comment will explain what needs manual attention.

To resolve manually instead:

git fetch origin
git merge origin/main
# resolve conflicts, then:
git add .
git commit
git push

@don-petry

Copy link
Copy Markdown
Collaborator Author

Auto-rebase failed — merge conflict — this branch has conflicts with main that must be resolved.

Claude will attempt to resolve this automatically. If it cannot, a follow-up comment will explain what needs manual attention.

To resolve manually instead:

git fetch origin
git merge origin/main
# resolve conflicts, then:
git add .
git commit
git push

@don-petry

Copy link
Copy Markdown
Collaborator Author

Auto-rebase failed — merge conflict — this branch has conflicts with main that must be resolved.

Claude will attempt to resolve this automatically. If it cannot, a follow-up comment will explain what needs manual attention.

To resolve manually instead:

git fetch origin
git merge origin/main
# resolve conflicts, then:
git add .
git commit
git push

…iew retry loop

The claude-opus-4-7 single-review confirmation could return valid JSON
with an empty 'decision' field (e.g. when the model writes a partial JSON
to $OUTPUT_FILE via Bash tool before completing). extract_verdict_json()
only checked that the JSON was syntactically valid (jq empty), not that
'decision' was a recognized value. This caused review-batch.sh to pass
the bad verdict to post-pr-review.sh which then hard-failed with
'ERROR: invalid decision ''' (exit code 1).

Fix: after extract_verdict_json succeeds, check that decision is one of
approve|escalate|skip before setting single_ok=true. If the value is
anything else, fall through to the retry logic with a clear diagnostic
message instead of crashing the entire batch.

Reproducer: job/75706589647 — PR combsmith/.github#266 failed
with 'ERROR: invalid decision ''' from post-pr-review.sh.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@don-petry

Copy link
Copy Markdown
Collaborator Author

Auto-rebase failed — merge conflict — this branch has conflicts with main that must be resolved.

Claude will attempt to resolve this automatically. If it cannot, a follow-up comment will explain what needs manual attention.

To resolve manually instead:

git fetch origin
git merge origin/main
# resolve conflicts, then:
git add .
git commit
git push

@don-petry
don-petry force-pushed the fix/single-review-empty-decision branch from f24787a to d50fc7a Compare May 16, 2026 23:02
@don-petry

Copy link
Copy Markdown
Collaborator Author

No description provided.

@donpetry-bot

Copy link
Copy Markdown
Contributor

Automated review — human attention needed

This PR has been through 3 automated review cycles (cap: 3) without converging on an approval-and-merge state. Further automated review has been paused to avoid infinite loops.

Please take a look manually, or close this PR if it's no longer needed. Once a human review resolves the situation, remove the needs-human-review label and the cascade can be re-engaged on the next push.

Posted by the donpetry-bot PR-review cascade.

@donpetry-bot donpetry-bot added the needs-human-review Flagged by automated PR review agent label May 16, 2026
@don-petry

Copy link
Copy Markdown
Collaborator Author

No description provided.

@don-petry

Copy link
Copy Markdown
Collaborator Author

No description provided.

@don-petry

Copy link
Copy Markdown
Collaborator Author

No description provided.

@sonarqubecloud

Copy link
Copy Markdown

@don-petry
don-petry merged commit 8aed3df into main May 17, 2026
18 checks passed
@don-petry
don-petry deleted the fix/single-review-empty-decision branch May 17, 2026 14:14
don-petry added a commit that referenced this pull request Jun 7, 2026
…iew retry loop (#153)

The claude-opus-4-7 single-review confirmation could return valid JSON
with an empty 'decision' field (e.g. when the model writes a partial JSON
to $OUTPUT_FILE via Bash tool before completing). extract_verdict_json()
only checked that the JSON was syntactically valid (jq empty), not that
'decision' was a recognized value. This caused review-batch.sh to pass
the bad verdict to post-pr-review.sh which then hard-failed with
'ERROR: invalid decision ''' (exit code 1).

Fix: after extract_verdict_json succeeds, check that decision is one of
approve|escalate|skip before setting single_ok=true. If the value is
anything else, fall through to the retry logic with a clear diagnostic
message instead of crashing the entire batch.

Reproducer: job/75706589647 — PR combsmith/.github#266 failed
with 'ERROR: invalid decision ''' from post-pr-review.sh.

Co-authored-by: don-petry <don@petry.dev>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
don-petry added a commit that referenced this pull request Jun 7, 2026
…iew retry loop (#153)

The claude-opus-4-7 single-review confirmation could return valid JSON
with an empty 'decision' field (e.g. when the model writes a partial JSON
to $OUTPUT_FILE via Bash tool before completing). extract_verdict_json()
only checked that the JSON was syntactically valid (jq empty), not that
'decision' was a recognized value. This caused review-batch.sh to pass
the bad verdict to post-pr-review.sh which then hard-failed with
'ERROR: invalid decision ''' (exit code 1).

Fix: after extract_verdict_json succeeds, check that decision is one of
approve|escalate|skip before setting single_ok=true. If the value is
anything else, fall through to the retry logic with a clear diagnostic
message instead of crashing the entire batch.

Reproducer: job/75706589647 — PR combsmith/.github#266 failed
with 'ERROR: invalid decision ''' from post-pr-review.sh.

Co-authored-by: don-petry <don@petry.dev>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
don-petry added a commit that referenced this pull request Jun 12, 2026
…iew retry loop (#153)

The claude-opus-4-7 single-review confirmation could return valid JSON
with an empty 'decision' field (e.g. when the model writes a partial JSON
to $OUTPUT_FILE via Bash tool before completing). extract_verdict_json()
only checked that the JSON was syntactically valid (jq empty), not that
'decision' was a recognized value. This caused review-batch.sh to pass
the bad verdict to post-pr-review.sh which then hard-failed with
'ERROR: invalid decision ''' (exit code 1).

Fix: after extract_verdict_json succeeds, check that decision is one of
approve|escalate|skip before setting single_ok=true. If the value is
anything else, fall through to the retry logic with a clear diagnostic
message instead of crashing the entire batch.

Reproducer: job/75706589647 — PR combsmith/.github#266 failed
with 'ERROR: invalid decision ''' from post-pr-review.sh.

Co-authored-by: don-petry <don@petry.dev>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
don-petry added a commit that referenced this pull request Jun 14, 2026
…iew retry loop (#153)

The claude-opus-4-7 single-review confirmation could return valid JSON
with an empty 'decision' field (e.g. when the model writes a partial JSON
to $OUTPUT_FILE via Bash tool before completing). extract_verdict_json()
only checked that the JSON was syntactically valid (jq empty), not that
'decision' was a recognized value. This caused review-batch.sh to pass
the bad verdict to post-pr-review.sh which then hard-failed with
'ERROR: invalid decision ''' (exit code 1).

Fix: after extract_verdict_json succeeds, check that decision is one of
approve|escalate|skip before setting single_ok=true. If the value is
anything else, fall through to the retry logic with a clear diagnostic
message instead of crashing the entire batch.

Reproducer: job/75706589647 — PR combsmith/.github#266 failed
with 'ERROR: invalid decision ''' from post-pr-review.sh.

Co-authored-by: don-petry <don@petry.dev>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-human-review Flagged by automated PR review agent

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants