fix: propagate AddResult failures and log embedding errors (#2821) #15666
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Auto PR Comment | |
| # Review chain: CodeRabbit + Greptile (+ optional Qodo/Gemini) β Claude (FINAL) | |
| # Copilot disabled (REVIEW_CHAIN_SKIP_COPILOT=1) β re-enable copilot-* jobs to restore. | |
| # Fork PR jobs use pull_request_target so GH_TOKEN is available for comments. | |
| # | |
| # Claude trigger runs IN-BAND (same workflow) to avoid GitHub Actions | |
| # blocking bot-triggered workflows with action_required approval gates. | |
| on: | |
| issue_comment: | |
| types: [created] | |
| pull_request_review: | |
| types: [submitted] | |
| pull_request: | |
| types: [opened, synchronize] | |
| pull_request_target: | |
| types: [opened, synchronize] | |
| schedule: | |
| - cron: '30 3,9,15,21 * * *' | |
| workflow_dispatch: | |
| env: | |
| REVIEW_CHAIN_SKIP_COPILOT: '1' | |
| MAX_POLL_ATTEMPTS: 20 | |
| POLL_DELAY_MS: 30000 | |
| CLAUDE_TRIGGER_LOGINS: '["MervinPraison","github-actions[bot]"]' | |
| jobs: | |
| # ================================================================ | |
| # Claude FINAL after CodeRabbit / Greptile / optional Qodo / Gemini | |
| # ================================================================ | |
| claude-after-prior-reviewers: | |
| if: | | |
| (github.event_name == 'issue_comment' && | |
| github.event.issue.pull_request && | |
| ( | |
| github.event.comment.user.login == 'coderabbitai[bot]' || | |
| github.event.comment.user.login == 'greptile-apps[bot]' || | |
| contains(github.event.comment.user.login, 'gemini-code-assist') || | |
| contains(github.event.comment.user.login, 'qodo-code-review') | |
| )) || | |
| (github.event_name == 'pull_request_review' && | |
| contains(github.event.review.user.login, 'qodo-code-review')) | |
| runs-on: ubuntu-latest | |
| permissions: | |
| issues: write | |
| pull-requests: read | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: Post Claude FINAL after prior reviewers | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.GH_TOKEN }} | |
| script: | | |
| const path = require('path'); | |
| const chain = require(path.join(process.env.GITHUB_WORKSPACE, '.github/scripts/pr-review-chain.js')); | |
| const mergeGate = require(path.join(process.env.GITHUB_WORKSPACE, '.github/scripts/merge-gate.js')); | |
| const ps = require(path.join(process.env.GITHUB_WORKSPACE, '.github/scripts/pipeline-status.js')); | |
| const prNumber = | |
| context.payload.issue?.number || | |
| context.payload.pull_request?.number; | |
| const owner = context.repo.owner; | |
| const repo = context.repo.repo; | |
| const { data: pr } = await github.rest.pulls.get({ | |
| owner, repo, pull_number: prNumber, | |
| }); | |
| const result = await chain.maybeTriggerClaudeFinal( | |
| github, owner, repo, prNumber, | |
| mergeGate.FINAL_CLAUDE_REVIEW_BODY, core, | |
| { skipCopilot: true, optionalWaitMs: 0, prCreatedAt: pr.created_at } | |
| ); | |
| if (result.posted) core.info(`Posted Claude FINAL on PR #${prNumber}`); | |
| await ps.ensurePipelineLabels(github, owner, repo, core); | |
| await ps.syncPipelineLabels(github, owner, repo, prNumber, core); | |
| # ================================================================ | |
| # FALLBACK: Claude FINAL after PR open if reviewers are slow | |
| # ================================================================ | |
| claude-fallback-timeout: | |
| if: | | |
| github.event_name == 'pull_request_target' && | |
| github.event.action == 'opened' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| permissions: | |
| issues: write | |
| pull-requests: read | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Wait for prior reviewers (25 min) | |
| run: sleep 1500 | |
| - name: Post Claude FINAL (skip Copilot) | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.GH_TOKEN }} | |
| script: | | |
| const path = require('path'); | |
| const chain = require(path.join(process.env.GITHUB_WORKSPACE, '.github/scripts/pr-review-chain.js')); | |
| const mergeGate = require(path.join(process.env.GITHUB_WORKSPACE, '.github/scripts/merge-gate.js')); | |
| const pr = context.payload.pull_request.number; | |
| await chain.maybeTriggerClaudeFinal( | |
| github, context.repo.owner, context.repo.repo, pr, | |
| mergeGate.FINAL_CLAUDE_REVIEW_BODY, core, | |
| { skipCopilot: true, optionalWaitMs: 0 } | |
| ); | |
| # ================================================================ | |
| # Recovery: post FINAL @claude after push or when chain missed | |
| # ================================================================ | |
| claude-review-recovery: | |
| if: | | |
| github.event_name == 'pull_request_target' && | |
| github.event.action == 'synchronize' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| issues: write | |
| pull-requests: read | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Re-post FINAL Claude review when stale or missing | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.GH_TOKEN }} | |
| script: | | |
| const path = require('path'); | |
| const mergeGate = require(path.join(process.env.GITHUB_WORKSPACE, '.github/scripts/merge-gate.js')); | |
| const chain = require(path.join(process.env.GITHUB_WORKSPACE, '.github/scripts/pr-review-chain.js')); | |
| const pr = context.payload.pull_request.number; | |
| const owner = context.repo.owner; | |
| const repo = context.repo.repo; | |
| const comments = await mergeGate.listAllComments(github, owner, repo, pr); | |
| const headPushedAt = (await mergeGate.getHeadCommitDate(github, owner, repo, pr)) | |
| || context.payload.pull_request.updated_at; | |
| const headPusher = context.payload.sender?.login || null; | |
| const hasFinal = mergeGate.hasFinalClaudeReviewTrigger(comments); | |
| const isStale = mergeGate.isStaleFinalAfterPush(comments, headPushedAt); | |
| if (mergeGate.shouldSkipFinalRecovery(comments, headPushedAt)) { | |
| core.info('Recent @claude within 35min and FINAL not stale β skip recovery'); | |
| return; | |
| } | |
| if (await mergeGate.hasInProgressClaudeAssistant(github, owner, repo, pr)) { | |
| core.info('Claude workflow in progress β skip recovery'); | |
| return; | |
| } | |
| const finalBody = mergeGate.FINAL_CLAUDE_REVIEW_BODY; | |
| const opts = { skipCopilot: true, optionalWaitMs: 0 }; | |
| if (hasFinal && isStale) { | |
| const gate = mergeGate.shouldSkipStaleFinalRecovery(comments, headPushedAt, headPusher); | |
| if (gate.skip) { | |
| core.info(`Skip stale-FINAL recovery: ${gate.reason}`); | |
| return; | |
| } | |
| const result = await chain.maybeTriggerClaudeFinal( | |
| github, owner, repo, pr, finalBody, core, opts | |
| ); | |
| if (result.posted) core.info(`Posted stale-FINAL recovery @claude on PR #${pr}`); | |
| return; | |
| } | |
| if (hasFinal) { | |
| core.info('FINAL Claude review current on HEAD β skip recovery'); | |
| return; | |
| } | |
| const result = await chain.maybeTriggerClaudeFinal( | |
| github, owner, repo, pr, finalBody, core, opts | |
| ); | |
| if (result.posted) core.info(`Posted missing FINAL Claude review on PR #${pr}`); | |
| - name: Sync pipeline status labels | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.GH_TOKEN }} | |
| script: | | |
| const path = require('path'); | |
| const ps = require(path.join(process.env.GITHUB_WORKSPACE, '.github/scripts/pipeline-status.js')); | |
| const pr = context.payload.pull_request.number; | |
| await ps.ensurePipelineLabels(github, context.repo.owner, context.repo.repo, core); | |
| await ps.syncPipelineLabels(github, context.repo.owner, context.repo.repo, pr, core); | |
| # Scheduled + manual: post missing or stale FINAL @claude on all open PRs (incl. forks) | |
| post-missing-claude-final: | |
| if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| issues: write | |
| pull-requests: read | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Post missing/stale FINAL @claude on open PRs | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.GH_TOKEN }} | |
| script: | | |
| const path = require('path'); | |
| const mergeGate = require(path.join(process.env.GITHUB_WORKSPACE, '.github/scripts/merge-gate.js')); | |
| const chain = require(path.join(process.env.GITHUB_WORKSPACE, '.github/scripts/pr-review-chain.js')); | |
| const owner = context.repo.owner; | |
| const repo = context.repo.repo; | |
| const prs = await github.paginate(github.rest.pulls.list, { | |
| owner, repo, state: 'open', per_page: 100, | |
| }); | |
| let posted = 0; | |
| for (const pr of prs) { | |
| if (pr.draft) continue; | |
| const comments = await mergeGate.listAllComments(github, owner, repo, pr.number); | |
| const headPushedAt = (await mergeGate.getHeadCommitDate(github, owner, repo, pr.number)) || pr.updated_at; | |
| const hasFinal = mergeGate.hasFinalClaudeReviewTrigger(comments); | |
| const isStale = mergeGate.isStaleFinalAfterPush(comments, headPushedAt); | |
| if (hasFinal && !isStale) continue; | |
| if (mergeGate.hasRecentClaudeTrigger(comments, 10)) { | |
| core.info(`PR #${pr.number}: @claude within 10min β skip`); | |
| continue; | |
| } | |
| if (await mergeGate.hasInProgressClaudeAssistant(github, owner, repo, pr.number)) { | |
| core.info(`PR #${pr.number}: Claude in progress β skip`); | |
| continue; | |
| } | |
| if (hasFinal && isStale) { | |
| const gate = mergeGate.shouldSkipStaleFinalRecovery(comments, headPushedAt); | |
| if (gate.skip) { | |
| core.info(`PR #${pr.number}: skip stale β ${gate.reason}`); | |
| continue; | |
| } | |
| } | |
| const result = await chain.maybeTriggerClaudeFinal( | |
| github, owner, repo, pr.number, | |
| mergeGate.FINAL_CLAUDE_REVIEW_BODY, core, | |
| { skipCopilot: true, optionalWaitMs: 0 } | |
| ); | |
| if (result.posted) { | |
| posted++; | |
| core.info(`Posted Claude FINAL on PR #${pr.number}`); | |
| } | |
| if (posted >= 10) break; | |
| } | |
| core.info(`Missing/stale Claude FINAL sweep complete (${posted} posted)`); | |
| # ================================================================ | |
| # BOT PRs: kick CodeRabbit + Qodo (Greptile may follow on its own) | |
| # ================================================================ | |
| bot-pr-trigger-reviews: | |
| if: | | |
| github.event_name == 'pull_request' && | |
| github.event.action == 'opened' && | |
| ( | |
| github.event.pull_request.user.login == 'github-actions[bot]' || | |
| github.event.pull_request.user.login == 'praisonai-triage-agent[bot]' || | |
| github.event.pull_request.user.type == 'Bot' | |
| ) | |
| runs-on: ubuntu-latest | |
| permissions: | |
| pull-requests: write | |
| issues: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: Trigger CodeRabbit and Qodo reviews | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.GH_TOKEN }} | |
| script: | | |
| const path = require('path'); | |
| const chain = require(path.join(process.env.GITHUB_WORKSPACE, '.github/scripts/bot-pr-review-chain.js')); | |
| const pr = context.payload.pull_request.number; | |
| await chain.kickReviewChain(github, context.repo.owner, context.repo.repo, pr, core); | |
| - name: Sync pipeline status labels | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.GH_TOKEN }} | |
| script: | | |
| const path = require('path'); | |
| const ps = require(path.join(process.env.GITHUB_WORKSPACE, '.github/scripts/pipeline-status.js')); | |
| const pr = context.payload.pull_request.number; | |
| await ps.ensurePipelineLabels(github, context.repo.owner, context.repo.repo, core); | |
| await ps.syncPipelineLabels(github, context.repo.owner, context.repo.repo, pr, core); |