Skip to content

feat(orchestrator): comprehensive artifact injection across all FSM phases #658

feat(orchestrator): comprehensive artifact injection across all FSM phases

feat(orchestrator): comprehensive artifact injection across all FSM phases #658

name: Claude Code Review
on:
pull_request:
types: [opened, synchronize, ready_for_review, reopened]
# Optional: Only run on specific file changes
# paths:
# - "src/**/*.ts"
# - "src/**/*.tsx"
# - "src/**/*.js"
# - "src/**/*.jsx"
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
claude-review:
# Optional: Filter by PR author
# if: |
# github.event.pull_request.user.login == 'external-contributor' ||
# github.event.pull_request.user.login == 'new-developer' ||
# github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'
if: github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
issues: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Run Claude Code Review
id: claude-review
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
allowed_bots: '*'
track_progress: true
plugin_marketplaces: 'https://github.com/anthropics/claude-code.git'
plugins: 'code-review@claude-code-plugins'
claude_args: '--allowedTools mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*)'
prompt: |
Review PR #${{ github.event.pull_request.number }} in ${{ github.repository }}.
The PR branch is already checked out. Use `gh pr diff ${{ github.event.pull_request.number }}` to get the full diff and `gh pr view ${{ github.event.pull_request.number }}` for PR metadata.
Review the full PR diff (not just the latest commits). Focus on:
- Bugs and correctness issues
- Security vulnerabilities
- Performance concerns
- Design and architecture
Use inline comments for specific code issues. Post a top-level summary comment with your overall assessment using `gh pr comment`.
Always post your review, even if this is a re-review after new commits were pushed. Review the full PR diff, not just the latest commits.
# Review Gate — runs AFTER claude-review so it sees any new review threads
# that Claude posted against the current PR head. If this lived in a separate
# workflow that also triggered on `pull_request: synchronize`, it'd race the
# reviewer and pass on stale (already-resolved) state from the previous commit.
# Using `needs: [claude-review]` here orders the two jobs.
#
# `if: always()` keeps the gate running even when claude-review is skipped
# (fork PRs) or fails — the gate still provides its unresolved-threads check.
review-gate:
name: Review Gate
needs: [claude-review]
if: ${{ always() && github.event_name == 'pull_request' }}
runs-on: ubuntu-latest
permissions:
pull-requests: read
steps:
- name: Check for unresolved review comments
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
# Fetch all review threads via GraphQL and check for unresolved ones
UNRESOLVED=$(gh api graphql -f query='
query($owner: String!, $repo: String!, $pr: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $pr) {
reviewThreads(first: 100) {
nodes {
isResolved
comments(first: 1) {
nodes {
author { login }
body
}
}
}
}
}
}
}
' -f owner="${{ github.repository_owner }}" \
-f repo="$(echo '${{ github.repository }}' | cut -d/ -f2)" \
-F pr="${PR_NUMBER}" \
--jq '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false)')
if [ -z "$UNRESOLVED" ]; then
echo "No unresolved review threads."
exit 0
fi
COUNT=$(echo "$UNRESOLVED" | jq -s 'length')
echo ""
echo "::error::$COUNT unresolved review thread(s) found. Resolve all review comments before merging."
echo ""
# Print details of unresolved threads
echo "$UNRESOLVED" | jq -r '.comments.nodes[0] | " - \(.author.login): \(.body | split("\n") | .[0])"'
echo ""
exit 1