From d0dcd5054daf212044def8bb1d5b6c5d2420718e Mon Sep 17 00:00:00 2001 From: Tim Johns Date: Fri, 24 Jul 2026 19:20:09 -0700 Subject: [PATCH 1/5] Make claude-review tolerant of missing structured_output Co-Authored-By: Claude Opus 4.8 --- .github/workflows/dependabot-auto-approve.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dependabot-auto-approve.yml b/.github/workflows/dependabot-auto-approve.yml index a8c6b1a..6a34f27 100644 --- a/.github/workflows/dependabot-auto-approve.yml +++ b/.github/workflows/dependabot-auto-approve.yml @@ -218,6 +218,7 @@ jobs: compat-lookup: true - name: Claude compatibility assessment id: analyze + continue-on-error: true if: >- ${{ steps.dependabot-metadata.outputs.update-type == 'version-update:semver-major' || @@ -268,9 +269,19 @@ jobs: if: steps.analyze.conclusion != 'skipped' run: | OUTPUT='${{ steps.analyze.outputs.structured_output }}' + echo "### Claude Code Compatibility Summary" >> "$GITHUB_STEP_SUMMARY" + # The claude-code-action --json-schema path intermittently returns no + # structured_output even when the assessment itself succeeds + # (anthropics/claude-code#23265) — and a CI re-run can't escape it, + # since each run is a fresh first-invocation. Treat a missing/invalid + # object as advisory-only: post a note and don't fail the check. A real + # non-PASS verdict (valid object) still fails the check below. + if [ -z "$OUTPUT" ] || ! echo "$OUTPUT" | jq -e 'type == "object"' >/dev/null 2>&1; then + echo "⚠️ No structured output returned by the assessment (known claude-code-action --json-schema flakiness). See Claude's PR comment for the verdict; not blocking this check." >> "$GITHUB_STEP_SUMMARY" + exit 0 + fi RISKLEVEL=$(echo "$OUTPUT" | jq -r '.riskLevel') RECOMMENDATION=$(echo "$OUTPUT" | jq -r '.recommendation') - echo "### Claude Code Compatibility Summary" >> "$GITHUB_STEP_SUMMARY" echo "Risk Level: $RISKLEVEL" >> "$GITHUB_STEP_SUMMARY" echo "Recommendation: $RECOMMENDATION" >> "$GITHUB_STEP_SUMMARY" if [ "$RECOMMENDATION" == "PASS" ]; then From 82061374f1c5ae8b674839f073a462641ef2853d Mon Sep 17 00:00:00 2001 From: Tim Johns Date: Sat, 25 Jul 2026 09:47:34 -0700 Subject: [PATCH 2/5] Drop track_progress so --json-schema returns structured_output Co-Authored-By: Claude Opus 4.8 --- .github/workflows/dependabot-auto-approve.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dependabot-auto-approve.yml b/.github/workflows/dependabot-auto-approve.yml index 6a34f27..a62e1c7 100644 --- a/.github/workflows/dependabot-auto-approve.yml +++ b/.github/workflows/dependabot-auto-approve.yml @@ -231,7 +231,10 @@ jobs: with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} allowed_bots: "dependabot[bot]" - track_progress: true + # Do NOT set track_progress: true here — it forces "tag mode" + # (comment-updating), which is incompatible with --json-schema and makes + # the run return no structured_output (empty $0/1-turn result). Keep this + # step in automation mode so the --json-schema verdict is actually produced. prompt: | REPO: ${{ github.repository }} PR NUMBER: ${{ github.event.pull_request.number }} From 2bebd229dee93249de00a85406789e33dcf4b4d1 Mon Sep 17 00:00:00 2001 From: Tim Johns Date: Sat, 25 Jul 2026 12:03:17 -0700 Subject: [PATCH 3/5] Restore review comment via a summary field posted by the workflow Co-Authored-By: Claude Opus 4.8 --- .github/workflows/dependabot-auto-approve.yml | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/.github/workflows/dependabot-auto-approve.yml b/.github/workflows/dependabot-auto-approve.yml index a62e1c7..252a6d8 100644 --- a/.github/workflows/dependabot-auto-approve.yml +++ b/.github/workflows/dependabot-auto-approve.yml @@ -262,31 +262,42 @@ jobs: ### Recommendation Details [explain why you gave this recommendation] - Add the response as a PR comment - - Return: recommendation (string), riskLevel (string) + Return three fields: + - recommendation (string): PASS, FAIL, or NEEDS REVIEW + - riskLevel (string): Low, Medium, or High + - summary (string): the full markdown assessment above (the + "## Compatibility Assessment ..." block). The workflow posts this + as the PR comment — do NOT post a comment yourself. claude_args: | - --allowedTools "Bash(gh pr comment:*)" - --json-schema '{"type":"object","properties":{"recommendation":{"type":"string"},"riskLevel":{"type":"string"}},"required":["recommendation","riskLevel"]}' + --allowedTools "Read,Grep,Glob,LS" + --json-schema '{"type":"object","properties":{"recommendation":{"type":"string"},"riskLevel":{"type":"string"},"summary":{"type":"string"}},"required":["recommendation","riskLevel","summary"]}' - name: Claude Code Compatibility Summary if: steps.analyze.conclusion != 'skipped' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_URL: ${{ github.event.pull_request.html_url }} run: | OUTPUT='${{ steps.analyze.outputs.structured_output }}' echo "### Claude Code Compatibility Summary" >> "$GITHUB_STEP_SUMMARY" - # The claude-code-action --json-schema path intermittently returns no - # structured_output even when the assessment itself succeeds - # (anthropics/claude-code#23265) — and a CI re-run can't escape it, - # since each run is a fresh first-invocation. Treat a missing/invalid - # object as advisory-only: post a note and don't fail the check. A real + # Defense-in-depth: if structured_output is ever missing/invalid, stay + # advisory (don't fail the check) rather than red-flagging the PR. A real # non-PASS verdict (valid object) still fails the check below. if [ -z "$OUTPUT" ] || ! echo "$OUTPUT" | jq -e 'type == "object"' >/dev/null 2>&1; then - echo "⚠️ No structured output returned by the assessment (known claude-code-action --json-schema flakiness). See Claude's PR comment for the verdict; not blocking this check." >> "$GITHUB_STEP_SUMMARY" + echo "⚠️ No structured output returned by the assessment; not blocking this check." >> "$GITHUB_STEP_SUMMARY" exit 0 fi RISKLEVEL=$(echo "$OUTPUT" | jq -r '.riskLevel') RECOMMENDATION=$(echo "$OUTPUT" | jq -r '.recommendation') + SUMMARY=$(echo "$OUTPUT" | jq -r '.summary // ""') echo "Risk Level: $RISKLEVEL" >> "$GITHUB_STEP_SUMMARY" echo "Recommendation: $RECOMMENDATION" >> "$GITHUB_STEP_SUMMARY" + # The model runs in automation mode and does not post a comment itself, + # so the workflow posts its markdown summary. Edit the bot's existing + # comment if there is one (avoids spamming on re-runs), else create it. + if [ -n "$SUMMARY" ]; then + printf '%s' "$SUMMARY" | gh pr comment "$PR_URL" --edit-last --body-file - 2>/dev/null \ + || printf '%s' "$SUMMARY" | gh pr comment "$PR_URL" --body-file - + fi if [ "$RECOMMENDATION" == "PASS" ]; then exit 0 else From 3786f00f74c662a01adf54e9fa8131a2a1877238 Mon Sep 17 00:00:00 2001 From: Tim Johns Date: Sat, 25 Jul 2026 12:16:13 -0700 Subject: [PATCH 4/5] Always post the compatibility comment (verdict + summary) Co-Authored-By: Claude Opus 4.8 --- .github/workflows/dependabot-auto-approve.yml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/dependabot-auto-approve.yml b/.github/workflows/dependabot-auto-approve.yml index 252a6d8..995388e 100644 --- a/.github/workflows/dependabot-auto-approve.yml +++ b/.github/workflows/dependabot-auto-approve.yml @@ -292,12 +292,16 @@ jobs: echo "Risk Level: $RISKLEVEL" >> "$GITHUB_STEP_SUMMARY" echo "Recommendation: $RECOMMENDATION" >> "$GITHUB_STEP_SUMMARY" # The model runs in automation mode and does not post a comment itself, - # so the workflow posts its markdown summary. Edit the bot's existing - # comment if there is one (avoids spamming on re-runs), else create it. - if [ -n "$SUMMARY" ]; then - printf '%s' "$SUMMARY" | gh pr comment "$PR_URL" --edit-last --body-file - 2>/dev/null \ - || printf '%s' "$SUMMARY" | gh pr comment "$PR_URL" --body-file - - fi + # so the workflow posts the verdict (+ summary when present). Always post, + # editing the bot's existing comment to avoid spamming on re-runs. + { + printf '## Claude dependency compatibility assessment\n\n' + printf '**Recommendation:** %s \n' "$RECOMMENDATION" + printf '**Risk Level:** %s\n\n' "$RISKLEVEL" + [ -n "$SUMMARY" ] && printf '%s\n' "$SUMMARY" + } > "$RUNNER_TEMP/claude-comment.md" + gh pr comment "$PR_URL" --edit-last --body-file "$RUNNER_TEMP/claude-comment.md" 2>/dev/null \ + || gh pr comment "$PR_URL" --body-file "$RUNNER_TEMP/claude-comment.md" if [ "$RECOMMENDATION" == "PASS" ]; then exit 0 else From 4682d3e8ed077d5027d8eca7dd235e9aa33d9b49 Mon Sep 17 00:00:00 2001 From: Tim Johns Date: Sat, 25 Jul 2026 12:24:26 -0700 Subject: [PATCH 5/5] debug: plain create comment + log summary length Co-Authored-By: Claude Opus 4.8 --- .github/workflows/dependabot-auto-approve.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/dependabot-auto-approve.yml b/.github/workflows/dependabot-auto-approve.yml index 995388e..2d2430f 100644 --- a/.github/workflows/dependabot-auto-approve.yml +++ b/.github/workflows/dependabot-auto-approve.yml @@ -292,16 +292,16 @@ jobs: echo "Risk Level: $RISKLEVEL" >> "$GITHUB_STEP_SUMMARY" echo "Recommendation: $RECOMMENDATION" >> "$GITHUB_STEP_SUMMARY" # The model runs in automation mode and does not post a comment itself, - # so the workflow posts the verdict (+ summary when present). Always post, - # editing the bot's existing comment to avoid spamming on re-runs. + # so the workflow posts the verdict (+ summary when present). + BODY_FILE="$RUNNER_TEMP/claude-comment.md" { printf '## Claude dependency compatibility assessment\n\n' printf '**Recommendation:** %s \n' "$RECOMMENDATION" printf '**Risk Level:** %s\n\n' "$RISKLEVEL" [ -n "$SUMMARY" ] && printf '%s\n' "$SUMMARY" - } > "$RUNNER_TEMP/claude-comment.md" - gh pr comment "$PR_URL" --edit-last --body-file "$RUNNER_TEMP/claude-comment.md" 2>/dev/null \ - || gh pr comment "$PR_URL" --body-file "$RUNNER_TEMP/claude-comment.md" + } > "$BODY_FILE" + echo "DEBUG recommendation=[$RECOMMENDATION] risk=[$RISKLEVEL] summary_bytes=${#SUMMARY} body_bytes=$(wc -c < "$BODY_FILE")" + gh pr comment "$PR_URL" --body-file "$BODY_FILE" if [ "$RECOMMENDATION" == "PASS" ]; then exit 0 else