From 310433e28ed33b191668a215db7b927435d91532 Mon Sep 17 00:00:00 2001 From: loothero Date: Sun, 3 May 2026 05:14:34 -0700 Subject: [PATCH 1/3] ci: post Claude reviews from execution_file and fix general_review filter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two fixes after observing PR #144 CI run (run 25278577961): 1. Claude review jobs ran successfully but never posted PR comments. In claude-code-action v1, `prompt:` automation mode does not auto-post a comment the way the beta `direct_prompt:` did — that behavior is gone. Replace the comment-polling step with capture-extract-post-check using the action's `execution_file` output (Claude Code stream-JSON), mirroring the existing Codex flow. Removes 6×10s sleep loop and 4-fallback gh-api queries; review now appears immediately and the blocking-findings check greps a local file. 2. `claude-review-general` and `codex-review-general` triggered on PR #144 despite all changed files being inside excluded scopes. dorny/paths-filter v3 OR-list semantics don't reliably honor `!` negations against a leading `**` glob. Replace the negation-based filter with a shell step that computes `general_review` from `gh pr diff --name-only`, true iff at least one file lives outside contracts/, client/, indexer/, api/. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/pr-ci.yml | 308 ++++++++++++++++++++---------------- 1 file changed, 174 insertions(+), 134 deletions(-) diff --git a/.github/workflows/pr-ci.yml b/.github/workflows/pr-ci.yml index 7d4a9196..47478e79 100644 --- a/.github/workflows/pr-ci.yml +++ b/.github/workflows/pr-ci.yml @@ -23,7 +23,7 @@ jobs: api: ${{ steps.filter.outputs.api }} indexer_api_ci: ${{ steps.filter.outputs.indexer_api_ci }} indexer_api_review: ${{ steps.filter.outputs.indexer_api_review }} - general_review: ${{ steps.filter.outputs.general_review }} + general_review: ${{ steps.general.outputs.general_review }} can_run_ai_reviews: ${{ steps.ai-gate.outputs.can_run_ai_reviews }} ai_reviews_block_reason: ${{ steps.ai-gate.outputs.ai_reviews_block_reason }} can_run_claude_reviews: ${{ steps.ai-gate.outputs.can_run_claude_reviews }} @@ -65,12 +65,24 @@ jobs: - ".github/workflows/pr-ci.yml" - ".github/prompts/**" - ".github/actions/**" - general_review: - - "**" - - "!contracts/**" - - "!client/**" - - "!indexer/**" - - "!api/**" + + # dorny/paths-filter @v3 OR-list semantics don't reliably honor `!` + # negations against a leading `**` (changed files inside excluded + # scopes still match). Compute general_review explicitly: true iff + # the PR touches at least one file outside the four scoped trees. + - name: Compute general_review + id: general + env: + GH_TOKEN: ${{ github.token }} + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + NON_SCOPED=$(gh pr diff "$PR_NUMBER" --name-only \ + | grep -Ev '^(contracts|client|indexer|api)/' || true) + if [ -n "$NON_SCOPED" ]; then + echo "general_review=true" >> "$GITHUB_OUTPUT" + else + echo "general_review=false" >> "$GITHUB_OUTPUT" + fi - name: Determine AI review availability id: ai-gate @@ -478,6 +490,7 @@ jobs: echo "${DELIMITER}" >> "$GITHUB_OUTPUT" - name: Run Claude review + id: claude # v1.0.111 — pin by SHA for security (third-party action with write perms + secrets). uses: anthropics/claude-code-action@fefa07e9c665b7320f08c3b525980457f22f58aa with: @@ -487,45 +500,54 @@ jobs: --model claude-opus-4-7 --allowedTools "Bash(git diff *),Bash(git log *),Bash(git show *),Read,Glob,Grep" - - name: Check for blocking findings + - name: Extract review output if: always() env: - GH_TOKEN: ${{ github.token }} - HEADER: "## Claude Review - Cairo/Starknet Contract Review" - REVIEW_MARKER: run=${{ github.run_id }} attempt=${{ github.run_attempt }} sha=${{ github.event.pull_request.head.sha }} scope=contracts + EXECUTION_FILE: ${{ steps.claude.outputs.execution_file }} run: | - MAX_ATTEMPTS=6 - SLEEP_SECONDS=10 - BODY="" - - for i in $(seq 1 "$MAX_ATTEMPTS"); do - BODY=$(gh api "repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" --paginate \ - | jq -r --arg header "$HEADER" --arg marker "$REVIEW_MARKER" \ - '[.[] | select((.body // "") | contains($header) and contains($marker))] | last | .body // ""') - - if [ -z "$BODY" ]; then - BODY=$(gh api "repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" --paginate \ - | jq -r --arg header "$HEADER" --arg run_token "run=${{ github.run_id }}" --arg sha_token "sha=${{ github.event.pull_request.head.sha }}" \ - '[.[] | select((.body // "") | contains($header) and contains($run_token) and contains($sha_token))] | last | .body // ""') - fi + # v1 doesn't auto-post a PR comment in `prompt:` automation mode the + # way `direct_prompt:` did in beta. Capture Claude's final text from + # the streaming-JSON execution file and write it to /tmp/review.txt. + if [ -z "$EXECUTION_FILE" ] || [ ! -f "$EXECUTION_FILE" ]; then + echo "Claude action did not produce an execution file." > /tmp/review.txt + exit 0 + fi - if [ -n "$BODY" ]; then - echo "Found Claude review comment on attempt $i" - break - fi + REVIEW=$(jq -r 'select(.type == "result") | .result // empty' "$EXECUTION_FILE" 2>/dev/null | tail -1) + if [ -z "$REVIEW" ]; then + REVIEW=$(jq -r 'select(.type == "assistant") | .message.content[]? | select(.type == "text") | .text // empty' "$EXECUTION_FILE" 2>/dev/null) + fi + if [ -z "$REVIEW" ]; then + REVIEW="Claude review did not produce output." + fi + printf '%s\n' "$REVIEW" > /tmp/review.txt - if [ "$i" -lt "$MAX_ATTEMPTS" ]; then - echo "Claude review comment not found yet; sleeping ${SLEEP_SECONDS}s... (attempt $i/$MAX_ATTEMPTS)" - sleep "$SLEEP_SECONDS" + - name: Post review comment + if: always() + env: + GH_TOKEN: ${{ github.token }} + run: | + { + echo "## Claude Review - Cairo/Starknet Contract Review" + echo "" + if [ -s /tmp/review.txt ]; then + cat /tmp/review.txt + else + echo "No review output was produced." fi - done + } > /tmp/review-formatted.txt - if [ -z "$BODY" ]; then - echo "::error::Could not find Claude review output for contracts" + gh pr comment ${{ github.event.pull_request.number }} --body-file /tmp/review-formatted.txt + + - name: Check for blocking findings + if: always() + run: | + if [ ! -s /tmp/review.txt ]; then + echo "::error::No review output was produced despite reviewable changes" exit 1 fi - if echo "$BODY" | grep -qE '\[(CRITICAL|HIGH)\]'; then + if grep -qE '\[(CRITICAL|HIGH)\]' /tmp/review.txt; then echo "::error::Review found CRITICAL or HIGH severity issues" exit 1 fi @@ -712,6 +734,7 @@ jobs: echo "${DELIMITER}" >> "$GITHUB_OUTPUT" - name: Run Claude review + id: claude # v1.0.111 — pin by SHA for security (third-party action with write perms + secrets). uses: anthropics/claude-code-action@fefa07e9c665b7320f08c3b525980457f22f58aa with: @@ -721,45 +744,54 @@ jobs: --model claude-opus-4-7 --allowedTools "Bash(git diff *),Bash(git log *),Bash(git show *),Read,Glob,Grep" - - name: Check for blocking findings + - name: Extract review output if: always() env: - GH_TOKEN: ${{ github.token }} - HEADER: "## Claude Review - React/Frontend Review" - REVIEW_MARKER: run=${{ github.run_id }} attempt=${{ github.run_attempt }} sha=${{ github.event.pull_request.head.sha }} scope=client + EXECUTION_FILE: ${{ steps.claude.outputs.execution_file }} run: | - MAX_ATTEMPTS=6 - SLEEP_SECONDS=10 - BODY="" - - for i in $(seq 1 "$MAX_ATTEMPTS"); do - BODY=$(gh api "repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" --paginate \ - | jq -r --arg header "$HEADER" --arg marker "$REVIEW_MARKER" \ - '[.[] | select((.body // "") | contains($header) and contains($marker))] | last | .body // ""') - - if [ -z "$BODY" ]; then - BODY=$(gh api "repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" --paginate \ - | jq -r --arg header "$HEADER" --arg run_token "run=${{ github.run_id }}" --arg sha_token "sha=${{ github.event.pull_request.head.sha }}" \ - '[.[] | select((.body // "") | contains($header) and contains($run_token) and contains($sha_token))] | last | .body // ""') - fi + # v1 doesn't auto-post a PR comment in `prompt:` automation mode the + # way `direct_prompt:` did in beta. Capture Claude's final text from + # the streaming-JSON execution file and write it to /tmp/review.txt. + if [ -z "$EXECUTION_FILE" ] || [ ! -f "$EXECUTION_FILE" ]; then + echo "Claude action did not produce an execution file." > /tmp/review.txt + exit 0 + fi - if [ -n "$BODY" ]; then - echo "Found Claude review comment on attempt $i" - break - fi + REVIEW=$(jq -r 'select(.type == "result") | .result // empty' "$EXECUTION_FILE" 2>/dev/null | tail -1) + if [ -z "$REVIEW" ]; then + REVIEW=$(jq -r 'select(.type == "assistant") | .message.content[]? | select(.type == "text") | .text // empty' "$EXECUTION_FILE" 2>/dev/null) + fi + if [ -z "$REVIEW" ]; then + REVIEW="Claude review did not produce output." + fi + printf '%s\n' "$REVIEW" > /tmp/review.txt - if [ "$i" -lt "$MAX_ATTEMPTS" ]; then - echo "Claude review comment not found yet; sleeping ${SLEEP_SECONDS}s... (attempt $i/$MAX_ATTEMPTS)" - sleep "$SLEEP_SECONDS" + - name: Post review comment + if: always() + env: + GH_TOKEN: ${{ github.token }} + run: | + { + echo "## Claude Review - React/Frontend Review" + echo "" + if [ -s /tmp/review.txt ]; then + cat /tmp/review.txt + else + echo "No review output was produced." fi - done + } > /tmp/review-formatted.txt + + gh pr comment ${{ github.event.pull_request.number }} --body-file /tmp/review-formatted.txt - if [ -z "$BODY" ]; then - echo "::error::Could not find Claude review output for client" + - name: Check for blocking findings + if: always() + run: | + if [ ! -s /tmp/review.txt ]; then + echo "::error::No review output was produced despite reviewable changes" exit 1 fi - if echo "$BODY" | grep -qE '\[(CRITICAL|HIGH)\]'; then + if grep -qE '\[(CRITICAL|HIGH)\]' /tmp/review.txt; then echo "::error::Review found CRITICAL or HIGH severity issues" exit 1 fi @@ -946,6 +978,7 @@ jobs: echo "${DELIMITER}" >> "$GITHUB_OUTPUT" - name: Run Claude review + id: claude # v1.0.111 — pin by SHA for security (third-party action with write perms + secrets). uses: anthropics/claude-code-action@fefa07e9c665b7320f08c3b525980457f22f58aa with: @@ -955,57 +988,54 @@ jobs: --model claude-opus-4-7 --allowedTools "Bash(git diff *),Bash(git log *),Bash(git show *),Read,Glob,Grep" - - name: Check for blocking findings + - name: Extract review output if: always() env: - GH_TOKEN: ${{ github.token }} - HEADER: "## Claude Review - Indexer/API Review" - REVIEW_MARKER: run=${{ github.run_id }} attempt=${{ github.run_attempt }} sha=${{ github.event.pull_request.head.sha }} scope=indexer-api + EXECUTION_FILE: ${{ steps.claude.outputs.execution_file }} run: | - MAX_ATTEMPTS=6 - SLEEP_SECONDS=10 - BODY="" - - for i in $(seq 1 "$MAX_ATTEMPTS"); do - BODY=$(gh api "repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" --paginate \ - | jq -r --arg header "$HEADER" --arg marker "$REVIEW_MARKER" \ - '[.[] | select((.body // "") | contains($header) and contains($marker))] | last | .body // ""') - - if [ -z "$BODY" ]; then - BODY=$(gh api "repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" --paginate \ - | jq -r --arg header "$HEADER" --arg run_token "run=${{ github.run_id }}" --arg sha_token "sha=${{ github.event.pull_request.head.sha }}" \ - '[.[] | select((.body // "") | contains($header) and contains($run_token) and contains($sha_token))] | last | .body // ""') - fi - - if [ -z "$BODY" ]; then - BODY=$(gh api "repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" --paginate \ - | jq -r --arg marker "$REVIEW_MARKER" \ - '[.[] | select((.user.login // "") == "claude" and ((.body // "") | contains($marker)))] | last | .body // ""') - fi + # v1 doesn't auto-post a PR comment in `prompt:` automation mode the + # way `direct_prompt:` did in beta. Capture Claude's final text from + # the streaming-JSON execution file and write it to /tmp/review.txt. + if [ -z "$EXECUTION_FILE" ] || [ ! -f "$EXECUTION_FILE" ]; then + echo "Claude action did not produce an execution file." > /tmp/review.txt + exit 0 + fi - if [ -z "$BODY" ]; then - BODY=$(gh api "repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" --paginate \ - | jq -r --arg run_token "run=${{ github.run_id }}" --arg sha_token "sha=${{ github.event.pull_request.head.sha }}" \ - '[.[] | select((.user.login // "") == "claude" and ((.body // "") | contains($run_token) and contains($sha_token) and contains("scope=indexer-api")))] | last | .body // ""') - fi + REVIEW=$(jq -r 'select(.type == "result") | .result // empty' "$EXECUTION_FILE" 2>/dev/null | tail -1) + if [ -z "$REVIEW" ]; then + REVIEW=$(jq -r 'select(.type == "assistant") | .message.content[]? | select(.type == "text") | .text // empty' "$EXECUTION_FILE" 2>/dev/null) + fi + if [ -z "$REVIEW" ]; then + REVIEW="Claude review did not produce output." + fi + printf '%s\n' "$REVIEW" > /tmp/review.txt - if [ -n "$BODY" ]; then - echo "Found Claude review comment on attempt $i" - break + - name: Post review comment + if: always() + env: + GH_TOKEN: ${{ github.token }} + run: | + { + echo "## Claude Review - Indexer/API Review" + echo "" + if [ -s /tmp/review.txt ]; then + cat /tmp/review.txt + else + echo "No review output was produced." fi + } > /tmp/review-formatted.txt - if [ "$i" -lt "$MAX_ATTEMPTS" ]; then - echo "Claude review comment not found yet; sleeping ${SLEEP_SECONDS}s... (attempt $i/$MAX_ATTEMPTS)" - sleep "$SLEEP_SECONDS" - fi - done + gh pr comment ${{ github.event.pull_request.number }} --body-file /tmp/review-formatted.txt - if [ -z "$BODY" ]; then - echo "::error::Could not find Claude review output for indexer/api" + - name: Check for blocking findings + if: always() + run: | + if [ ! -s /tmp/review.txt ]; then + echo "::error::No review output was produced despite reviewable changes" exit 1 fi - if echo "$BODY" | grep -qE '\[(CRITICAL|HIGH)\]'; then + if grep -qE '\[(CRITICAL|HIGH)\]' /tmp/review.txt; then echo "::error::Review found CRITICAL or HIGH severity issues" exit 1 fi @@ -1193,6 +1223,7 @@ jobs: echo "${DELIMITER}" >> "$GITHUB_OUTPUT" - name: Run Claude review + id: claude # v1.0.111 — pin by SHA for security (third-party action with write perms + secrets). uses: anthropics/claude-code-action@fefa07e9c665b7320f08c3b525980457f22f58aa with: @@ -1202,45 +1233,54 @@ jobs: --model claude-opus-4-7 --allowedTools "Bash(git diff *),Bash(git log *),Bash(git show *),Read,Glob,Grep" - - name: Check for blocking findings + - name: Extract review output if: always() env: - GH_TOKEN: ${{ github.token }} - HEADER: "## Claude Review - General Engineering Review" - REVIEW_MARKER: run=${{ github.run_id }} attempt=${{ github.run_attempt }} sha=${{ github.event.pull_request.head.sha }} scope=general + EXECUTION_FILE: ${{ steps.claude.outputs.execution_file }} run: | - MAX_ATTEMPTS=6 - SLEEP_SECONDS=10 - BODY="" - - for i in $(seq 1 "$MAX_ATTEMPTS"); do - BODY=$(gh api "repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" --paginate \ - | jq -r --arg header "$HEADER" --arg marker "$REVIEW_MARKER" \ - '[.[] | select((.body // "") | contains($header) and contains($marker))] | last | .body // ""') - - if [ -z "$BODY" ]; then - BODY=$(gh api "repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" --paginate \ - | jq -r --arg header "$HEADER" --arg run_token "run=${{ github.run_id }}" --arg sha_token "sha=${{ github.event.pull_request.head.sha }}" \ - '[.[] | select((.body // "") | contains($header) and contains($run_token) and contains($sha_token))] | last | .body // ""') - fi + # v1 doesn't auto-post a PR comment in `prompt:` automation mode the + # way `direct_prompt:` did in beta. Capture Claude's final text from + # the streaming-JSON execution file and write it to /tmp/review.txt. + if [ -z "$EXECUTION_FILE" ] || [ ! -f "$EXECUTION_FILE" ]; then + echo "Claude action did not produce an execution file." > /tmp/review.txt + exit 0 + fi - if [ -n "$BODY" ]; then - echo "Found Claude review comment on attempt $i" - break - fi + REVIEW=$(jq -r 'select(.type == "result") | .result // empty' "$EXECUTION_FILE" 2>/dev/null | tail -1) + if [ -z "$REVIEW" ]; then + REVIEW=$(jq -r 'select(.type == "assistant") | .message.content[]? | select(.type == "text") | .text // empty' "$EXECUTION_FILE" 2>/dev/null) + fi + if [ -z "$REVIEW" ]; then + REVIEW="Claude review did not produce output." + fi + printf '%s\n' "$REVIEW" > /tmp/review.txt - if [ "$i" -lt "$MAX_ATTEMPTS" ]; then - echo "Claude review comment not found yet; sleeping ${SLEEP_SECONDS}s... (attempt $i/$MAX_ATTEMPTS)" - sleep "$SLEEP_SECONDS" + - name: Post review comment + if: always() + env: + GH_TOKEN: ${{ github.token }} + run: | + { + echo "## Claude Review - General Engineering Review" + echo "" + if [ -s /tmp/review.txt ]; then + cat /tmp/review.txt + else + echo "No review output was produced." fi - done + } > /tmp/review-formatted.txt - if [ -z "$BODY" ]; then - echo "::error::Could not find Claude review output for general changes" + gh pr comment ${{ github.event.pull_request.number }} --body-file /tmp/review-formatted.txt + + - name: Check for blocking findings + if: always() + run: | + if [ ! -s /tmp/review.txt ]; then + echo "::error::No review output was produced despite reviewable changes" exit 1 fi - if echo "$BODY" | grep -qE '\[(CRITICAL|HIGH)\]'; then + if grep -qE '\[(CRITICAL|HIGH)\]' /tmp/review.txt; then echo "::error::Review found CRITICAL or HIGH severity issues" exit 1 fi From f09ca84f34ef2e713800bb047a09692f60743003 Mon Sep 17 00:00:00 2001 From: loothero Date: Sun, 3 May 2026 05:19:25 -0700 Subject: [PATCH 2/3] ci: drop historical-context comments from review workflow Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/pr-ci.yml | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/.github/workflows/pr-ci.yml b/.github/workflows/pr-ci.yml index 47478e79..116ae2c7 100644 --- a/.github/workflows/pr-ci.yml +++ b/.github/workflows/pr-ci.yml @@ -66,10 +66,7 @@ jobs: - ".github/prompts/**" - ".github/actions/**" - # dorny/paths-filter @v3 OR-list semantics don't reliably honor `!` - # negations against a leading `**` (changed files inside excluded - # scopes still match). Compute general_review explicitly: true iff - # the PR touches at least one file outside the four scoped trees. + # True iff the PR touches a file outside contracts/, client/, indexer/, api/. - name: Compute general_review id: general env: @@ -505,9 +502,6 @@ jobs: env: EXECUTION_FILE: ${{ steps.claude.outputs.execution_file }} run: | - # v1 doesn't auto-post a PR comment in `prompt:` automation mode the - # way `direct_prompt:` did in beta. Capture Claude's final text from - # the streaming-JSON execution file and write it to /tmp/review.txt. if [ -z "$EXECUTION_FILE" ] || [ ! -f "$EXECUTION_FILE" ]; then echo "Claude action did not produce an execution file." > /tmp/review.txt exit 0 @@ -749,9 +743,6 @@ jobs: env: EXECUTION_FILE: ${{ steps.claude.outputs.execution_file }} run: | - # v1 doesn't auto-post a PR comment in `prompt:` automation mode the - # way `direct_prompt:` did in beta. Capture Claude's final text from - # the streaming-JSON execution file and write it to /tmp/review.txt. if [ -z "$EXECUTION_FILE" ] || [ ! -f "$EXECUTION_FILE" ]; then echo "Claude action did not produce an execution file." > /tmp/review.txt exit 0 @@ -993,9 +984,6 @@ jobs: env: EXECUTION_FILE: ${{ steps.claude.outputs.execution_file }} run: | - # v1 doesn't auto-post a PR comment in `prompt:` automation mode the - # way `direct_prompt:` did in beta. Capture Claude's final text from - # the streaming-JSON execution file and write it to /tmp/review.txt. if [ -z "$EXECUTION_FILE" ] || [ ! -f "$EXECUTION_FILE" ]; then echo "Claude action did not produce an execution file." > /tmp/review.txt exit 0 @@ -1238,9 +1226,6 @@ jobs: env: EXECUTION_FILE: ${{ steps.claude.outputs.execution_file }} run: | - # v1 doesn't auto-post a PR comment in `prompt:` automation mode the - # way `direct_prompt:` did in beta. Capture Claude's final text from - # the streaming-JSON execution file and write it to /tmp/review.txt. if [ -z "$EXECUTION_FILE" ] || [ ! -f "$EXECUTION_FILE" ]; then echo "Claude action did not produce an execution file." > /tmp/review.txt exit 0 From c3a2acbec56fc06a5a18e5ee29c9ccdbe720ddde Mon Sep 17 00:00:00 2001 From: loothero Date: Sun, 3 May 2026 05:25:31 -0700 Subject: [PATCH 3/3] ci: fail Claude review job when execution_file is missing or empty MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously the Extract review output step wrote a non-empty placeholder ("Claude action did not produce an execution file." / "Claude review did not produce output.") to /tmp/review.txt and exited 0 when the action produced no usable output. The downstream Check for blocking findings gate uses `[ ! -s /tmp/review.txt ]`, so the placeholder satisfied it and the job could pass without a real review — silently bypassing the [CRITICAL]/[HIGH] enforcement that's the whole point of the gate. Both branches now emit `::error::`, leave /tmp/review.txt empty, and exit 1. The Post review comment step (if: always()) still runs and posts the existing "No review output was produced." fallback so reviewers see the failure on the PR. Addresses Codex P1 + Copilot review findings on PR #145. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/pr-ci.yml | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/.github/workflows/pr-ci.yml b/.github/workflows/pr-ci.yml index 116ae2c7..8aab2b19 100644 --- a/.github/workflows/pr-ci.yml +++ b/.github/workflows/pr-ci.yml @@ -502,9 +502,11 @@ jobs: env: EXECUTION_FILE: ${{ steps.claude.outputs.execution_file }} run: | + : > /tmp/review.txt + if [ -z "$EXECUTION_FILE" ] || [ ! -f "$EXECUTION_FILE" ]; then - echo "Claude action did not produce an execution file." > /tmp/review.txt - exit 0 + echo "::error::Claude action did not produce an execution file" + exit 1 fi REVIEW=$(jq -r 'select(.type == "result") | .result // empty' "$EXECUTION_FILE" 2>/dev/null | tail -1) @@ -512,7 +514,8 @@ jobs: REVIEW=$(jq -r 'select(.type == "assistant") | .message.content[]? | select(.type == "text") | .text // empty' "$EXECUTION_FILE" 2>/dev/null) fi if [ -z "$REVIEW" ]; then - REVIEW="Claude review did not produce output." + echo "::error::Claude review produced no extractable output" + exit 1 fi printf '%s\n' "$REVIEW" > /tmp/review.txt @@ -743,9 +746,11 @@ jobs: env: EXECUTION_FILE: ${{ steps.claude.outputs.execution_file }} run: | + : > /tmp/review.txt + if [ -z "$EXECUTION_FILE" ] || [ ! -f "$EXECUTION_FILE" ]; then - echo "Claude action did not produce an execution file." > /tmp/review.txt - exit 0 + echo "::error::Claude action did not produce an execution file" + exit 1 fi REVIEW=$(jq -r 'select(.type == "result") | .result // empty' "$EXECUTION_FILE" 2>/dev/null | tail -1) @@ -753,7 +758,8 @@ jobs: REVIEW=$(jq -r 'select(.type == "assistant") | .message.content[]? | select(.type == "text") | .text // empty' "$EXECUTION_FILE" 2>/dev/null) fi if [ -z "$REVIEW" ]; then - REVIEW="Claude review did not produce output." + echo "::error::Claude review produced no extractable output" + exit 1 fi printf '%s\n' "$REVIEW" > /tmp/review.txt @@ -984,9 +990,11 @@ jobs: env: EXECUTION_FILE: ${{ steps.claude.outputs.execution_file }} run: | + : > /tmp/review.txt + if [ -z "$EXECUTION_FILE" ] || [ ! -f "$EXECUTION_FILE" ]; then - echo "Claude action did not produce an execution file." > /tmp/review.txt - exit 0 + echo "::error::Claude action did not produce an execution file" + exit 1 fi REVIEW=$(jq -r 'select(.type == "result") | .result // empty' "$EXECUTION_FILE" 2>/dev/null | tail -1) @@ -994,7 +1002,8 @@ jobs: REVIEW=$(jq -r 'select(.type == "assistant") | .message.content[]? | select(.type == "text") | .text // empty' "$EXECUTION_FILE" 2>/dev/null) fi if [ -z "$REVIEW" ]; then - REVIEW="Claude review did not produce output." + echo "::error::Claude review produced no extractable output" + exit 1 fi printf '%s\n' "$REVIEW" > /tmp/review.txt @@ -1226,9 +1235,11 @@ jobs: env: EXECUTION_FILE: ${{ steps.claude.outputs.execution_file }} run: | + : > /tmp/review.txt + if [ -z "$EXECUTION_FILE" ] || [ ! -f "$EXECUTION_FILE" ]; then - echo "Claude action did not produce an execution file." > /tmp/review.txt - exit 0 + echo "::error::Claude action did not produce an execution file" + exit 1 fi REVIEW=$(jq -r 'select(.type == "result") | .result // empty' "$EXECUTION_FILE" 2>/dev/null | tail -1) @@ -1236,7 +1247,8 @@ jobs: REVIEW=$(jq -r 'select(.type == "assistant") | .message.content[]? | select(.type == "text") | .text // empty' "$EXECUTION_FILE" 2>/dev/null) fi if [ -z "$REVIEW" ]; then - REVIEW="Claude review did not produce output." + echo "::error::Claude review produced no extractable output" + exit 1 fi printf '%s\n' "$REVIEW" > /tmp/review.txt