diff --git a/.github/workflows/claude-social-review.yml b/.github/workflows/claude-social-review.yml index 611fe99def92..d4d66a679d83 100644 --- a/.github/workflows/claude-social-review.yml +++ b/.github/workflows/claude-social-review.yml @@ -94,22 +94,55 @@ jobs: SERVER_URL: ${{ github.server_url }} run: | set -e - # "Successful" prior run = "the workflow ran without crashing on this branch." - # The PASS/FAIL verdict lives in the PR comment, not the run conclusion. - # Either way, if the social copy hasn't changed, the prior verdict still - # applies — re-running risks flapping (PASS<->FAIL on identical copy). + # The review verdict lives in the PR comment, not the run conclusion — + # if the social copy hasn't changed since the comment was last updated, + # the verdict still applies. Re-running risks flapping (PASS<->FAIL on + # identical copy) and wastes Claude calls on PRs stuck on a char-limit + # fail (where every push to the same over-limit copy would otherwise + # trigger a fresh review). + # + # Baseline preference order: + # 1. Our edit-in-place comment's footer SHA (most accurate — atomic + # with the comment write, unaffected by later step failures). + # 2. Last successful workflow run on this branch (legacy fallback + # for PRs that pre-date the marker comment). + PRIOR_SHA="" + PRIOR_RUN_ID="" + BASELINE_SOURCE="" + + # (1) Try the marker comment first. + # `|| true` on the grep pipeline so a missing footer doesn't kill the + # script under `set -e` — fall through to the run-based baseline instead. + COMMENT_BODY=$(gh api "repos/$REPO/issues/$PR_NUMBER/comments" \ + --jq '[.[] | select(.body | startswith(""))] | .[-1].body // empty') + if [ -n "$COMMENT_BODY" ]; then + COMMENT_SHA=$(echo "$COMMENT_BODY" | grep -oE 'commit `[a-f0-9]{7,40}`' | head -1 | grep -oE '[a-f0-9]{7,40}' || true) + if [ -n "$COMMENT_SHA" ]; then + PRIOR_SHA="$COMMENT_SHA" + BASELINE_SOURCE="comment" + echo "Baseline: social-review comment footer SHA $PRIOR_SHA" + fi + fi + + # (2) Fall back to last successful run on this branch. # `gh run list --branch` (and the API's ?branch= query) is unreliable # for slash-containing branch names — silently returns empty for some # branches that do have runs. Query the workflow's runs unfiltered and # filter by head_branch in jq client-side, which always works. - PRIOR=$(gh api \ - "repos/$REPO/actions/workflows/claude-social-review.yml/runs?per_page=30" \ - --jq "[.workflow_runs[] | select(.head_branch == \"$HEAD_REF\" and .event == \"pull_request\" and .conclusion == \"success\" and .id != $RUN_ID)] | .[0]") - PRIOR_SHA=$(echo "$PRIOR" | jq -r '.head_sha // ""') - PRIOR_RUN_ID=$(echo "$PRIOR" | jq -r '.id // ""') + if [ -z "$PRIOR_SHA" ]; then + PRIOR=$(gh api \ + "repos/$REPO/actions/workflows/claude-social-review.yml/runs?per_page=30" \ + --jq "[.workflow_runs[] | select(.head_branch == \"$HEAD_REF\" and .event == \"pull_request\" and .conclusion == \"success\" and .id != $RUN_ID)] | .[0]") + PRIOR_SHA=$(echo "$PRIOR" | jq -r '.head_sha // ""') + PRIOR_RUN_ID=$(echo "$PRIOR" | jq -r '.id // ""') + if [ -n "$PRIOR_SHA" ]; then + BASELINE_SOURCE="run" + echo "Baseline: prior successful run $PRIOR_RUN_ID at SHA $PRIOR_SHA" + fi + fi if [ -z "$PRIOR_SHA" ]; then - echo "No prior successful run on this branch — running review." + echo "No prior baseline (no marker comment, no successful run) — running review." echo "should_skip=false" >> "$GITHUB_OUTPUT" exit 0 fi @@ -134,8 +167,26 @@ jobs: if [ "$SHOULD_SKIP" = "true" ]; then echo "should_skip=true" >> "$GITHUB_OUTPUT" - gh pr comment "$PR_NUMBER" --repo "$REPO" --body \ - "🟰 **Skipping social media review** — $REASON. The verdict from the [prior run](${SERVER_URL}/${REPO}/actions/runs/${PRIOR_RUN_ID}) still applies. Comment \`/social-review\` to force a fresh review." + # Log to the action run instead of posting a PR comment — the comment + # was noisy on active PRs (one per push that didn't touch the social + # block). Anyone wanting a fresh review can still comment `/social-review`. + if [ "$BASELINE_SOURCE" = "comment" ]; then + VERDICT_LINK="The verdict in the existing social-review PR comment still applies." + NOTICE_TAIL="The PR comment from commit $PRIOR_SHA still applies." + else + VERDICT_LINK="The verdict from the [prior run](${SERVER_URL}/${REPO}/actions/runs/${PRIOR_RUN_ID}) still applies." + NOTICE_TAIL="Prior run: ${SERVER_URL}/${REPO}/actions/runs/${PRIOR_RUN_ID}." + fi + echo "::notice::Skipping social media review — $REASON. $NOTICE_TAIL Comment /social-review to force a fresh review." + { + echo "## 🟰 Skipping social media review" + echo "" + echo "**Reason:** $REASON" + echo "" + echo "$VERDICT_LINK" + echo "" + echo "Comment \`/social-review\` on the PR to force a fresh review." + } >> "$GITHUB_STEP_SUMMARY" else echo "should_skip=false" >> "$GITHUB_OUTPUT" fi @@ -217,8 +268,32 @@ jobs: IMPORTANT: Keep the review short and scannable. Use `#### Platform — PASS` or `#### Platform — FAIL` headings with bullet-pointed reasons. No analysis paragraphs, no rubric citations. A blogger should read it in 30 seconds. - Post your review as a single comment titled `## Social Media Review` via `gh pr comment #${{ steps.pr-info.outputs.number }}`. - claude_args: '--model claude-sonnet-4-6 --allowed-tools "Read,Glob,Grep,Agent,Bash(python3:*),Bash(gh pr view:*),Bash(gh pr diff:*),Bash(gh pr comment:*)"' + Write the review to `.social-review.md`. Do NOT post a PR comment — a subsequent workflow step handles that. The file's body must: + + 1. Start with the literal line `` (HTML comment marker; lets future runs find this comment to update it in place). + 2. Then `## Social Media Review` and the per-platform sections. + 3. End with a footer line: `_Updated for commit \`${{ github.event.pull_request.head.sha || github.event.workflow_run.head_sha }}\` (short: \`$(echo "${{ github.event.pull_request.head.sha || github.event.workflow_run.head_sha }}" | cut -c1-7)\`) at $(date -u '+%Y-%m-%d %H:%M UTC')._` + + Use the Write tool. Do not run `gh pr comment`. + claude_args: '--model claude-sonnet-4-6 --allowed-tools "Read,Glob,Grep,Agent,Write,Bash(python3:*),Bash(gh pr view:*),Bash(gh pr diff:*),Bash(date:*)"' + + - name: Find existing social review comment + if: steps.gate.outputs.should_skip != 'true' && steps.check.outputs.needs_review == 'true' + id: find-social-comment + uses: peter-evans/find-comment@v3 + with: + issue-number: ${{ steps.pr-info.outputs.number }} + body-includes: '' + direction: last + + - name: Post or update social review comment + if: steps.gate.outputs.should_skip != 'true' && steps.check.outputs.needs_review == 'true' + uses: peter-evans/create-or-update-comment@v4 + with: + issue-number: ${{ steps.pr-info.outputs.number }} + comment-id: ${{ steps.find-social-comment.outputs.comment-id }} + body-path: .social-review.md + edit-mode: replace - name: Fail if copy exceeds character limits if: steps.gate.outputs.should_skip != 'true' && steps.check.outputs.has_errors == 'true'