From c20ec958372b709856b3b924735f1198b083f40e Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Watenberg Date: Tue, 19 May 2026 13:52:25 +0200 Subject: [PATCH 1/3] fix(claude-code-review): finalize stuck "in_progress" check on job failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The reusable Claude code-review workflow publishes an "in_progress" check at the start of the review and relies on Claude itself to call publish-summary.sh again with the final conclusion. When the Claude action fails (continue-on-error masks it), the 15-min timeout fires, or the runner is cancelled, the check sits at status=in_progress forever and blocks any branch protection that waits on it. Add a post-step that runs when steps.claude-review.outcome != 'success'. It locates publish-summary.sh in whichever marketplace was checked out and invokes it with the new "cancelled" conclusion, which finalizes the check with a link back to the workflow run. Companion: scality/agent-hub#47 — adds the "cancelled" conclusion (and switches the success-with-issues conclusion from "action_required" to "neutral" so the check no longer shadows real CI status). Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/claude-code-review.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml index 784fdd8..8d38c41 100644 --- a/.github/workflows/claude-code-review.yml +++ b/.github/workflows/claude-code-review.yml @@ -171,3 +171,25 @@ jobs: ANTHROPIC_VERTEX_PROJECT_ID: ${{ secrets.ANTHROPIC_VERTEX_PROJECT_ID }} CLOUD_ML_REGION: ${{ secrets.CLOUD_ML_REGION }} PUBLISH_MODE: ${{ inputs.summary-mode }} + + # Finalize the "Claude Code Review" check when the review job did not + # complete normally (action failure, runner timeout, cancellation). + # Without this the check sits at status=in_progress forever and blocks + # any branch protection that waits on it. + - name: Finalize stuck Claude Code Review check + if: always() && steps.claude-review.outcome != 'success' + env: + GH_TOKEN: ${{ github.token }} + PUBLISH_MODE: ${{ inputs.summary-mode }} + OWNER_REPO: ${{ github.repository }} + PR_NUMBER: ${{ github.event.pull_request.number }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + run: | + script=$(ls .marketplaces/*/plugins/scality-skills/skills/review-pr/publish-summary.sh 2>/dev/null | head -1) + if [[ -z "${script}" ]]; then + echo "publish-summary.sh not found in any marketplace checkout — nothing to finalize" >&2 + exit 0 + fi + summary="Claude review job did not complete (action failure, timeout, or cancellation). See [workflow logs](${RUN_URL})." + "${script}" "${OWNER_REPO}" "${PR_NUMBER}" "${HEAD_SHA}" cancelled <<< "${summary}" From f643d5a48cbd336f7cfab4428f80c031e055d5a1 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Watenberg Date: Wed, 20 May 2026 16:32:07 +0200 Subject: [PATCH 2/3] add check_neutral summary-mode to opt out of CI-shadowing review check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Folds the neutral opt-in into the existing summary-mode input instead of adding a new boolean. Consumers select between blocking and non-blocking review checks the same way they already select between check / comment / auto. - check_neutral: like `check` (check run only, no fallback) but uses GitHub's non-blocking `neutral` conclusion when issues are found, so the review check does not contribute a failure to the PR status rollup and stops shadowing real CI checks. The full review summary still appears in the check run. The actual `action_required` → `neutral` translation lives in publish-summary.sh (scality/agent-hub#47); this PR just exposes the new mode at the workflow input layer. Default (auto) preserves the historical blocking behavior, so existing consumers see no change. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/claude-code-review.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml index 8d38c41..16d4add 100644 --- a/.github/workflows/claude-code-review.yml +++ b/.github/workflows/claude-code-review.yml @@ -28,6 +28,9 @@ on: description: > How the review output should be posted: - check: post as a check run (requires checks: write permission) + - check_neutral: like check, but uses GitHub's non-blocking `neutral` + conclusion when issues are found, so the check does not shadow + real CI checks in the PR status rollup - comment: post as a PR comment - auto: check if allowed, otherwise post as a comment default: auto From 092a074063c66b6e28f97e700fe84b71436b0977 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Watenberg Date: Wed, 20 May 2026 19:31:09 +0200 Subject: [PATCH 3/3] finalize-check: PATCH via gh api directly, drop script coupling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses three review comments on PR #105: - charlesprost: the previous step hardcoded the path to publish-summary.sh in the agent-hub marketplace checkout, so renaming or moving the script would silently break this workflow. The finalize step does exactly one thing — find the in-progress "Claude Code Review" check on this SHA and PATCH it to cancelled — which is ~10 lines of gh api with no skill coupling. Title/summary formatting now lives in two places (here and in publish-summary.sh) but is unlikely to evolve; worth the decoupling. - francoisferrand: the GH_TOKEN env line is redundant — gh CLI picks up the runner-injected GITHUB_TOKEN automatically when the job has the appropriate permissions block (which this one does). - francoisferrand: legacy skills that do not publish an in_progress check no longer cause a spurious script invocation. The gh api GET returns empty for those cases and the step exits 0 naturally. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/claude-code-review.yml | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml index 16d4add..5757e61 100644 --- a/.github/workflows/claude-code-review.yml +++ b/.github/workflows/claude-code-review.yml @@ -178,21 +178,26 @@ jobs: # Finalize the "Claude Code Review" check when the review job did not # complete normally (action failure, runner timeout, cancellation). # Without this the check sits at status=in_progress forever and blocks - # any branch protection that waits on it. + # any branch protection that waits on it. PATCHes via gh api directly + # so the step has no coupling to the skill's helper script and is a + # natural no-op when no in-progress check exists (e.g. legacy skill). - name: Finalize stuck Claude Code Review check if: always() && steps.claude-review.outcome != 'success' env: - GH_TOKEN: ${{ github.token }} - PUBLISH_MODE: ${{ inputs.summary-mode }} OWNER_REPO: ${{ github.repository }} - PR_NUMBER: ${{ github.event.pull_request.number }} HEAD_SHA: ${{ github.event.pull_request.head.sha }} RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} run: | - script=$(ls .marketplaces/*/plugins/scality-skills/skills/review-pr/publish-summary.sh 2>/dev/null | head -1) - if [[ -z "${script}" ]]; then - echo "publish-summary.sh not found in any marketplace checkout — nothing to finalize" >&2 + check_id=$(gh api "repos/${OWNER_REPO}/commits/${HEAD_SHA}/check-runs" \ + -f check_name="Claude Code Review" \ + --jq '.check_runs[] | select(.status != "completed") | .id' | head -1) + if [[ -z "${check_id}" ]]; then + echo "No in-progress 'Claude Code Review' check on this SHA — nothing to finalize" exit 0 fi summary="Claude review job did not complete (action failure, timeout, or cancellation). See [workflow logs](${RUN_URL})." - "${script}" "${OWNER_REPO}" "${PR_NUMBER}" "${HEAD_SHA}" cancelled <<< "${summary}" + jq -n --arg summary "${summary}" '{ + status: "completed", + conclusion: "cancelled", + output: { title: "Claude Code Review: cancelled", summary: $summary } + }' | gh api "repos/${OWNER_REPO}/check-runs/${check_id}" --method PATCH --input -