diff --git a/.github/workflows/CLAUDE_REVIEW_SETUP.md b/.github/workflows/CLAUDE_REVIEW_SETUP.md index dc63bd3..ea97351 100644 --- a/.github/workflows/CLAUDE_REVIEW_SETUP.md +++ b/.github/workflows/CLAUDE_REVIEW_SETUP.md @@ -122,32 +122,23 @@ docker-compose up -d ## Setup Instructions -### Step 1: Choose Your Phase +### Step 1: Verify Workflow File -Copy the desired workflow file: - -```bash -# Phase 1: Comment-only review -cp .github/workflows/claude-review-phase1.yml .github/workflows/claude-review.yml - -# Phase 2: Selective consensus -cp .github/workflows/claude-review-phase2.yml .github/workflows/claude-review.yml - -# Phase 3: Draft PR creation -cp .github/workflows/claude-review-phase3.yml .github/workflows/claude-review.yml -``` +The consolidated workflow is already at `.github/workflows/ai-code-review.yml`. +No copying needed — all features (review, consensus, autofix) are in one file. ### Step 2: Add Secrets ```bash -# Add Claude OAuth token -gh secret set CLAUDE_CODE_OAUTH_TOKEN +# At least one Claude API provider (required) +gh secret set ANTHROPIC_API_KEY +# or for AWS Bedrock: +gh secret set AWS_BEARER_TOKEN_BEDROCK +gh secret set AWS_REGION -# Phase 2/3: Add PAL MCP credentials +# PAL MCP credentials (optional — for multi-model consensus) gh secret set PAL_MCP_API_KEY gh secret set PAL_MCP_ENDPOINT --body "https://your-endpoint.com/api/tools/consensus" - -# Phase 3: PAT_TOKEN no longer required (GITHUB_TOKEN is used) ``` ### Step 3: Create CLAUDE.md (Optional but Recommended) diff --git a/.github/workflows/DEPLOYMENT_STATUS.md b/.github/workflows/DEPLOYMENT_STATUS.md index a6e9e0f..164a53f 100644 --- a/.github/workflows/DEPLOYMENT_STATUS.md +++ b/.github/workflows/DEPLOYMENT_STATUS.md @@ -4,94 +4,52 @@ All components have been successfully implemented and are ready for deployment. -### Files Created (7 total, ~70KB) - -#### Workflow Files (4) -| File | Size | Purpose | Cost/PR | Latency | -|------|------|---------|---------|---------| -| `claude-review-phase1.yml` | 3.6K | Comment-only review | ~$1.50 | 2-5 min | -| `claude-review-phase2.yml` | 9.8K | Selective consensus | $1.50-$5 | 5-13 min | -| `claude-review-phase3.yml` | 14K | Draft PR creation | $3-$8 | 8-20 min | -| `ai-review-cost-monitor.yml` | 9.4K | Cost tracking & alerts | N/A | N/A | - -#### Setup & Documentation (3) -| File | Size | Purpose | -|------|------|---------| -| `setup-claude-review.sh` | 13K | Automated setup script (executable) | -| `CLAUDE_REVIEW_SETUP.md` | 12K | Comprehensive setup guide | -| `README_CLAUDE_REVIEW.md` | 9.2K | Quick start guide | +### Files ---- - -## 🚀 Quick Start (< 5 minutes) +#### Workflow Files +| File | Purpose | Cost/PR | Latency | +|------|---------|---------|---------| +| `ai-code-review.yml` | Consolidated review + consensus + autofix | $1.50-$8 | 2-20 min | +| `ai-review-cost-monitor.yml` | Cost tracking & alerts | N/A | N/A | -```bash -# Run automated setup -cd .github/workflows -bash setup-claude-review.sh -``` - -The script will guide you through: -1. ✅ Installing Claude GitHub App -2. ✅ Configuring secrets (Claude OAuth, PAL MCP API keys) -3. ✅ Selecting which phase to install -4. ✅ Creating test PR +#### Setup & Documentation +| File | Purpose | +|------|---------| +| `setup-claude-review.sh` | Automated setup script (executable) | +| `CLAUDE_REVIEW_SETUP.md` | Comprehensive setup guide | +| `README_CLAUDE_REVIEW.md` | Quick start guide | --- -## 📋 Manual Setup (if preferred) - -### Step 1: Choose Your Phase - -**Recommendation**: Start with Phase 1 for 2-4 weeks, then graduate to Phase 2/3. +## Quick Start ```bash -# Phase 1: Safest starting point -cp claude-review-phase1.yml claude-review.yml - -# Phase 2: Add selective consensus (requires PAL MCP) -cp claude-review-phase2.yml claude-review.yml - -# Phase 3: Automated draft PRs (requires PAL MCP + PAT) -cp claude-review-phase3.yml claude-review.yml +bash .github/workflows/setup-claude-review.sh ``` -### Step 2: Configure GitHub Secrets +Or manually configure: ```bash -# Required for all phases -gh secret set CLAUDE_CODE_OAUTH_TOKEN -# Get from: https://claude.com/console +# Required — at least one Claude API provider +gh secret set ANTHROPIC_API_KEY +# or +gh secret set AWS_BEARER_TOKEN_BEDROCK +gh secret set AWS_REGION -# Required for Phase 2 & 3 (PAL MCP) +# Optional — for multi-model consensus gh secret set PAL_MCP_API_KEY gh secret set PAL_MCP_ENDPOINT - -# Phase 3: PAT_TOKEN no longer required (GITHUB_TOKEN is used for PR creation) ``` -### Step 3: Install Claude GitHub App - -1. Visit: https://github.com/apps/claude/installations/new -2. Select this repository -3. Grant permissions: - - Read: Contents, Issues, Pull Requests - - Write: Contents (Phase 3 only), Issues, Pull Requests - -### Step 4: Test It +### Test It ```bash -# Create test PR git checkout -b test/ai-review echo "# AI Review Test" >> test-file.md git add test-file.md git commit -m "test: trigger AI review" git push origin test/ai-review gh pr create --title "Test: AI Review" --body "Testing integration" - -# Phase 1: Check for review comment -# Phase 2: Add 'ai-consensus' label to trigger multi-model -# Phase 3: Wait for draft PR to be created ``` --- @@ -327,8 +285,8 @@ gh secret set PAL_MCP_API_KEY - [ ] Run `setup-claude-review.sh` OR manually configure secrets - [ ] Install Claude GitHub App with proper permissions - [ ] Choose starting phase (recommend Phase 1) -- [ ] Copy workflow file: `cp claude-review-phase1.yml claude-review.yml` -- [ ] Commit and push: `git add .github/workflows/ && git commit -m "feat: add AI code review workflows"` +- [ ] Verify `ai-code-review.yml` exists in `.github/workflows/` +- [ ] Commit and push: `git add .github/workflows/ && git commit -m "feat: add AI code review workflow"` - [ ] Create test PR to verify integration - [ ] Monitor cost with `ai-review-cost-monitor.yml` workflow - [ ] Update CLAUDE.md with project-specific review guidelines (optional) diff --git a/.github/workflows/README_CLAUDE_REVIEW.md b/.github/workflows/README_CLAUDE_REVIEW.md index c8de5de..ca38e8e 100644 --- a/.github/workflows/README_CLAUDE_REVIEW.md +++ b/.github/workflows/README_CLAUDE_REVIEW.md @@ -1,78 +1,48 @@ -# 🤖 Claude Review + PAL MCP Integration +# Claude Review + PAL MCP Integration -**AI-Powered Code Review with Multi-Model Consensus and Automated PR Creation** +**AI-Powered Code Review with Multi-Model Consensus and Automated Draft PRs** --- -## 🚀 Quick Start (< 5 minutes) +## Quick Start + +See [CLAUDE_REVIEW_SETUP.md](./CLAUDE_REVIEW_SETUP.md) for detailed setup, or +run the setup script: ```bash -# Run automated setup script bash .github/workflows/setup-claude-review.sh ``` -The script will guide you through: -1. ✅ Installing Claude GitHub App -2. ✅ Configuring secrets (Claude OAuth, PAL MCP API keys) -3. ✅ Installing workflow files -4. ✅ Creating test PR +--- -**Or follow manual setup**: See [CLAUDE_REVIEW_SETUP.md](./CLAUDE_REVIEW_SETUP.md) +## What's Included ---- +A **single consolidated workflow** (`ai-code-review.yml`) with three jobs: + +### Review (always runs) +- Claude reviews PRs and posts advisory comments +- Dual provider: AWS Bedrock (primary) + Anthropic API (fallback) +- Cost: ~$1.50/PR | Latency: 2-5 minutes + +### Consensus (conditional) +- Multi-model review for security-sensitive changes +- Models: GPT-5-mini, Claude Haiku 4.5, Gemini-2-Flash +- Triggers automatically for high-stakes files or `ai-consensus` label +- Cost: ~$3-5 additional | Latency: +5-8 minutes -## 📊 What's Included - -This implementation provides a **three-phase approach** to AI code review: - -### Phase 1: Comment-Only Review 💬 -- **What**: Claude reviews PRs, posts comments -- **Cost**: ~$1.50/PR -- **Latency**: 2-5 minutes -- **Risk**: Low (read-only) -- **Best for**: All teams, first-time users - -**Triggers**: -- Automatic on PR open/ready for review -- Manual: Comment `@claude-review` on PR - -### Phase 2: Selective Consensus 🔒 -- **What**: Multi-model review for security-sensitive changes -- **Models**: Claude Opus + GPT-5-mini + Claude Haiku + Gemini-2-Flash -- **Cost**: $1.50-$5/PR (only for high-stakes PRs) -- **Latency**: 5-13 minutes -- **Best for**: Repos with security-critical code - -**Auto-triggers when**: -- Changes to `auth/`, `security/`, `crypto/` -- Modifications to `.env`, secrets -- Updates to database migrations or workflows - -**Manual trigger**: Add `ai-consensus` label - -### Phase 3: Draft PR Creation 🚀 -- **What**: Generates draft PRs with AI-suggested fixes -- **Cost**: $3-$8/PR -- **Latency**: 8-20 minutes -- **Risk**: Medium (creates PRs, but requires human approval) -- **Best for**: Mature repos with strong CI/CD - -**Safety features**: -- ✅ Always creates DRAFT PRs (never auto-merges) -- ✅ Blocks modifications to workflows, secrets -- ✅ Requires consensus approval from 3 models -- ✅ Runs security scans on generated patches -- ✅ Requires human `/approve-ai-patch` label +### Autofix (opt-in via `ai-patch` label) +- Generates draft PRs with AI-suggested fixes +- Blocked for protected files and fork PRs +- Always creates DRAFT PRs (never auto-merges) +- Cost: ~$3 additional | Latency: +8-15 minutes --- -## 📁 Files Installed +## Files ``` .github/workflows/ -├── claude-review-phase1.yml # Phase 1: Comment-only -├── claude-review-phase2.yml # Phase 2: Selective consensus -├── claude-review-phase3.yml # Phase 3: Draft PR creation +├── ai-code-review.yml # Consolidated review workflow ├── ai-review-cost-monitor.yml # Cost tracking & alerts ├── setup-claude-review.sh # Automated setup script ├── CLAUDE_REVIEW_SETUP.md # Detailed setup guide @@ -81,26 +51,27 @@ This implementation provides a **three-phase approach** to AI code review: --- -## 🔑 Required Secrets +## Required Secrets -### All Phases: -- `CLAUDE_CODE_OAUTH_TOKEN` - From [Claude Console](https://claude.com/console) +### Review (required — at least one): +- `ANTHROPIC_API_KEY` - Anthropic API key +- `AWS_BEARER_TOKEN_BEDROCK` + `AWS_REGION` - AWS Bedrock credentials -### Phase 2 & 3: +### Consensus (optional): - `PAL_MCP_API_KEY` - From PAL MCP provider - `PAL_MCP_ENDPOINT` - PAL MCP API URL (optional, has default) -### Phase 3: +### Autofix: - Uses `GITHUB_TOKEN` (automatically provided, no additional secrets needed) --- -## 💰 Cost Management +## Cost Management ### Built-in Controls 1. **Size Limits**: PRs >1000 lines require `force-review` label -2. **Selective Triggering**: Phase 2 only on high-stakes changes +2. **Draft Skip**: Draft PRs skip review unless `force-review` label 3. **Concurrency Control**: One review per PR at a time 4. **Cost Monitoring**: Automated tracking every 6 hours @@ -112,254 +83,88 @@ The cost monitor creates GitHub issues when: Customize limits in [`ai-review-cost-monitor.yml`](./ai-review-cost-monitor.yml) -### Optimization Tips - -**Use cheaper models** (Phase 2/3): -```yaml -models: [ - {"model": "gpt-5-mini", "stance": "for"}, # $0.10/1M vs $3/1M - {"model": "claude-haiku-4.5", "stance": "against"}, # $0.25/1M vs $15/1M - {"model": "gemini-2-flash", "stance": "neutral"} # $0.15/1M vs $7/1M -] -``` - -**Result**: 84% cost reduction with minimal quality loss - --- -## 🔒 Security +## Security ### Protections Built-In -| Protection | Phase 1 | Phase 2 | Phase 3 | -|------------|---------|---------|---------| -| Read-only mode | ✅ | ✅ | ❌ | -| Infinite loop prevention | ✅ | ✅ | ✅ | -| Fork PR safety | ✅ | ✅ | ✅ | -| Prompt injection defense | ✅ | ✅ | ✅ | -| Protected file blocking | N/A | N/A | ✅ | -| Secret scanning | N/A | N/A | ✅ | -| Draft PR only (no auto-merge) | N/A | N/A | ✅ | - -### Files Protected from AI Modification - -Phase 3 automatically blocks changes to: -- `.github/workflows/*` - Workflow files -- `secrets/`, `.env*` - Configuration -- `*.key`, `*.pem` - Cryptographic keys +| Protection | Review | Consensus | Autofix | +|------------|--------|-----------|---------| +| Bot loop prevention | Yes | Yes | Yes | +| Fork PR safety | Yes | Yes | Yes (blocked) | +| Protected file blocking | N/A | N/A | Yes | +| Secret scanning | N/A | N/A | Yes | +| Draft PR only (no auto-merge) | N/A | N/A | Yes | + +### Files Protected from AI Modification (Autofix) + +- `.github/workflows/*`, `secrets/`, `.env*` +- `CLAUDE.md`, `.claude/skills/*` +- `agents/core/*`, `agents/traits/*` - PRs from forks --- -## 📖 Usage Examples +## Usage -### Example 1: Trigger Review Manually +### Trigger Review Manually +Comment `@claude-review` on any PR. +### Request Multi-Model Consensus ```bash -# Comment on any PR -@claude-review -``` - -### Example 2: Request Multi-Model Consensus - -```bash -# Add label to PR gh pr edit --add-label "ai-consensus" ``` -### Example 3: Force Review Large PR - +### Force Review (draft or large PR) ```bash -# Override size limit gh pr edit --add-label "force-review" ``` -### Example 4: Approve AI-Generated Draft PR - +### Enable Autofix Draft PR ```bash -# After reviewing the draft PR -gh pr edit --add-label "approve-ai-patch" -gh pr ready # Convert from draft -# Then merge normally (requires approvals per branch protection) +gh pr edit --add-label "ai-patch" ``` ---- - -## 📊 Expected Outputs - -### Phase 1 Output: - -```markdown -🤖 Claude Review Summary - -Issues Found: 3 -- [High] Potential SQL injection in query builder (line 45) -- [Medium] Missing error handling in API endpoint (line 78) -- [Low] Consider using const instead of let (line 12) - -Suggestions: 2 -- Add input validation for user parameters -- Extract magic numbers to named constants - -Confidence: 87% (3/4 agents agree) -Estimated cost: ~$1.50 -Phase: 1 (Comment Only) -``` - -### Phase 2 Output: - -```markdown -🔒 Multi-Model Security Consensus - -Trigger: Security-sensitive files detected (auth/middleware.py) - -Consensus Recommendation: -✅ Safe to proceed with conditions: -1. Add unit tests for new auth logic -2. Update security documentation -3. Require two reviewers minimum - -Model Perspectives: -- GPT-5-mini (for): Improvements are valid -- Claude Haiku (against): Noted concerns about edge cases -- Gemini-2-Flash (neutral): Approved with testing requirements - -Estimated cost: ~$4.20 -Phase: 2 (Selective Consensus) +### Skip Review +```bash +gh pr edit --add-label "skip-ci-review" ``` -### Phase 3 Output: - -```markdown -🤖 AI Suggestions Available - -I've analyzed this PR and created draft PR #147 with suggested improvements. - -🔗 View Draft PR: #147 - -What's included: -✅ Multi-model consensus validation (3 models) -✅ Security checks passed -✅ Patch applied cleanly -✅ Linters/formatters run +--- -Next steps: -1. Review the suggested changes in draft PR #147 -2. If approved, add `/approve-ai-patch` label -3. Merge normally (requires human approval) +## Labels Reference -⚠️ The draft PR requires human review - do not auto-merge. - -Estimated cost: ~$6.80 -Phase: 3 (Draft PR Creation) -``` +| Label | Effect | +|-------|--------| +| `skip-ci-review` | Skip AI review entirely | +| `force-review` | Override draft/size gates | +| `ai-consensus` | Force multi-model consensus | +| `ai-patch` | Enable autofix draft PR creation | --- -## 🐛 Troubleshooting +## Troubleshooting ### Workflow doesn't trigger +1. Check `ai-code-review.yml` exists in `.github/workflows/` +2. Verify YAML syntax: `yamllint .github/workflows/ai-code-review.yml` +3. Confirm secrets are configured: `gh secret list` -**Check**: -1. Workflow file exists in `.github/workflows/` -2. YAML syntax is valid: `yamllint .github/workflows/*.yml` -3. Secrets are configured: `gh secret list` -4. PR is not from a fork (Phase 3 disabled for forks) - -**Debug**: +### Debug ```bash -# View workflow runs -gh run list --workflow=claude-review.yml --limit 5 - -# View specific run logs +gh run list --workflow=ai-code-review.yml --limit 5 gh run view --log ``` -### "CLAUDE_CODE_OAUTH_TOKEN not found" - -```bash -# Verify token in Claude Console: https://claude.com/console -# Add to GitHub: -gh secret set CLAUDE_CODE_OAUTH_TOKEN -# Paste token when prompted -``` - -### PAL MCP returns 401 Unauthorized - -```bash -# Test API key -curl -H "Authorization: Bearer YOUR_KEY" \ - https://your-pal-endpoint.com/api/health - -# If invalid, generate new key and update secret: -gh secret set PAL_MCP_API_KEY -``` - -### Cost too high - -1. **Reduce frequency**: Only trigger on `ready_for_review`, not `synchronize` -2. **Use cheaper models**: Edit Phase 2/3 to use mini/haiku/flash variants -3. **Increase size limits**: Only review PRs <500 lines -4. **Enable label-based triggering**: Remove automatic triggers, require `ai-review` label - -See [CLAUDE_REVIEW_SETUP.md](./CLAUDE_REVIEW_SETUP.md#cost-optimization-tips) for details. - --- -## 📚 Additional Resources +## Resources -- **Full Setup Guide**: [CLAUDE_REVIEW_SETUP.md](./CLAUDE_REVIEW_SETUP.md) -- **Research Report**: [RESEARCH_REPORT.md](../../RESEARCH_REPORT.md) (if available) +- **Setup Guide**: [CLAUDE_REVIEW_SETUP.md](./CLAUDE_REVIEW_SETUP.md) - **Claude Code Docs**: https://code.claude.com/docs - **PAL MCP GitHub**: https://github.com/BeehiveInnovations/pal-mcp-server -- **GitHub Actions**: https://docs.github.com/actions --- -## 🎯 Recommended Adoption Path - -### Week 1-2: Phase 1 Only -- Get familiar with Claude review comments -- Calibrate CLAUDE.md guidelines -- Monitor accuracy and usefulness - -### Week 3-4: Add Phase 2 -- Enable selective consensus for high-stakes -- Test multi-model validation -- Adjust sensitivity thresholds - -### Week 5+: Consider Phase 3 -- Only if Phase 1-2 quality is high -- Strong CI/CD with comprehensive tests required -- Start with label-based triggering, not automatic - ---- - -## 🤝 Support - -**Issues with this integration?** -1. Check [CLAUDE_REVIEW_SETUP.md](./CLAUDE_REVIEW_SETUP.md) troubleshooting section -2. Review workflow run logs: `gh run view --log` -3. Open an issue in this repository - -**Questions about Claude Code?** -- Docs: https://code.claude.com/docs -- Support: support@anthropic.com - -**Questions about PAL MCP?** -- GitHub: https://github.com/BeehiveInnovations/pal-mcp-server -- Issues: https://github.com/BeehiveInnovations/pal-mcp-server/issues - ---- - -## 📄 License - -See repository LICENSE file. - ---- - -**🎉 Happy reviewing with AI!** - -Generated by: Claude Code Research + Implementation -Version: 1.0.0 -Last Updated: 2026-03-04 +Last Updated: 2026-04-01 diff --git a/.github/workflows/ai-code-review.yml b/.github/workflows/ai-code-review.yml new file mode 100644 index 0000000..43831a3 --- /dev/null +++ b/.github/workflows/ai-code-review.yml @@ -0,0 +1,783 @@ +# SuperClaude Consolidated AI Code Review Pipeline +# Combines review, consensus, and autofix into a single workflow +# Dual Provider: AWS Bedrock (Primary) + Anthropic API (Fallback) +# NON-BLOCKING: Advisory comments only, does not prevent merge + +name: AI Code Review + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review, labeled] + issue_comment: + types: [created] + +concurrency: + group: ai-code-review-${{ github.event.pull_request.number || github.event.issue.number }} + cancel-in-progress: true + +# --------------------------------------------------------------------------- +# Job 1: Preflight — gate logic that determines what runs +# --------------------------------------------------------------------------- +jobs: + preflight: + name: Preflight Checks + runs-on: ubuntu-latest + timeout-minutes: 5 + + permissions: + contents: read + pull-requests: read + issues: read + + outputs: + should_review: ${{ steps.gate.outputs.should_review }} + should_consensus: ${{ steps.gate.outputs.should_consensus }} + should_autofix: ${{ steps.gate.outputs.should_autofix }} + is_fork: ${{ steps.gate.outputs.is_fork }} + pr_number: ${{ steps.gate.outputs.pr_number }} + + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + fetch-depth: 0 + + - name: Evaluate gates + id: gate + env: + EVENT_NAME: ${{ github.event_name }} + ACTOR: ${{ github.actor }} + PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }} + PR_DRAFT: ${{ github.event.pull_request.draft }} + COMMENT_BODY: ${{ github.event.comment.body }} + IS_PR_COMMENT: ${{ github.event.issue.pull_request && 'true' || 'false' }} + HAS_SKIP_LABEL: ${{ contains(github.event.pull_request.labels.*.name, 'skip-ci-review') }} + HAS_FORCE_LABEL: ${{ contains(github.event.pull_request.labels.*.name, 'force-review') }} + HAS_CONSENSUS_LABEL: ${{ contains(github.event.pull_request.labels.*.name, 'ai-consensus') }} + HAS_PATCH_LABEL: ${{ contains(github.event.pull_request.labels.*.name, 'ai-patch') }} + IS_FORK: ${{ github.event.pull_request.head.repo.full_name != github.repository && 'true' || 'false' }} + BASE_REF: ${{ github.base_ref }} + run: | + SHOULD_REVIEW="true" + SHOULD_CONSENSUS="false" + SHOULD_AUTOFIX="false" + PR_NUM="${PR_NUMBER}" + + # ── Skip bots ── + if [[ "$ACTOR" == "claude[bot]" ]] || \ + [[ "$ACTOR" == "dependabot[bot]" ]] || \ + [[ "$ACTOR" == "github-actions[bot]" ]]; then + SHOULD_REVIEW="false" + echo "::notice::Skipping — bot actor: $ACTOR" + fi + + # ── Skip label ── + if [[ "$HAS_SKIP_LABEL" == "true" ]]; then + SHOULD_REVIEW="false" + echo "::notice::Skipping — skip-ci-review label present" + fi + + # ── Draft PRs (unless force-review) ── + if [[ "$PR_DRAFT" == "true" ]] && [[ "$HAS_FORCE_LABEL" != "true" ]]; then + SHOULD_REVIEW="false" + echo "::notice::Skipping — PR is draft (add force-review label to override)" + fi + + # ── issue_comment: only proceed on @claude-review ── + if [[ "$EVENT_NAME" == "issue_comment" ]]; then + if [[ "$IS_PR_COMMENT" != "true" ]] || \ + ! echo "$COMMENT_BODY" | grep -q "@claude-review"; then + SHOULD_REVIEW="false" + echo "::notice::Skipping — comment does not contain @claude-review or is not on a PR" + fi + fi + + # ── Size gate (>1000 lines unless force-review) ── + if [[ "$SHOULD_REVIEW" == "true" ]]; then + BASE="${BASE_REF:-main}" + LINES_CHANGED=$(git diff --stat "origin/${BASE}...HEAD" 2>/dev/null | tail -1 | awk '{print $4+$6}') + LINES_CHANGED="${LINES_CHANGED:-0}" + if [[ "$LINES_CHANGED" -gt 1000 ]] && [[ "$HAS_FORCE_LABEL" != "true" ]]; then + SHOULD_REVIEW="false" + echo "::warning::PR too large for AI review (${LINES_CHANGED} lines >1000). Add force-review label to override." + fi + fi + + # ── Consensus detection ── + if [[ "$HAS_CONSENSUS_LABEL" == "true" ]]; then + SHOULD_CONSENSUS="true" + fi + + # Check high-stakes file patterns + if [[ "$SHOULD_REVIEW" == "true" ]]; then + BASE="${BASE_REF:-main}" + CHANGED_FILES=$(git diff --name-only "origin/${BASE}...HEAD" 2>/dev/null || true) + + HIGH_STAKES_PATTERNS=( + "agents/core/" + "agents/traits/" + ".claude/skills/" + ".github/workflows/" + "scripts/" + "CLAUDE.md" + ".env" + "secrets/" + ) + + for pattern in "${HIGH_STAKES_PATTERNS[@]}"; do + if echo "$CHANGED_FILES" | grep -q "$pattern"; then + SHOULD_CONSENSUS="true" + echo "::notice::High-stakes pattern matched: $pattern — consensus enabled" + break + fi + done + fi + + # ── Autofix detection ── + if [[ "$HAS_PATCH_LABEL" == "true" ]] && [[ "$IS_FORK" != "true" ]]; then + # Check no protected files are modified + BASE="${BASE_REF:-main}" + CHANGED_FILES=$(git diff --name-only "origin/${BASE}...HEAD" 2>/dev/null || true) + + PROTECTED_PATTERNS=( + ".github/workflows/" + "secrets/" + ".env" + "CLAUDE.md" + ".claude/skills/" + "agents/core/" + "agents/traits/" + ) + + PROTECTED_HIT="false" + for pattern in "${PROTECTED_PATTERNS[@]}"; do + if echo "$CHANGED_FILES" | grep -q "$pattern"; then + PROTECTED_HIT="true" + echo "::warning::Protected file pattern matched: $pattern — autofix disabled" + break + fi + done + + if [[ "$PROTECTED_HIT" == "false" ]]; then + SHOULD_AUTOFIX="true" + fi + fi + + echo "should_review=$SHOULD_REVIEW" >> "$GITHUB_OUTPUT" + echo "should_consensus=$SHOULD_CONSENSUS" >> "$GITHUB_OUTPUT" + echo "should_autofix=$SHOULD_AUTOFIX" >> "$GITHUB_OUTPUT" + echo "is_fork=$IS_FORK" >> "$GITHUB_OUTPUT" + echo "pr_number=$PR_NUM" >> "$GITHUB_OUTPUT" + + echo "::notice::Gate results — review=$SHOULD_REVIEW consensus=$SHOULD_CONSENSUS autofix=$SHOULD_AUTOFIX fork=$IS_FORK pr=$PR_NUM" + + # --------------------------------------------------------------------------- + # Job 2: Review — main Claude Code review + optional consensus + # --------------------------------------------------------------------------- + review: + name: AI Code Review + needs: preflight + if: needs.preflight.outputs.should_review == 'true' + runs-on: ubuntu-latest + timeout-minutes: 25 + continue-on-error: true + + permissions: + contents: read + pull-requests: write + issues: read + + env: + BEDROCK_CONFIGURED: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK != '' }} + ANTHROPIC_KEY_SET: ${{ secrets.ANTHROPIC_API_KEY != '' }} + PR_NUMBER: ${{ needs.preflight.outputs.pr_number }} + PR_TITLE: ${{ github.event.pull_request.title }} + + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + fetch-depth: 0 + + - name: Validate provider configuration + id: validate_providers + run: | + if [[ "${{ env.BEDROCK_CONFIGURED }}" != "true" ]] && [[ "${{ env.ANTHROPIC_KEY_SET }}" != "true" ]]; then + echo "::warning::No Claude API provider configured (AWS Bedrock or Anthropic API). Skipping review." + echo "skip_review=true" >> "$GITHUB_OUTPUT" + else + echo "skip_review=false" >> "$GITHUB_OUTPUT" + echo "Provider check passed:" + echo " - Bedrock: ${{ env.BEDROCK_CONFIGURED }}" + echo " - Anthropic: ${{ env.ANTHROPIC_KEY_SET }}" + fi + + - name: Gather PR context + if: steps.validate_providers.outputs.skip_review != 'true' + id: context + run: | + BASE_REF="${{ github.base_ref }}" + BASE_REF="${BASE_REF:-main}" + + FILES_CHANGED=$(git diff --name-only "origin/${BASE_REF}...HEAD" | wc -l | tr -d ' ') + echo "files_changed=$FILES_CHANGED" >> "$GITHUB_OUTPUT" + + DIFF_STATS=$(git diff --stat "origin/${BASE_REF}...HEAD" | tail -1) + echo "diff_stats=$DIFF_STATS" >> "$GITHUB_OUTPUT" + + LINES_CHANGED=$(echo "$DIFF_STATS" | awk '{print $4+$6}') + echo "lines_changed=${LINES_CHANGED:-0}" >> "$GITHUB_OUTPUT" + + TESTS_MODIFIED=$(git diff --name-only "origin/${BASE_REF}...HEAD" | grep '^tests/' | wc -l | tr -d ' ') + echo "tests_modified=${TESTS_MODIFIED:-0}" >> "$GITHUB_OUTPUT" + + # ===== PRIMARY: AWS Bedrock ===== + - name: Claude Code Review (AWS Bedrock - Primary) + if: steps.validate_providers.outputs.skip_review != 'true' && env.BEDROCK_CONFIGURED == 'true' + id: bedrock_review + continue-on-error: true + uses: anthropics/claude-code-action@58dbe8ed6879f0d3b02ac295b20d5fdfe7733e0c # v1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + use_bedrock: "true" + timeout_minutes: 15 + max_turns: 20 + allowed_tools: "Bash(git diff:*), Bash(git log:*), Bash(gh pr diff:*), Read, Glob, Grep" + direct_prompt: | + # SuperClaude PR Code Review + + **Repository**: ${{ github.repository }} + **PR #${{ env.PR_NUMBER }}**: ${{ env.PR_TITLE }} + **Files Changed**: ${{ steps.context.outputs.files_changed }} + **Diff Stats**: ${{ steps.context.outputs.diff_stats }} + **Tests Modified**: ${{ steps.context.outputs.tests_modified }} + + ## Instructions + + You are reviewing a pull request for SuperClaude, an AI-enhanced development + framework for Claude Code. Perform a comprehensive code review covering: + + 1. **Security** — injection risks, secret exposure, auth issues + 2. **Correctness** — logic errors, edge cases, null handling + 3. **Performance** — algorithmic complexity, unnecessary allocations + 4. **Architecture** — design patterns, separation of concerns, SOLID + 5. **Testing** — adequate coverage, edge-case tests, mocking + 6. **Code Quality** — readability, naming, DRY, complexity + + ### Process + + 1. Run `gh pr diff ${{ env.PR_NUMBER }}` to examine the changes + 2. Read relevant files for full context where needed + 3. Write your findings to `review-results.md` using the Write tool + + ### Output Format (write to review-results.md) + + ```markdown + ## AI Code Review — PR #${{ env.PR_NUMBER }} + + ### Overview + [Brief summary of changes reviewed] + + ### Critical Issues + [Blocking issues — must fix before merge] + + ### High Priority + [Important issues that should be addressed] + + ### Medium Priority + [Improvements recommended] + + ### Positive Observations + [Good patterns and practices observed] + + ### Review Summary + | Category | Rating | + |----------|--------| + | Security | /5 | + | Code Quality | /5 | + | Architecture | /5 | + | Testing | /5 | + + --- + *AI Code Review (AWS Bedrock). Advisory only — use human judgment.* + ``` + + IMPORTANT: Write the review to `review-results.md` using the Write tool. + Do NOT use `gh pr comment` — a follow-up step will post it. + env: + AWS_BEARER_TOKEN_BEDROCK: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK }} + AWS_REGION: ${{ secrets.AWS_REGION || 'us-east-1' }} + CLAUDE_CODE_USE_BEDROCK: "1" + + - name: Post Bedrock review comment + if: steps.bedrock_review.outcome == 'success' + run: | + if [[ -f "review-results.md" ]]; then + gh pr comment "$PR_NUMBER" --body-file review-results.md + echo "Posted AI review comment (Bedrock)" + else + echo "::warning::review-results.md not found — Claude may not have written the review file" + fi + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Cleanup review file before fallback + if: steps.bedrock_review.outcome != 'success' + run: rm -f review-results.md + + # ===== FALLBACK: Anthropic Direct API ===== + - name: Claude Code Review (Anthropic API - Fallback) + if: >- + steps.validate_providers.outputs.skip_review != 'true' && + env.ANTHROPIC_KEY_SET == 'true' && + (env.BEDROCK_CONFIGURED != 'true' || steps.bedrock_review.outcome != 'success') + id: anthropic_review + continue-on-error: true + uses: anthropics/claude-code-action@58dbe8ed6879f0d3b02ac295b20d5fdfe7733e0c # v1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + timeout_minutes: 15 + max_turns: 20 + allowed_tools: "Bash(git diff:*), Bash(git log:*), Bash(gh pr diff:*), Read, Glob, Grep" + direct_prompt: | + # SuperClaude PR Code Review + + **Repository**: ${{ github.repository }} + **PR #${{ env.PR_NUMBER }}**: ${{ env.PR_TITLE }} + **Files Changed**: ${{ steps.context.outputs.files_changed }} + **Diff Stats**: ${{ steps.context.outputs.diff_stats }} + **Tests Modified**: ${{ steps.context.outputs.tests_modified }} + + ## Instructions + + You are reviewing a pull request for SuperClaude, an AI-enhanced development + framework for Claude Code. Perform a comprehensive code review covering: + + 1. **Security** — injection risks, secret exposure, auth issues + 2. **Correctness** — logic errors, edge cases, null handling + 3. **Performance** — algorithmic complexity, unnecessary allocations + 4. **Architecture** — design patterns, separation of concerns, SOLID + 5. **Testing** — adequate coverage, edge-case tests, mocking + 6. **Code Quality** — readability, naming, DRY, complexity + + ### Process + + 1. Run `gh pr diff ${{ env.PR_NUMBER }}` to examine the changes + 2. Read relevant files for full context where needed + 3. Write your findings to `review-results.md` using the Write tool + + ### Output Format (write to review-results.md) + + ```markdown + ## AI Code Review — PR #${{ env.PR_NUMBER }} + + ### Overview + [Brief summary of changes reviewed] + + ### Critical Issues + [Blocking issues — must fix before merge] + + ### High Priority + [Important issues that should be addressed] + + ### Medium Priority + [Improvements recommended] + + ### Positive Observations + [Good patterns and practices observed] + + ### Review Summary + | Category | Rating | + |----------|--------| + | Security | /5 | + | Code Quality | /5 | + | Architecture | /5 | + | Testing | /5 | + + --- + *AI Code Review (Anthropic API - Fallback). Advisory only — use human judgment.* + ``` + + IMPORTANT: Write the review to `review-results.md` using the Write tool. + Do NOT use `gh pr comment` — a follow-up step will post it. + + - name: Post Anthropic review comment + if: steps.anthropic_review.outcome == 'success' + run: | + if [[ -f "review-results.md" ]]; then + gh pr comment "$PR_NUMBER" --body-file review-results.md + echo "Posted AI review comment (Anthropic)" + else + echo "::warning::review-results.md not found — Claude may not have written the review file" + fi + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # ===== CONDITIONAL CONSENSUS (only for high-stakes changes) ===== + - name: Check PAL MCP configuration + if: needs.preflight.outputs.should_consensus == 'true' + id: check_pal + env: + HAS_PAL_KEY: ${{ secrets.PAL_MCP_API_KEY != '' }} + run: | + if [[ "$HAS_PAL_KEY" != "true" ]]; then + echo "configured=false" >> "$GITHUB_OUTPUT" + echo "::notice::PAL MCP not configured — skipping consensus" + else + echo "configured=true" >> "$GITHUB_OUTPUT" + fi + + - name: Post consensus not-available notice + if: >- + needs.preflight.outputs.should_consensus == 'true' && + steps.check_pal.outputs.configured == 'false' + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + try { + const prNumber = ${{ env.PR_NUMBER }}; + await github.rest.issues.createComment({ + ...context.repo, + issue_number: prNumber, + body: `## PAL MCP Consensus Not Available + + Security-sensitive files were detected, but PAL MCP multi-model consensus is not configured. + + **To enable consensus**: Configure \`PAL_MCP_API_KEY\` and \`PAL_MCP_ENDPOINT\` secrets. + + **Claude Code Review results are still available above.**` + }); + } catch (error) { + core.warning(`Failed to post notice: ${error.message}`); + } + + - name: Run PAL MCP consensus + if: >- + needs.preflight.outputs.should_consensus == 'true' && + steps.check_pal.outputs.configured == 'true' + id: consensus + env: + PAL_MCP_API_KEY: ${{ secrets.PAL_MCP_API_KEY }} + PAL_MCP_ENDPOINT: ${{ secrets.PAL_MCP_ENDPOINT || 'https://pal-mcp.example.com/api/tools/consensus' }} + run: | + BASE_REF="${{ github.base_ref }}" + BASE_REF="${BASE_REF:-main}" + + git diff "origin/${BASE_REF}...HEAD" > /tmp/pr-diff.patch + CHANGED_FILES=$(git diff --name-only "origin/${BASE_REF}...HEAD" | head -10) + + jq -n \ + --arg pr_title "${{ env.PR_TITLE }}" \ + --arg changed_files "$CHANGED_FILES" \ + '{ + prompt: ("Review this PR for security, correctness, and architectural concerns.\n\nPR: " + $pr_title + "\n\nChanged Files:\n" + $changed_files), + models: [ + {model: "gpt-5-mini", stance: "for", stance_prompt: "Evaluate the benefits and improvements"}, + {model: "claude-haiku-4.5", stance: "against", stance_prompt: "Identify security risks, bugs, and potential issues"}, + {model: "gemini-2-flash", stance: "neutral", stance_prompt: "Provide a balanced assessment of merge-readiness"} + ], + focus_areas: ["security", "correctness", "maintainability", "testing"], + temperature: 0.2, + thinking_mode: "high" + }' > /tmp/consensus-request.json + + RESPONSE=$(curl -s -X POST "$PAL_MCP_ENDPOINT" \ + -H "Authorization: Bearer $PAL_MCP_API_KEY" \ + -H "Content-Type: application/json" \ + -d @/tmp/consensus-request.json) + + RECOMMENDATION=$(echo "$RESPONSE" | jq -r '.synthesized_recommendation // "Error: No recommendation"') + echo "recommendation<> "$GITHUB_OUTPUT" + echo "$RECOMMENDATION" >> "$GITHUB_OUTPUT" + echo "EOF" >> "$GITHUB_OUTPUT" + + - name: Post consensus comment + if: >- + needs.preflight.outputs.should_consensus == 'true' && + steps.check_pal.outputs.configured == 'true' + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const recommendation = `${{ steps.consensus.outputs.recommendation }}`; + const body = `## Multi-Model Security Consensus + + ### Consensus Recommendation + + ${recommendation} + + --- + **Models Consulted**: GPT-5-mini (for), Claude Haiku 4.5 (against), Gemini-2-Flash (neutral) + **Human review required** — do not auto-merge security-sensitive changes.`; + + await github.rest.issues.createComment({ + ...context.repo, + issue_number: ${{ env.PR_NUMBER }}, + body: body + }); + + # ===== Summary ===== + - name: Review status + if: always() + run: | + echo "## AI Code Review Status" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + + if [[ "${{ steps.validate_providers.outputs.skip_review }}" == "true" ]]; then + echo "**Status**: Skipped — No providers configured" >> "$GITHUB_STEP_SUMMARY" + exit 0 + fi + + BEDROCK_STATUS="${{ steps.bedrock_review.outcome || 'skipped' }}" + ANTHROPIC_STATUS="${{ steps.anthropic_review.outcome || 'skipped' }}" + CONSENSUS_STATUS="${{ steps.consensus.outcome || 'skipped' }}" + + echo "| Component | Status |" >> "$GITHUB_STEP_SUMMARY" + echo "|-----------|--------|" >> "$GITHUB_STEP_SUMMARY" + echo "| AWS Bedrock (Primary) | $BEDROCK_STATUS |" >> "$GITHUB_STEP_SUMMARY" + echo "| Anthropic API (Fallback) | $ANTHROPIC_STATUS |" >> "$GITHUB_STEP_SUMMARY" + echo "| Multi-Model Consensus | $CONSENSUS_STATUS |" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + + if [[ "$BEDROCK_STATUS" == "success" ]] || [[ "$ANTHROPIC_STATUS" == "success" ]]; then + echo "**Result**: Code review posted to PR" >> "$GITHUB_STEP_SUMMARY" + else + echo "**Result**: Review failed (both providers)" >> "$GITHUB_STEP_SUMMARY" + fi + + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "**Note**: This review is advisory only and does not block merging." >> "$GITHUB_STEP_SUMMARY" + + # --------------------------------------------------------------------------- + # Job 3: Autofix — creates a draft PR with suggested fixes + # --------------------------------------------------------------------------- + autofix: + name: AI Autofix (Draft PR) + needs: [preflight, review] + if: needs.preflight.outputs.should_autofix == 'true' + runs-on: ubuntu-latest + timeout-minutes: 25 + + permissions: + contents: write + pull-requests: write + issues: write + + env: + BEDROCK_CONFIGURED: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK != '' }} + ANTHROPIC_KEY_SET: ${{ secrets.ANTHROPIC_API_KEY != '' }} + PR_NUMBER: ${{ needs.preflight.outputs.pr_number }} + + steps: + # ── Security pre-check ── + - name: Security pre-check + id: security + run: | + if [[ "${{ needs.preflight.outputs.is_fork }}" == "true" ]]; then + echo "::error::Autofix blocked — PR is from a fork" + echo "safe=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + echo "safe=true" >> "$GITHUB_OUTPUT" + + - name: Checkout code + if: steps.security.outputs.safe == 'true' + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + # ===== PRIMARY: AWS Bedrock ===== + - name: Claude Autofix (AWS Bedrock - Primary) + if: steps.security.outputs.safe == 'true' && env.BEDROCK_CONFIGURED == 'true' + id: bedrock_fix + continue-on-error: true + uses: anthropics/claude-code-action@58dbe8ed6879f0d3b02ac295b20d5fdfe7733e0c # v1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + use_bedrock: "true" + timeout_minutes: 20 + max_turns: 15 + allowed_tools: "Edit, Write, Bash(git diff:*), Read, Glob, Grep" + direct_prompt: | + # Autofix: Apply targeted improvements + + Fix issues identified in the code review for PR #${{ env.PR_NUMBER }}. + Create minimal, targeted changes. Do NOT modify protected files: + .github/workflows/*, secrets/*, .env*, CLAUDE.md, .claude/skills/*, + agents/core/*, agents/traits/* + + Steps: + 1. Run `git diff origin/${{ github.base_ref }}...HEAD` to see changes + 2. Identify fixable issues (bugs, style, missing validation) + 3. Apply minimal fixes using Edit/Write + 4. Do NOT create new files unless absolutely necessary + env: + AWS_BEARER_TOKEN_BEDROCK: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK }} + AWS_REGION: ${{ secrets.AWS_REGION || 'us-east-1' }} + CLAUDE_CODE_USE_BEDROCK: "1" + + # ===== FALLBACK: Anthropic Direct API ===== + - name: Claude Autofix (Anthropic API - Fallback) + if: >- + steps.security.outputs.safe == 'true' && + env.ANTHROPIC_KEY_SET == 'true' && + (env.BEDROCK_CONFIGURED != 'true' || steps.bedrock_fix.outcome != 'success') + id: anthropic_fix + continue-on-error: true + uses: anthropics/claude-code-action@58dbe8ed6879f0d3b02ac295b20d5fdfe7733e0c # v1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + timeout_minutes: 20 + max_turns: 15 + allowed_tools: "Edit, Write, Bash(git diff:*), Read, Glob, Grep" + direct_prompt: | + # Autofix: Apply targeted improvements + + Fix issues identified in the code review for PR #${{ env.PR_NUMBER }}. + Create minimal, targeted changes. Do NOT modify protected files: + .github/workflows/*, secrets/*, .env*, CLAUDE.md, .claude/skills/*, + agents/core/*, agents/traits/* + + Steps: + 1. Run `git diff origin/${{ github.base_ref }}...HEAD` to see changes + 2. Identify fixable issues (bugs, style, missing validation) + 3. Apply minimal fixes using Edit/Write + 4. Do NOT create new files unless absolutely necessary + + # ── Validate changes ── + - name: Validate generated changes + if: steps.security.outputs.safe == 'true' + id: validate + run: | + if git diff --quiet; then + echo "has_changes=false" >> "$GITHUB_OUTPUT" + echo "::notice::No code changes suggested by autofix" + exit 0 + fi + + echo "has_changes=true" >> "$GITHUB_OUTPUT" + + # Check diff size (reject if unreasonably large) + DIFF_LINES=$(git diff --stat | tail -1 | awk '{print $4+$6}') + if [[ "${DIFF_LINES:-0}" -gt 500 ]]; then + echo "::error::Autofix generated too many changes (${DIFF_LINES} lines). Aborting." + echo "has_changes=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + # Block protected file modifications + PROTECTED_HIT="false" + for pattern in ".github/workflows/" "secrets/" ".env" "CLAUDE.md" ".claude/skills/" "agents/core/" "agents/traits/"; do + if git diff --name-only | grep -q "$pattern"; then + PROTECTED_HIT="true" + echo "::error::Autofix modified protected path: $pattern — discarding" + break + fi + done + + if [[ "$PROTECTED_HIT" == "true" ]]; then + git checkout -- . + echo "has_changes=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + # Secret scanning + if git diff | grep -iE "(api[_-]?key|password|secret|token)\s*[:=]" > /dev/null 2>&1; then + echo "::warning::Potential secret detected in autofix changes — manual review required" + fi + + echo "::notice::Autofix validated — $(git diff --stat | tail -1)" + + # ── Create draft PR ── + - name: Create Draft Pull Request + if: steps.validate.outputs.has_changes == 'true' + id: create_pr + uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: | + fix: AI-suggested improvements from PR #${{ env.PR_NUMBER }} + + Generated by AI Code Review autofix. + + Co-Authored-By: Claude + branch: ai-fix/${{ env.PR_NUMBER }} + delete-branch: true + draft: true + title: "AI Fix: ${{ github.event.pull_request.title }}" + body: | + ## Automated Improvements + + This draft PR contains AI-suggested fixes for PR #${{ env.PR_NUMBER }}. + + ### Safety Checks + - Security pre-check: Passed + - Protected file scan: Passed + - Secret scan: Passed + - Diff size: Within limits + + ### Important + - **This is a DRAFT PR** — requires human review before merge + - **Do not auto-merge** — maintainer approval required + - Run full CI/CD pipeline before merging + + --- + Generated by [AI Code Review](https://github.com/${{ github.repository }}/actions/workflows/ai-code-review.yml) + labels: | + ai-fix + skip-ci-review + + - name: Comment on original PR + if: steps.create_pr.outputs.pull-request-number + continue-on-error: true + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const prNumber = '${{ steps.create_pr.outputs.pull-request-number }}'; + const prUrl = '${{ steps.create_pr.outputs.pull-request-url }}'; + + await github.rest.issues.createComment({ + ...context.repo, + issue_number: ${{ env.PR_NUMBER }}, + body: `## AI Autofix Available + + A draft PR with suggested improvements has been created: **#${prNumber}** + + [View Draft PR](${prUrl}) + + **The draft PR requires human approval** — do not auto-merge.` + }); + + - name: Autofix summary + if: always() + run: | + echo "## AI Autofix Status" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + if [[ "${{ steps.create_pr.outputs.pull-request-number }}" != "" ]]; then + echo "**Draft PR**: #${{ steps.create_pr.outputs.pull-request-number }}" >> "$GITHUB_STEP_SUMMARY" + echo "**URL**: ${{ steps.create_pr.outputs.pull-request-url }}" >> "$GITHUB_STEP_SUMMARY" + else + echo "**Status**: No draft PR created (no changes or validation failed)" >> "$GITHUB_STEP_SUMMARY" + fi + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "**Note**: Draft PRs require human review before merge." >> "$GITHUB_STEP_SUMMARY" + +# Required secrets: +# - GITHUB_TOKEN (automatically provided) +# - ANTHROPIC_API_KEY or AWS_BEARER_TOKEN_BEDROCK (at least one required) +# - AWS_REGION (optional, defaults to us-east-1) +# - PAL_MCP_API_KEY (optional — consensus skipped if missing) +# - PAL_MCP_ENDPOINT (optional — has default) +# +# Labels: +# - skip-ci-review: Skip AI review entirely +# - force-review: Override draft/size gates +# - ai-consensus: Force multi-model consensus +# - ai-patch: Enable autofix draft PR creation diff --git a/.github/workflows/ai-review-cost-monitor.yml b/.github/workflows/ai-review-cost-monitor.yml index 15f1943..50e8ec6 100644 --- a/.github/workflows/ai-review-cost-monitor.yml +++ b/.github/workflows/ai-review-cost-monitor.yml @@ -8,7 +8,7 @@ on: - cron: '0 */6 * * *' # Every 6 hours workflow_dispatch: # Manual trigger workflow_run: - workflows: ["AI Code Review", "Nightly Code Review", "Claude Review - Phase 3"] + workflows: ["AI Code Review", "Nightly Code Review"] types: [completed] jobs: @@ -22,7 +22,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Query workflow runs id: usage @@ -33,13 +33,13 @@ jobs: SINCE=$(date -u -d '24 hours ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-24H +%Y-%m-%dT%H:%M:%SZ) # Count runs per workflow - AI_REVIEW_RUNS=$(gh run list --workflow=ai-review.yml --created ">=$SINCE" --json conclusion --jq '[.[] | select(.conclusion=="success")] | length' 2>/dev/null || echo "0") + # ai-code-review.yml is the consolidated workflow (review + consensus + autofix) + AI_REVIEW_RUNS=$(gh run list --workflow=ai-code-review.yml --created ">=$SINCE" --json conclusion --jq '[.[] | select(.conclusion=="success")] | length' 2>/dev/null || echo "0") NIGHTLY_RUNS=$(gh run list --workflow=nightly-review.yml --created ">=$SINCE" --json conclusion --jq '[.[] | select(.conclusion=="success")] | length' 2>/dev/null || echo "0") CI_RUNS=$(gh run list --workflow=ci.yml --created ">=$SINCE" --json conclusion --jq '[.[] | select(.conclusion=="success")] | length' 2>/dev/null || echo "0") TRIAGE_RUNS=$(gh run list --workflow=ai-issue-triage.yml --created ">=$SINCE" --json conclusion --jq '[.[] | select(.conclusion=="success")] | length' 2>/dev/null || echo "0") DOCS_RUNS=$(gh run list --workflow=nightly-docs-update.yml --created ">=$SINCE" --json conclusion --jq '[.[] | select(.conclusion=="success")] | length' 2>/dev/null || echo "0") RELEASE_RUNS=$(gh run list --workflow=release-notes.yml --created ">=$SINCE" --json conclusion --jq '[.[] | select(.conclusion=="success")] | length' 2>/dev/null || echo "0") - PHASE3_RUNS=$(gh run list --workflow=claude-review-phase3.yml --created ">=$SINCE" --json conclusion --jq '[.[] | select(.conclusion=="success")] | length' 2>/dev/null || echo "0") echo "ai_review_runs=$AI_REVIEW_RUNS" >> $GITHUB_OUTPUT echo "nightly_runs=$NIGHTLY_RUNS" >> $GITHUB_OUTPUT @@ -47,19 +47,19 @@ jobs: echo "triage_runs=$TRIAGE_RUNS" >> $GITHUB_OUTPUT echo "docs_runs=$DOCS_RUNS" >> $GITHUB_OUTPUT echo "release_runs=$RELEASE_RUNS" >> $GITHUB_OUTPUT - echo "phase3_runs=$PHASE3_RUNS" >> $GITHUB_OUTPUT - name: Calculate costs id: costs run: | # Cost estimates per run (USD) - AI_REVIEW_COST=1.50 + # ai-code-review.yml: consolidated review ($1.50) + optional consensus ($3) + optional autofix ($3) + # Average ~$3/run accounting for mixed usage + AI_REVIEW_COST=3.00 NIGHTLY_COST=3.50 CI_COST=1.50 TRIAGE_COST=0.50 DOCS_COST=2.00 RELEASE_COST=1.00 - PHASE3_COST=6.00 AI_REVIEW_RUNS=${{ steps.usage.outputs.ai_review_runs }} NIGHTLY_RUNS=${{ steps.usage.outputs.nightly_runs }} @@ -67,7 +67,6 @@ jobs: TRIAGE_RUNS=${{ steps.usage.outputs.triage_runs }} DOCS_RUNS=${{ steps.usage.outputs.docs_runs }} RELEASE_RUNS=${{ steps.usage.outputs.release_runs }} - PHASE3_RUNS=${{ steps.usage.outputs.phase3_runs }} DAILY_AI_REVIEW=$(echo "$AI_REVIEW_RUNS * $AI_REVIEW_COST" | bc) DAILY_NIGHTLY=$(echo "$NIGHTLY_RUNS * $NIGHTLY_COST" | bc) @@ -75,9 +74,8 @@ jobs: DAILY_TRIAGE=$(echo "$TRIAGE_RUNS * $TRIAGE_COST" | bc) DAILY_DOCS=$(echo "$DOCS_RUNS * $DOCS_COST" | bc) DAILY_RELEASE=$(echo "$RELEASE_RUNS * $RELEASE_COST" | bc) - DAILY_PHASE3=$(echo "$PHASE3_RUNS * $PHASE3_COST" | bc) - DAILY_TOTAL=$(echo "$DAILY_AI_REVIEW + $DAILY_NIGHTLY + $DAILY_CI + $DAILY_TRIAGE + $DAILY_DOCS + $DAILY_RELEASE + $DAILY_PHASE3" | bc) + DAILY_TOTAL=$(echo "$DAILY_AI_REVIEW + $DAILY_NIGHTLY + $DAILY_CI + $DAILY_TRIAGE + $DAILY_DOCS + $DAILY_RELEASE" | bc) MONTHLY_PROJECTED=$(echo "$DAILY_TOTAL * 30" | bc) echo "daily_total=$DAILY_TOTAL" >> $GITHUB_OUTPUT @@ -89,7 +87,6 @@ jobs: echo "triage_cost=$DAILY_TRIAGE" >> $GITHUB_OUTPUT echo "docs_cost=$DAILY_DOCS" >> $GITHUB_OUTPUT echo "release_cost=$DAILY_RELEASE" >> $GITHUB_OUTPUT - echo "phase3_cost=$DAILY_PHASE3" >> $GITHUB_OUTPUT - name: Check budget limits id: budget @@ -132,13 +129,12 @@ jobs: | Workflow | Runs | Cost/Run | Total Cost | |---------|------|----------|------------| - | AI Code Review | ${{ steps.usage.outputs.ai_review_runs }} | \$1.50 | \$${{ steps.costs.outputs.ai_review_cost }} | + | AI Code Review (consolidated) | ${{ steps.usage.outputs.ai_review_runs }} | \$3.00 | \$${{ steps.costs.outputs.ai_review_cost }} | | Nightly Code Review | ${{ steps.usage.outputs.nightly_runs }} | \$3.50 | \$${{ steps.costs.outputs.nightly_cost }} | | CI | ${{ steps.usage.outputs.ci_runs }} | \$1.50 | \$${{ steps.costs.outputs.ci_cost }} | | AI Issue Triage | ${{ steps.usage.outputs.triage_runs }} | \$0.50 | \$${{ steps.costs.outputs.triage_cost }} | | Nightly Docs Update | ${{ steps.usage.outputs.docs_runs }} | \$2.00 | \$${{ steps.costs.outputs.docs_cost }} | | Release Notes | ${{ steps.usage.outputs.release_runs }} | \$1.00 | \$${{ steps.costs.outputs.release_cost }} | - | Phase 3 (Draft PR) | ${{ steps.usage.outputs.phase3_runs }} | \$6.00 | \$${{ steps.costs.outputs.phase3_cost }} | | **Total** | - | - | **\$${{ steps.costs.outputs.daily_total }}** | ## Monthly Projection @@ -159,7 +155,7 @@ jobs: echo "1. Use cheaper models (claude-haiku, gemini-2-flash)" echo "2. Enable stricter size limits (max 500 lines instead of 1000)" echo "3. Use label-based triggering instead of automatic" - echo "4. Reduce Phase 3 usage (most expensive)" + echo "4. Reduce autofix usage (remove ai-patch label)" else echo "Spend is within budget. No action needed." fi) @@ -176,7 +172,7 @@ jobs: - name: Create budget alert issue if: steps.budget.outputs.daily_exceeded == 'true' || steps.budget.outputs.monthly_exceeded == 'true' - uses: actions/github-script@v8 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | @@ -210,7 +206,7 @@ jobs: - name: Close budget alert if resolved if: steps.budget.outputs.daily_exceeded == 'false' && steps.budget.outputs.monthly_exceeded == 'false' - uses: actions/github-script@v8 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | @@ -239,7 +235,7 @@ jobs: - name: Create issue on workflow failure if: failure() - uses: actions/github-script@v8 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | diff --git a/.github/workflows/ai-review.yml b/.github/workflows/ai-review.yml deleted file mode 100644 index a70c66b..0000000 --- a/.github/workflows/ai-review.yml +++ /dev/null @@ -1,312 +0,0 @@ -# SuperClaude AI Code Review Pipeline -# Uses Claude Code Action with PAL MCP Consensus Code Review -# Dual Provider: AWS Bedrock (Primary) + Anthropic API (Fallback) -# NON-BLOCKING: Advisory comments only, does not prevent merge - -name: AI Code Review - -on: - pull_request: - types: [opened, synchronize, reopened] - -# Note: This workflow is intentionally non-blocking -# It provides AI-powered feedback but does not gate merges - -concurrency: - group: ai-review-${{ github.event.pull_request.number }} - cancel-in-progress: true - -jobs: - ai-review: - name: PAL MCP Consensus Code Review - runs-on: ubuntu-latest - timeout-minutes: 15 - # Skip for dependabot PRs to avoid API costs - if: github.actor != 'dependabot[bot]' - continue-on-error: true - - permissions: - contents: read - pull-requests: write - issues: read - - env: - BEDROCK_CONFIGURED: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK != '' }} - ANTHROPIC_KEY_SET: ${{ secrets.ANTHROPIC_API_KEY != '' }} - PR_NUMBER: ${{ github.event.pull_request.number }} - PR_TITLE: ${{ github.event.pull_request.title }} - - steps: - - name: Checkout repository - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - name: Validate provider configuration - id: validate_providers - run: | - if [[ "${{ env.BEDROCK_CONFIGURED }}" != "true" ]] && [[ "${{ env.ANTHROPIC_KEY_SET }}" != "true" ]]; then - echo "::warning::No Claude API provider configured (AWS Bedrock or Anthropic API). Skipping review." - echo "skip_review=true" >> $GITHUB_OUTPUT - else - echo "skip_review=false" >> $GITHUB_OUTPUT - echo "Provider check passed:" - echo " - Bedrock: ${{ env.BEDROCK_CONFIGURED }}" - echo " - Anthropic: ${{ env.ANTHROPIC_KEY_SET }}" - fi - - - name: Get PR context - if: steps.validate_providers.outputs.skip_review != 'true' - id: context - run: | - # Get changed files count - FILES_CHANGED=$(git diff --name-only origin/${{ github.base_ref }}...HEAD | wc -l | tr -d ' ') - echo "files_changed=$FILES_CHANGED" >> $GITHUB_OUTPUT - - # Get diff statistics - DIFF_STATS=$(git diff --stat origin/${{ github.base_ref }}...HEAD | tail -1) - echo "diff_stats=$DIFF_STATS" >> $GITHUB_OUTPUT - - # Get list of changed Python files (absolute paths) - PYTHON_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD | grep '\.py$' | head -20 | sed "s|^|$(pwd)/|" | tr '\n' ',' | sed 's/,$//') - echo "python_files=$PYTHON_FILES" >> $GITHUB_OUTPUT - - # Check if tests were modified (use wc -l to avoid grep exit code issues) - TESTS_MODIFIED=$(git diff --name-only origin/${{ github.base_ref }}...HEAD | grep '^tests/' | wc -l | tr -d ' ') - echo "tests_modified=${TESTS_MODIFIED:-0}" >> $GITHUB_OUTPUT - - - name: PAL MCP Review (AWS Bedrock - Primary) - if: steps.validate_providers.outputs.skip_review != 'true' && env.BEDROCK_CONFIGURED == 'true' - id: bedrock_review - uses: anthropics/claude-code-action@v1 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - use_bedrock: "true" - show_full_output: true - max_turns: 20 - prompt: | - # SuperClaude PR Code Review - PAL MCP Consensus - - **Repository**: ${{ github.repository }} - **PR Number**: ${{ env.PR_NUMBER }} - **PR Title**: ${{ env.PR_TITLE }} - **Files Changed**: ${{ steps.context.outputs.files_changed }} - **Diff Stats**: ${{ steps.context.outputs.diff_stats }} - **Python Files**: ${{ steps.context.outputs.python_files }} - **Tests Modified**: ${{ steps.context.outputs.tests_modified }} - - ## Instructions - - You are reviewing a pull request for SuperClaude, an AI-enhanced development - framework for Claude Code. Use PAL MCP's consensus code review to get - multi-model perspectives on the changes. - - ### Step 1: Get the PR diff - First, use `gh pr diff ${{ env.PR_NUMBER }}` to examine the changes. - - ### Step 2: Run PAL MCP Consensus Code Review - Use the `mcp__pal__codereview` tool to perform a comprehensive code review. - - Configure the review with: - - `review_type`: "full" (covers quality, security, performance, architecture) - - `relevant_files`: List the changed Python files from the diff - - Focus areas: security (critical for MCP/AI framework), code quality, testing - - The codereview tool will: - 1. Analyze the code systematically - 2. Identify issues by severity (critical, high, medium, low) - 3. Provide expert validation of findings - - ### Step 3: Write Results to File - After the review completes, write the results to `pal-review.md` using - the Write tool. Use this format: - - ```markdown - ## PAL MCP Consensus Code Review (via AWS Bedrock) - - ### Overview - [Brief summary of changes reviewed] - - ### Critical Issues - [Any blocking issues - must fix before merge] - - ### High Priority - [Important issues that should be addressed] - - ### Medium Priority - [Improvements recommended] - - ### Positive Observations - [Good patterns and practices observed] - - ### Review Summary - | Category | Rating | - |----------|--------| - | Security | /5 | - | Code Quality | /5 | - | Architecture | /5 | - | Testing | /5 | - - --- - *This review was generated by PAL MCP Consensus Code Review (AWS Bedrock).* - *Multiple AI models were consulted to validate findings.* - *Review is advisory - please use human judgment for final decisions.* - ``` - - IMPORTANT: Write the review to `pal-review.md` using the Write tool. - Do NOT use `gh pr comment` — a follow-up step will post it for you. - - claude_args: >- - --allowed-tools - "Write,Bash(gh pr diff *),Bash(gh pr view *),mcp__pal__codereview,mcp__pal__consensus" - env: - AWS_BEARER_TOKEN_BEDROCK: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK }} - AWS_REGION: ${{ secrets.AWS_REGION || 'us-east-1' }} - CLAUDE_CODE_USE_BEDROCK: "1" - - - name: Post Bedrock review comment - if: steps.bedrock_review.outcome == 'success' - run: | - if [[ -f "pal-review.md" ]]; then - gh pr comment ${{ env.PR_NUMBER }} --body-file pal-review.md - echo "Posted PAL MCP review comment (Bedrock)" - else - echo "::warning::pal-review.md not found — Claude agent may not have written the review file" - fi - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Cleanup review file before fallback - if: steps.bedrock_review.outcome != 'success' - run: rm -f pal-review.md - - - name: PAL MCP Review (Anthropic API - Fallback) - if: steps.validate_providers.outputs.skip_review != 'true' && env.ANTHROPIC_KEY_SET == 'true' && (env.BEDROCK_CONFIGURED != 'true' || steps.bedrock_review.outcome != 'success') - id: anthropic_review - uses: anthropics/claude-code-action@v1 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} - show_full_output: true - max_turns: 20 - prompt: | - # SuperClaude PR Code Review - PAL MCP Consensus - - **Repository**: ${{ github.repository }} - **PR Number**: ${{ env.PR_NUMBER }} - **PR Title**: ${{ env.PR_TITLE }} - **Files Changed**: ${{ steps.context.outputs.files_changed }} - **Diff Stats**: ${{ steps.context.outputs.diff_stats }} - **Python Files**: ${{ steps.context.outputs.python_files }} - **Tests Modified**: ${{ steps.context.outputs.tests_modified }} - - ## Instructions - - You are reviewing a pull request for SuperClaude, an AI-enhanced development - framework for Claude Code. Use PAL MCP's consensus code review to get - multi-model perspectives on the changes. - - ### Step 1: Get the PR diff - First, use `gh pr diff ${{ env.PR_NUMBER }}` to examine the changes. - - ### Step 2: Run PAL MCP Consensus Code Review - Use the `mcp__pal__codereview` tool to perform a comprehensive code review. - - Configure the review with: - - `review_type`: "full" (covers quality, security, performance, architecture) - - `relevant_files`: List the changed Python files from the diff - - Focus areas: security (critical for MCP/AI framework), code quality, testing - - The codereview tool will: - 1. Analyze the code systematically - 2. Identify issues by severity (critical, high, medium, low) - 3. Provide expert validation of findings - - ### Step 3: Write Results to File - After the review completes, write the results to `pal-review.md` using - the Write tool. Use this format: - - ```markdown - ## PAL MCP Consensus Code Review (via Anthropic API - Fallback) - - ### Overview - [Brief summary of changes reviewed] - - ### Critical Issues - [Any blocking issues - must fix before merge] - - ### High Priority - [Important issues that should be addressed] - - ### Medium Priority - [Improvements recommended] - - ### Positive Observations - [Good patterns and practices observed] - - ### Review Summary - | Category | Rating | - |----------|--------| - | Security | /5 | - | Code Quality | /5 | - | Architecture | /5 | - | Testing | /5 | - - --- - *This review was generated by PAL MCP Consensus Code Review (Anthropic API - Fallback).* - *Multiple AI models were consulted to validate findings.* - *Review is advisory - please use human judgment for final decisions.* - ``` - - IMPORTANT: Write the review to `pal-review.md` using the Write tool. - Do NOT use `gh pr comment` — a follow-up step will post it for you. - - claude_args: >- - --allowed-tools - "Write,Bash(gh pr diff *),Bash(gh pr view *),mcp__pal__codereview,mcp__pal__consensus" - - - name: Post Anthropic review comment - if: steps.anthropic_review.outcome == 'success' - run: | - if [[ -f "pal-review.md" ]]; then - gh pr comment ${{ env.PR_NUMBER }} --body-file pal-review.md - echo "Posted PAL MCP review comment (Anthropic)" - else - echo "::warning::pal-review.md not found — Claude agent may not have written the review file" - fi - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Review status - if: always() - run: | - echo "## PAL MCP Consensus Code Review Status" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - - if [[ "${{ steps.validate_providers.outputs.skip_review }}" == "true" ]]; then - echo "**Status**: Skipped - No providers configured" >> $GITHUB_STEP_SUMMARY - exit 0 - fi - - BEDROCK_STATUS="${{ steps.bedrock_review.outcome || 'skipped' }}" - ANTHROPIC_STATUS="${{ steps.anthropic_review.outcome || 'skipped' }}" - - echo "| Provider | Status |" >> $GITHUB_STEP_SUMMARY - echo "|----------|--------|" >> $GITHUB_STEP_SUMMARY - echo "| AWS Bedrock (Primary) | $BEDROCK_STATUS |" >> $GITHUB_STEP_SUMMARY - echo "| Anthropic API (Fallback) | $ANTHROPIC_STATUS |" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - - if [[ "$BEDROCK_STATUS" == "success" ]] || [[ "$ANTHROPIC_STATUS" == "success" ]]; then - echo "**Result**: Multi-model consensus code review posted to PR" >> $GITHUB_STEP_SUMMARY - else - echo "**Result**: Review failed (both providers)" >> $GITHUB_STEP_SUMMARY - fi - - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Features:**" >> $GITHUB_STEP_SUMMARY - echo "- Full review covering security, quality, performance, architecture" >> $GITHUB_STEP_SUMMARY - echo "- Issues categorized by severity (critical/high/medium/low)" >> $GITHUB_STEP_SUMMARY - echo "- Expert model validation of findings" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Note**: This review is advisory only and does not block merging." >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ba611eb..04ebd50 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,10 +31,10 @@ jobs: timeout-minutes: 10 steps: - name: Checkout repository - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 with: python-version: '3.11' cache: 'pip' @@ -69,7 +69,7 @@ jobs: name: Claude Code Review runs-on: ubuntu-latest timeout-minutes: 15 - # Only run on PRs, skip dependabot; ai-review.yml handles full PAL MCP review + # Only run on PRs, skip dependabot; ai-code-review.yml handles full PAL MCP review if: >- github.event_name == 'pull_request' && github.actor != 'dependabot[bot]' && @@ -88,7 +88,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 @@ -135,7 +135,7 @@ jobs: if: steps.validate_providers.outputs.skip_review != 'true' && env.BEDROCK_CONFIGURED == 'true' id: bedrock_review continue-on-error: true - uses: anthropics/claude-code-action@v1 + uses: anthropics/claude-code-action@58dbe8ed6879f0d3b02ac295b20d5fdfe7733e0c # v1 with: github_token: ${{ secrets.GITHUB_TOKEN }} use_bedrock: "true" @@ -204,7 +204,7 @@ jobs: - name: Claude Review (Anthropic API - Fallback) if: steps.validate_providers.outputs.skip_review != 'true' && env.ANTHROPIC_KEY_SET == 'true' && (env.BEDROCK_CONFIGURED != 'true' || steps.bedrock_review.outcome != 'success') id: anthropic_review - uses: anthropics/claude-code-action@v1 + uses: anthropics/claude-code-action@58dbe8ed6879f0d3b02ac295b20d5fdfe7733e0c # v1 with: github_token: ${{ secrets.GITHUB_TOKEN }} anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} @@ -305,10 +305,10 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 with: python-version: ${{ matrix.python-version }} cache: 'pip' @@ -332,7 +332,7 @@ jobs: - name: Upload coverage artifact if: matrix.python-version == '3.10' - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 with: name: coverage-report path: coverage.xml @@ -340,7 +340,7 @@ jobs: - name: Upload coverage to Codecov if: matrix.python-version == '3.10' - uses: codecov/codecov-action@v6 + uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6 with: files: ./coverage.xml fail_ci_if_error: false @@ -358,10 +358,10 @@ jobs: timeout-minutes: 10 steps: - name: Checkout repository - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 with: python-version: '3.11' cache: 'pip' @@ -397,10 +397,10 @@ jobs: timeout-minutes: 10 steps: - name: Checkout repository - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 with: python-version: '3.11' cache: 'pip' @@ -417,7 +417,7 @@ jobs: run: twine check dist/* - name: Upload build artifacts - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 with: name: dist path: dist/ @@ -457,13 +457,13 @@ jobs: steps: - name: Download build artifacts - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 with: name: dist path: dist/ - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 with: python-version: ${{ matrix.python-version }} @@ -535,10 +535,10 @@ jobs: timeout-minutes: 10 steps: - name: Checkout repository - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 with: python-version: '3.11' cache: 'pip' @@ -576,20 +576,20 @@ jobs: echo "skip=false" >> "$GITHUB_OUTPUT" fi - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 if: steps.check-token.outputs.skip != 'true' with: fetch-depth: 0 - name: Download coverage report if: steps.check-token.outputs.skip != 'true' - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 with: name: coverage-report - name: Set up Python if: steps.check-token.outputs.skip != 'true' - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 with: python-version: '3.11' cache: 'pip' @@ -609,7 +609,7 @@ jobs: - name: SonarCloud Scan if: steps.check-token.outputs.skip != 'true' continue-on-error: true - uses: SonarSource/sonarqube-scan-action@v7 + uses: SonarSource/sonarqube-scan-action@299e4b793aaa83bf2aba7c9c14bedbb485688ec4 # v7 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_HOST_URL: https://sonarcloud.io @@ -625,7 +625,7 @@ jobs: - name: SonarCloud Quality Gate if: steps.check-token.outputs.skip != 'true' && success() continue-on-error: true - uses: SonarSource/sonarqube-quality-gate-action@v1 + uses: SonarSource/sonarqube-quality-gate-action@d304d050d930b02a896b0f85935344f023928496 # v1 timeout-minutes: 5 env: SONAR_HOST_URL: https://sonarcloud.io diff --git a/.github/workflows/claude-review-phase1.yml b/.github/workflows/claude-review-phase1.yml deleted file mode 100644 index 8d4b62e..0000000 --- a/.github/workflows/claude-review-phase1.yml +++ /dev/null @@ -1,102 +0,0 @@ -name: Claude Review - Phase 1 (Comment Only) - -# Phase 1: Basic Claude Code Review with comment-only mode -# Safe starting point - no PR creation, just review comments -# Estimated cost: ~$1.50/PR | Latency: 2-5 minutes - -on: - pull_request: - types: [opened, ready_for_review] - issue_comment: - types: [created] - -permissions: - contents: read - pull-requests: write - issues: read - -defaults: - run: - shell: bash - -jobs: - claude-review: - # Security: Prevent infinite loops - don't review bot's own PRs - if: | - github.actor != 'claude[bot]' && - github.actor != 'dependabot[bot]' && - github.actor != 'github-actions[bot]' && - ( - github.event_name == 'pull_request' || - (github.event_name == 'issue_comment' && - github.event.issue.pull_request && - contains(github.event.comment.body, '@claude-review')) - ) - - runs-on: ubuntu-latest - timeout-minutes: 15 - - concurrency: - group: claude-review-phase1-${{ github.event.pull_request.number || github.event.issue.number }} - cancel-in-progress: true - - steps: - - name: Checkout code - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - name: Check PR size (cost control) - id: pr_size - run: | - BASE_REF="${{ github.base_ref }}" - BASE_REF="${BASE_REF:-main}" - FILES_CHANGED=$(git diff --name-only origin/${BASE_REF}...HEAD | wc -l) - LINES_CHANGED=$(git diff --stat origin/${BASE_REF}...HEAD | tail -1 | awk '{print $4+$6}') - - echo "files_changed=$FILES_CHANGED" >> $GITHUB_OUTPUT - echo "lines_changed=$LINES_CHANGED" >> $GITHUB_OUTPUT - - if [ "$LINES_CHANGED" -gt 1000 ]; then - echo "skip_review=true" >> $GITHUB_OUTPUT - echo "::warning::PR too large for AI review (>1000 lines). Use 'force-review' label to override." - else - echo "skip_review=false" >> $GITHUB_OUTPUT - fi - - - name: Skip large PR notice - if: steps.pr_size.outputs.skip_review == 'true' && !contains(github.event.pull_request.labels.*.name, 'force-review') - run: | - echo "::notice::Skipping review for large PR. Add 'force-review' label to override." - exit 0 - - - name: Run Claude Code Review - if: steps.pr_size.outputs.skip_review == 'false' || contains(github.event.pull_request.labels.*.name, 'force-review') - uses: anthropics/claude-code-action@v1 - with: - claude_args: | - code-review --comment \ - --model claude-opus-4-6 \ - --max-turns 20 \ - --allowedTools "Bash(git diff *),Bash(git log *),Bash(git blame *),Read" - - github_token: ${{ secrets.GITHUB_TOKEN }} - claude_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} - - - name: Review metrics - if: always() - run: | - echo "### Review Metrics" >> $GITHUB_STEP_SUMMARY - echo "- Files changed: ${{ steps.pr_size.outputs.files_changed }}" >> $GITHUB_STEP_SUMMARY - echo "- Lines changed: ${{ steps.pr_size.outputs.lines_changed }}" >> $GITHUB_STEP_SUMMARY - echo "- Estimated cost: ~\$1.50" >> $GITHUB_STEP_SUMMARY - echo "- Phase: 1 (Comment Only)" >> $GITHUB_STEP_SUMMARY - -# Required secrets: -# - GITHUB_TOKEN (automatically provided) -# - CLAUDE_CODE_OAUTH_TOKEN (from Claude Console) -# -# Setup instructions: -# 1. Install Claude GitHub App: https://github.com/apps/claude -# 2. Create OAuth token in Claude Console -# 3. Add as repository secret: CLAUDE_CODE_OAUTH_TOKEN diff --git a/.github/workflows/claude-review-phase2.yml b/.github/workflows/claude-review-phase2.yml deleted file mode 100644 index cdebb08..0000000 --- a/.github/workflows/claude-review-phase2.yml +++ /dev/null @@ -1,335 +0,0 @@ -name: Claude Review - Phase 2 (Selective Consensus) - -# Phase 2: Claude Review + PAL MCP consensus for high-stakes changes -# Triggers multi-model review for security-sensitive files -# Estimated cost: $1.50/PR (normal) | $3-5/PR (high-stakes with consensus) - -on: - pull_request: - types: [opened, ready_for_review, synchronize] - issue_comment: - types: [created] - -permissions: - contents: read - pull-requests: write - issues: read - -defaults: - run: - shell: bash - -jobs: - detect-high-stakes: - name: Detect High-Stakes Changes - runs-on: ubuntu-latest - timeout-minutes: 5 - - concurrency: - group: claude-review-phase2-detect-${{ github.event_name }}-${{ github.event.pull_request.number || github.event.issue.number }} - cancel-in-progress: true - - outputs: - is_high_stakes: ${{ steps.check.outputs.is_high_stakes }} - sensitive_files: ${{ steps.check.outputs.sensitive_files }} - reason: ${{ steps.check.outputs.reason }} - - steps: - - name: Checkout code - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - name: Check for high-stakes changes - id: check - run: | - BASE_REF="${{ github.base_ref }}" - BASE_REF="${BASE_REF:-main}" - - # SuperClaude high-stakes patterns - HIGH_STAKES_PATTERNS=( - "agents/core/" - "agents/traits/" - ".claude/skills/" - ".github/workflows/" - "scripts/" - "CLAUDE.md" - ".env" - "secrets/" - ) - - CHANGED_FILES=$(git diff --name-only origin/${BASE_REF}...HEAD) - - IS_HIGH_STAKES="false" - SENSITIVE_FILES="" - REASON="" - - for pattern in "${HIGH_STAKES_PATTERNS[@]}"; do - if echo "$CHANGED_FILES" | grep -i "$pattern" > /dev/null; then - IS_HIGH_STAKES="true" - SENSITIVE_FILES=$(echo "$CHANGED_FILES" | grep -i "$pattern" | head -5) - REASON="Security-sensitive files detected: $pattern" - break - fi - done - - # Check if ai-consensus label is present (manual trigger) - if [[ "${{ contains(github.event.pull_request.labels.*.name, 'ai-consensus') }}" == "true" ]]; then - IS_HIGH_STAKES="true" - REASON="Manual consensus requested via label" - fi - - echo "is_high_stakes=$IS_HIGH_STAKES" >> $GITHUB_OUTPUT - echo "sensitive_files<> $GITHUB_OUTPUT - echo "$SENSITIVE_FILES" >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT - echo "reason=$REASON" >> $GITHUB_OUTPUT - - if [ "$IS_HIGH_STAKES" == "true" ]; then - echo "::notice::High-stakes changes detected - will trigger multi-model consensus" - fi - - claude-review: - name: Claude Code Review - needs: detect-high-stakes - if: | - github.actor != 'claude[bot]' && - github.actor != 'dependabot[bot]' && - github.actor != 'github-actions[bot]' - - runs-on: ubuntu-latest - timeout-minutes: 15 - - concurrency: - group: claude-review-phase2-review-${{ github.event_name }}-${{ github.event.pull_request.number || github.event.issue.number }} - cancel-in-progress: true - - outputs: - review_output: ${{ steps.review.outputs.result }} - - steps: - - name: Checkout code - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - name: Run Claude Code Review - id: review - uses: anthropics/claude-code-action@v1 - with: - claude_args: | - code-review \ - --model claude-opus-4-6 \ - --max-turns 20 \ - --allowedTools "Bash(git diff *),Bash(git log *),Bash(git blame *),Read" - - github_token: ${{ secrets.GITHUB_TOKEN }} - claude_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} - - - name: Save Claude review to artifact - if: needs.detect-high-stakes.outputs.is_high_stakes == 'true' - run: | - mkdir -p /tmp/review-artifacts - echo "${{ steps.review.outputs.result }}" > /tmp/review-artifacts/claude-review.json - - - name: Upload review artifact - if: needs.detect-high-stakes.outputs.is_high_stakes == 'true' - uses: actions/upload-artifact@v7 - with: - name: claude-review-result - path: /tmp/review-artifacts/ - - pal-consensus: - name: PAL MCP Multi-Model Consensus - needs: [detect-high-stakes, claude-review] - if: needs.detect-high-stakes.outputs.is_high_stakes == 'true' - runs-on: ubuntu-latest - timeout-minutes: 20 - continue-on-error: true - - concurrency: - group: claude-review-phase2-consensus-${{ github.event_name }}-${{ github.event.pull_request.number || github.event.issue.number }} - cancel-in-progress: true - - steps: - - name: Checkout code - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - name: Check PAL MCP configuration - id: check-pal - env: - HAS_PAL_KEY: ${{ secrets.PAL_MCP_API_KEY != '' }} - run: | - if [ "$HAS_PAL_KEY" != "true" ]; then - echo "configured=false" >> $GITHUB_OUTPUT - echo "PAL MCP is not configured (missing PAL_MCP_API_KEY)" - else - echo "configured=true" >> $GITHUB_OUTPUT - fi - - - name: Post configuration notice - if: steps.check-pal.outputs.configured == 'false' - continue-on-error: true - uses: actions/github-script@v8 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - try { - await github.rest.issues.createComment({ - ...context.repo, - issue_number: context.payload.pull_request.number, - body: `## PAL MCP Consensus Not Available - - Security-sensitive files were detected, but PAL MCP multi-model consensus is not configured. - - **High-stakes files changed**: \`${{ needs.detect-high-stakes.outputs.sensitive_files }}\` - - **To enable PAL MCP consensus**: - 1. Configure \`PAL_MCP_API_KEY\` and \`PAL_MCP_ENDPOINT\` secrets - - **Claude Code Review results are still available above.**` - }); - core.info('PAL MCP not configured - posted notice comment'); - } catch (error) { - core.warning(`Failed to post comment (may lack permissions): ${error.message}`); - core.info('PAL MCP not configured - skipping consensus'); - } - process.exit(0); - - - name: Download Claude review - if: steps.check-pal.outputs.configured == 'true' - uses: actions/download-artifact@v8 - with: - name: claude-review-result - path: /tmp/review-artifacts/ - - - name: Prepare context files - if: steps.check-pal.outputs.configured == 'true' - id: context - run: | - BASE_REF="${{ github.base_ref }}" - BASE_REF="${BASE_REF:-main}" - git diff origin/${BASE_REF}...HEAD > /tmp/pr-diff.patch - git diff --name-only origin/${BASE_REF}...HEAD | head -10 > /tmp/changed-files.txt - LINES_CHANGED=$(git diff --stat origin/${BASE_REF}...HEAD | tail -1 | awk '{print $4+$6}') - echo "lines_changed=$LINES_CHANGED" >> $GITHUB_OUTPUT - - - name: Run PAL MCP Consensus - if: steps.check-pal.outputs.configured == 'true' - id: consensus - env: - PAL_MCP_API_KEY: ${{ secrets.PAL_MCP_API_KEY }} - PAL_MCP_ENDPOINT: ${{ secrets.PAL_MCP_ENDPOINT || 'https://pal-mcp.example.com/api/tools/consensus' }} - PR_TITLE: ${{ github.event.pull_request.title }} - CONSENSUS_REASON: ${{ needs.detect-high-stakes.outputs.reason }} - run: | - CLAUDE_REVIEW=$(cat /tmp/review-artifacts/claude-review.json | jq -R -s .) - CHANGED_FILES=$(cat /tmp/changed-files.txt) - - jq -n \ - --arg pr_title "$PR_TITLE" \ - --arg changed_files "$CHANGED_FILES" \ - --arg claude_review "$CLAUDE_REVIEW" \ - --arg consensus_reason "$CONSENSUS_REASON" \ - '{ - prompt: ("Review this PR for security, correctness, and architectural concerns. Claude Code Review has already analyzed it. Provide a multi-model consensus on whether this change is safe to merge.\n\nPR Description: " + $pr_title + "\n\nChanged Files:\n" + $changed_files + "\n\nClaude Review Summary:\n" + $claude_review + "\n\nReason for consensus: " + $consensus_reason), - models: [ - {model: "gpt-5-mini", stance: "for", stance_prompt: "Evaluate the benefits and improvements this change brings"}, - {model: "claude-haiku-4.5", stance: "against", stance_prompt: "Identify security risks, bugs, and potential issues"}, - {model: "gemini-2-flash", stance: "neutral", stance_prompt: "Provide a balanced assessment of merge-readiness"} - ], - focus_areas: ["security", "correctness", "maintainability", "testing"], - temperature: 0.2, - thinking_mode: "high" - }' > /tmp/consensus-request.json - - RESPONSE=$(curl -s -X POST "$PAL_MCP_ENDPOINT" \ - -H "Authorization: Bearer $PAL_MCP_API_KEY" \ - -H "Content-Type: application/json" \ - -d @/tmp/consensus-request.json) - - echo "$RESPONSE" > /tmp/consensus-result.json - - RECOMMENDATION=$(echo "$RESPONSE" | jq -r '.synthesized_recommendation // "Error: No recommendation"') - echo "recommendation<> $GITHUB_OUTPUT - echo "$RECOMMENDATION" >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT - - - name: Post consensus comment - if: steps.check-pal.outputs.configured == 'true' - uses: actions/github-script@v8 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const recommendation = `${{ steps.consensus.outputs.recommendation }}`; - const reason = `${{ needs.detect-high-stakes.outputs.reason }}`; - const sensitiveFiles = `${{ needs.detect-high-stakes.outputs.sensitive_files }}`; - - const body = `## Multi-Model Security Consensus - - **Trigger**: ${reason} - - **Sensitive Files Detected**: - \`\`\` - ${sensitiveFiles} - \`\`\` - - ### Consensus Recommendation - - ${recommendation} - - --- - **Models Consulted**: GPT-5-mini (for), Claude Haiku 4.5 (against), Gemini-2-Flash (neutral) - **Cost**: ~$3-5 for this consensus review - **Phase**: 2 (Selective Consensus) - - **Human review required** - Do not auto-merge security-sensitive changes. - `; - - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - body: body - }); - - - name: Upload consensus artifact - uses: actions/upload-artifact@v7 - with: - name: pal-consensus-result - path: /tmp/consensus-result.json - - review-summary: - name: Review Summary - needs: [detect-high-stakes, claude-review, pal-consensus] - if: always() - runs-on: ubuntu-latest - timeout-minutes: 5 - - steps: - - name: Generate summary - run: | - echo "### Review Complete" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "- **Phase**: 2 (Selective Consensus)" >> $GITHUB_STEP_SUMMARY - echo "- **High-Stakes**: ${{ needs.detect-high-stakes.outputs.is_high_stakes }}" >> $GITHUB_STEP_SUMMARY - if [ "${{ needs.detect-high-stakes.outputs.is_high_stakes }}" == "true" ]; then - echo "- **Reason**: ${{ needs.detect-high-stakes.outputs.reason }}" >> $GITHUB_STEP_SUMMARY - echo "- **Models**: Claude Opus, GPT-5-mini, Claude Haiku, Gemini-2-Flash" >> $GITHUB_STEP_SUMMARY - echo "- **Estimated Cost**: ~\$3-5" >> $GITHUB_STEP_SUMMARY - else - echo "- **Review Type**: Standard (Claude only)" >> $GITHUB_STEP_SUMMARY - echo "- **Estimated Cost**: ~\$1.50" >> $GITHUB_STEP_SUMMARY - fi - -# Required secrets: -# - GITHUB_TOKEN (automatically provided) -# - CLAUDE_CODE_OAUTH_TOKEN (from Claude Console) -# - PAL_MCP_API_KEY (from PAL MCP provider) -# - PAL_MCP_ENDPOINT (optional, defaults to example.com) -# -# Required labels: -# - ai-consensus: Manually trigger multi-model consensus -# - force-review: Override size limits diff --git a/.github/workflows/claude-review-phase3.yml b/.github/workflows/claude-review-phase3.yml deleted file mode 100644 index 667ff93..0000000 --- a/.github/workflows/claude-review-phase3.yml +++ /dev/null @@ -1,423 +0,0 @@ -name: Claude Review - Phase 3 (Draft PR Creation) - -# Phase 3: Full pipeline with automated draft PR creation -# Creates draft PRs with AI-suggested fixes (requires human approval to merge) -# CRITICAL: Never auto-merges, always requires maintainer review -# Estimated cost: $3-8/PR depending on consensus + patch generation - -on: - pull_request: - types: [opened, ready_for_review, labeled] - -# Cost control: Only allow one workflow per PR at a time -concurrency: - group: claude-full-review-${{ github.event.pull_request.number }} - cancel-in-progress: true - -jobs: - security-check: - name: Pre-flight Security Check - runs-on: ubuntu-latest - timeout-minutes: 5 - outputs: - safe_to_proceed: ${{ steps.check.outputs.safe }} - blocked_reason: ${{ steps.check.outputs.reason }} - - steps: - - name: Checkout code - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - name: Security validation - id: check - run: | - SAFE="true" - REASON="" - - CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD) - - # CRITICAL: Block modifications to sensitive paths - BLOCKED_PATTERNS=( - ".github/workflows/" - ".github/actions/" - "secrets/" - ".env" - ".env.*" - "*.key" - "*.pem" - "credentials" - "CLAUDE.md" - ) - - for pattern in "${BLOCKED_PATTERNS[@]}"; do - if echo "$CHANGED_FILES" | grep -E "$pattern" > /dev/null; then - SAFE="false" - REASON="Modifications to protected files detected: $pattern. AI patch generation not allowed." - break - fi - done - - # Check for fork PRs (security risk) - if [ "${{ github.event.pull_request.head.repo.full_name }}" != "${{ github.repository }}" ]; then - SAFE="false" - REASON="PR from fork - AI patch generation disabled for security" - fi - - echo "safe=$SAFE" >> $GITHUB_OUTPUT - echo "reason=$REASON" >> $GITHUB_OUTPUT - - if [ "$SAFE" == "false" ]; then - echo "::error::$REASON" - fi - - claude-review: - name: Claude Code Review + Patch Generation - needs: security-check - if: | - needs.security-check.outputs.safe_to_proceed == 'true' && - contains(github.event.pull_request.labels.*.name, 'ai-patch') && - github.actor != 'claude[bot]' && - github.actor != 'dependabot[bot]' && - github.actor != 'github-actions[bot]' - - runs-on: ubuntu-latest - timeout-minutes: 20 - env: - BEDROCK_CONFIGURED: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK != '' }} - ANTHROPIC_KEY_SET: ${{ secrets.ANTHROPIC_API_KEY != '' }} - outputs: - has_suggestions: ${{ steps.parse.outputs.has_suggestions }} - patch_file: ${{ steps.parse.outputs.patch_file }} - - steps: - - name: Checkout code - uses: actions/checkout@v6 - with: - fetch-depth: 0 - token: ${{ secrets.GITHUB_TOKEN }} - - # ========== PRIMARY: AWS Bedrock ========== - - name: Claude Review (Bedrock - Primary) - id: bedrock - if: env.BEDROCK_CONFIGURED == 'true' - continue-on-error: true - uses: anthropics/claude-code-action@v1 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - use_bedrock: "true" - timeout_minutes: 15 - claude_args: >- - --allowed-tools - "Bash(git diff *),Bash(git log *),Read,Edit,Write" - --max-turns 25 - env: - AWS_BEARER_TOKEN_BEDROCK: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK }} - AWS_REGION: ${{ secrets.AWS_REGION || 'us-east-1' }} - CLAUDE_CODE_USE_BEDROCK: "1" - - # ========== FALLBACK: Anthropic Direct API ========== - - name: Claude Review (Anthropic - Fallback) - id: anthropic - if: | - env.ANTHROPIC_KEY_SET == 'true' && - (env.BEDROCK_CONFIGURED != 'true' || steps.bedrock.outcome != 'success') - continue-on-error: true - uses: anthropics/claude-code-action@v1 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} - timeout_minutes: 15 - claude_args: >- - --allowed-tools - "Bash(git diff *),Bash(git log *),Read,Edit,Write" - --max-turns 25 - - - name: Parse review results - id: parse - run: | - if git diff --quiet; then - echo "has_suggestions=false" >> $GITHUB_OUTPUT - echo "::notice::No code changes suggested by AI review" - else - echo "has_suggestions=true" >> $GITHUB_OUTPUT - - git diff > /tmp/ai-suggestions.patch - - if git apply --check /tmp/ai-suggestions.patch 2>&1; then - echo "patch_file=/tmp/ai-suggestions.patch" >> $GITHUB_OUTPUT - echo "::notice::Valid patch generated - $(git diff --stat | tail -1)" - else - echo "has_suggestions=false" >> $GITHUB_OUTPUT - echo "::error::Generated patch cannot be cleanly applied" - fi - fi - - - name: Upload patch artifact - if: steps.parse.outputs.has_suggestions == 'true' - uses: actions/upload-artifact@v7 - with: - name: ai-suggestions-patch - path: /tmp/ai-suggestions.patch - retention-days: 7 - - pal-consensus-validation: - name: Validate Suggestions with Multi-Model Consensus - needs: [security-check, claude-review] - if: needs.claude-review.outputs.has_suggestions == 'true' - runs-on: ubuntu-latest - timeout-minutes: 15 - outputs: - consensus_approve: ${{ steps.consensus.outputs.approved }} - consensus_reason: ${{ steps.consensus.outputs.reason }} - - steps: - - name: Checkout code - uses: actions/checkout@v6 - - - name: Download patch - uses: actions/download-artifact@v8 - with: - name: ai-suggestions-patch - path: /tmp/ - - - name: Prepare patch context - run: | - git apply --stat /tmp/ai-suggestions.patch > /tmp/patch-summary.txt - - - name: Run consensus on suggested changes - id: consensus - env: - PAL_MCP_API_KEY: ${{ secrets.PAL_MCP_API_KEY }} - PAL_MCP_ENDPOINT: ${{ secrets.PAL_MCP_ENDPOINT || 'https://pal-mcp.example.com/api/tools/consensus' }} - run: | - if [ -z "$PAL_MCP_API_KEY" ]; then - echo "::warning::PAL_MCP_API_KEY not set — skipping consensus, auto-approving" - echo "approved=true" >> $GITHUB_OUTPUT - echo "reason=Consensus skipped (PAL_MCP_API_KEY not configured)" >> $GITHUB_OUTPUT - exit 0 - fi - - PATCH_CONTENT=$(cat /tmp/ai-suggestions.patch | jq -R -s .) - PATCH_SUMMARY=$(cat /tmp/patch-summary.txt) - - cat > /tmp/consensus-request.json < /dev/null; then - echo "approved=true" >> $GITHUB_OUTPUT - else - echo "approved=false" >> $GITHUB_OUTPUT - fi - - echo "reason<> $GITHUB_OUTPUT - echo "$RECOMMENDATION" >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT - - create-draft-pr: - name: Create Draft PR with AI Suggestions - needs: [security-check, claude-review, pal-consensus-validation] - if: | - needs.claude-review.outputs.has_suggestions == 'true' && - needs.pal-consensus-validation.outputs.consensus_approve == 'true' - - runs-on: ubuntu-latest - timeout-minutes: 10 - - steps: - - name: Checkout code - uses: actions/checkout@v6 - with: - fetch-depth: 0 - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Download patch - uses: actions/download-artifact@v8 - with: - name: ai-suggestions-patch - path: /tmp/ - - - name: Apply AI suggestions - run: | - git apply /tmp/ai-suggestions.patch - - - name: Run linters and formatters - continue-on-error: true - run: | - if command -v ruff &> /dev/null; then - ruff check . --fix - ruff format . - fi - - - name: Run secret scanning - run: | - if git diff | grep -iE "(api[_-]?key|password|secret|token)" > /dev/null; then - echo "::warning::Potential secret detected in changes - manual review required" - fi - - - name: Create Draft Pull Request - id: create_pr - uses: peter-evans/create-pull-request@v8 - with: - token: ${{ secrets.GITHUB_TOKEN }} - commit-message: | - AI-suggested improvements from PR #${{ github.event.pull_request.number }} - - Generated by Claude Code Review + PAL MCP consensus validation. - - Co-Authored-By: Claude Opus 4.6 - branch: ai/auto-fix-pr-${{ github.event.pull_request.number }}-${{ github.run_id }} - delete-branch: true - draft: true - title: "AI Suggestions: ${{ github.event.pull_request.title }}" - body: | - ## Automated Improvements - - This draft PR contains AI-suggested improvements for PR #${{ github.event.pull_request.number }}. - - ### Review Process - - 1. **Claude Code Review**: Analyzed code and generated suggestions - 2. **PAL MCP Consensus**: Validated changes with 3 models (GPT-5-mini, Claude Haiku, Gemini-2-Flash) - 3. **Security Checks**: Passed pre-flight validation - 4. **Patch Application**: Applied cleanly - - ### Consensus Recommendation - - ${{ needs.pal-consensus-validation.outputs.consensus_reason }} - - ### Important - - - **This is a DRAFT PR** - requires human review before merge - - **Do not auto-merge** - maintainer approval required - - Review all changes carefully before approving - - Run full CI/CD pipeline before merging - - ### Metrics - - - Original PR: #${{ github.event.pull_request.number }} - - Models: Claude Opus 4.6, GPT-5-mini, Claude Haiku 4.5, Gemini-2-Flash - - Estimated cost: ~$5-8 - - Phase: 3 (Draft PR Creation) - - --- - Generated by [Claude Code](https://claude.com/claude-code) + [PAL MCP](https://github.com/BeehiveInnovations/pal-mcp-server) - labels: | - ai-generated - needs-human-review - draft - reviewers: ${{ github.event.pull_request.user.login }} - - - name: Comment on original PR - if: steps.create_pr.outputs.pull-request-number - uses: actions/github-script@v8 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const prNumber = '${{ steps.create_pr.outputs.pull-request-number }}'; - const prUrl = '${{ steps.create_pr.outputs.pull-request-url }}'; - - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - body: `## AI Suggestions Available - - I've analyzed this PR and created a draft PR with suggested improvements: **#${prNumber}** - - [View Draft PR with AI Suggestions](${prUrl}) - - ### What's included - - - Multi-model consensus validation (3 models) - - Security checks passed - - Patch applied cleanly - - ### Next steps - - 1. Review the suggested changes in draft PR #${prNumber} - 2. If approved, merge the draft PR - 3. That PR will auto-close when this PR is merged - - **The draft PR requires human approval** - do not auto-merge. - - --- - Estimated cost: ~$5-8 | Phase 3: Draft PR Creation` - }); - - - name: Workflow summary - if: always() - run: | - echo "### Draft PR Created" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "- **Draft PR**: #${{ steps.create_pr.outputs.pull-request-number }}" >> $GITHUB_STEP_SUMMARY - echo "- **URL**: ${{ steps.create_pr.outputs.pull-request-url }}" >> $GITHUB_STEP_SUMMARY - echo "- **Status**: Awaiting human review" >> $GITHUB_STEP_SUMMARY - echo "- **Phase**: 3 (Draft PR Creation)" >> $GITHUB_STEP_SUMMARY - - blocked-security: - name: Security Block Notice - needs: security-check - if: needs.security-check.outputs.safe_to_proceed == 'false' - runs-on: ubuntu-latest - timeout-minutes: 5 - - permissions: - issues: write - pull-requests: write - - steps: - - name: Post security notice - uses: actions/github-script@v8 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const reason = `${{ needs.security-check.outputs.blocked_reason }}`; - - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - body: `## AI Patch Generation Blocked - - ${reason} - - **Security Policy**: Automated patch generation is disabled for: - - Workflow files (.github/workflows/*) - - Secrets and credentials - - CLAUDE.md (manually maintained) - - PRs from forks - - You can still get a review comment by using the AI Code Review workflow.` - }); - -# Required secrets: -# - GITHUB_TOKEN (automatically provided) -# - (PAT_TOKEN removed — GITHUB_TOKEN is used for all operations) -# - ANTHROPIC_API_KEY or AWS_BEARER_TOKEN_BEDROCK (at least one required) -# - PAL_MCP_API_KEY (optional - consensus skipped if missing) -# - PAL_MCP_ENDPOINT (optional - has default) -# -# SECURITY NOTES: -# - Never auto-merges (always creates DRAFT PRs) -# - Blocks sensitive file modifications (including CLAUDE.md) -# - Requires human approval -# - Runs secret scanning on generated patches diff --git a/.github/workflows/setup-claude-review.sh b/.github/workflows/setup-claude-review.sh index 2fab74c..421db7c 100755 --- a/.github/workflows/setup-claude-review.sh +++ b/.github/workflows/setup-claude-review.sh @@ -171,26 +171,15 @@ install_workflows() { # Create workflows directory if it doesn't exist mkdir -p .github/workflows - case $PHASE in - phase1) - cp .github/workflows/claude-review-phase1.yml .github/workflows/claude-review.yml - success "Installed Phase 1 workflow" - ;; - phase2) - cp .github/workflows/claude-review-phase2.yml .github/workflows/claude-review.yml - success "Installed Phase 2 workflow" - ;; - phase3) - cp .github/workflows/claude-review-phase3.yml .github/workflows/claude-review.yml - success "Installed Phase 3 workflow" - ;; - all) - # Keep all phase files with their original names - success "All phase workflows available" - ;; - esac + # The consolidated workflow (ai-code-review.yml) is already in place + if [ -f .github/workflows/ai-code-review.yml ]; then + success "Consolidated AI code review workflow found" + else + error "ai-code-review.yml not found in .github/workflows/" + exit 1 + fi - # Install cost monitor + # Cost monitor if [ -f .github/workflows/ai-review-cost-monitor.yml ]; then success "Cost monitor workflow available" else diff --git a/tests/integration/test_workflow_configs.py b/tests/integration/test_workflow_configs.py index 2efc761..3493c8a 100644 --- a/tests/integration/test_workflow_configs.py +++ b/tests/integration/test_workflow_configs.py @@ -12,8 +12,7 @@ WORKFLOWS_DIR = Path(__file__).parent.parent.parent / ".github" / "workflows" CI_YML = WORKFLOWS_DIR / "ci.yml" -PHASE2_YML = WORKFLOWS_DIR / "claude-review-phase2.yml" -PHASE3_YML = WORKFLOWS_DIR / "claude-review-phase3.yml" +AI_CODE_REVIEW_YML = WORKFLOWS_DIR / "ai-code-review.yml" @pytest.fixture @@ -22,13 +21,9 @@ def ci_config(): @pytest.fixture -def phase2_config(): - return yaml.safe_load(PHASE2_YML.read_text()) - - -@pytest.fixture -def phase3_config(): - return yaml.safe_load(PHASE3_YML.read_text()) +def ai_code_review_config(): + """Consolidated AI code review workflow (replaces phase1/2/3).""" + return yaml.safe_load(AI_CODE_REVIEW_YML.read_text()) class TestYAMLValidity: @@ -113,90 +108,124 @@ def test_grep_pipefail_safety_test_files(self, ci_config): ) -class TestPhase2Workflow: - """Tests for claude-review-phase2.yml workflow configuration.""" - - def test_detect_high_stakes_job_exists(self, phase2_config): - assert "detect-high-stakes" in phase2_config["jobs"] - - def test_concurrency_includes_event_name(self, phase2_config): - """PR #91: Concurrency groups must include event_name to prevent cross-event cancellation.""" - jobs = phase2_config["jobs"] - for job_name, job_config in jobs.items(): - concurrency = job_config.get("concurrency") - if concurrency and concurrency.get("cancel-in-progress"): - group = concurrency["group"] - assert "event_name" in group, ( - f"Job '{job_name}' concurrency group '{group}' must include " - f"event_name to prevent issue_comment runs from cancelling pull_request runs" - ) - - def test_high_stakes_patterns_include_workflows(self, phase2_config): - """High-stakes detection includes .github/workflows/ changes.""" - steps = phase2_config["jobs"]["detect-high-stakes"]["steps"] - check_step = None - for step in steps: - if step.get("id") == "check" or "high-stakes" in step.get("name", "").lower(): - check_step = step - break - assert check_step is not None - assert ".github/workflows/" in check_step["run"] - - def test_triggers_on_pull_request_and_issue_comment(self, phase2_config): - """Phase 2 triggers on both pull_request and issue_comment events.""" - triggers = phase2_config[True] # 'on' key is True in YAML - assert "pull_request" in triggers - assert "issue_comment" in triggers - - def test_has_read_write_permissions(self, phase2_config): - """Workflow-level permissions include pull-requests: write.""" - perms = phase2_config.get("permissions", {}) - assert perms.get("pull-requests") == "write" - assert perms.get("contents") == "read" +class TestAICodeReviewWorkflow: + """Tests for consolidated ai-code-review.yml workflow (replaces phase1/2/3).""" + def test_has_preflight_job(self, ai_code_review_config): + """Preflight job gates all downstream work.""" + assert "preflight" in ai_code_review_config["jobs"] -class TestPhase3Workflow: - """Tests for claude-review-phase3.yml workflow configuration.""" + def test_has_review_job(self, ai_code_review_config): + """Review job performs comment-only review + consensus.""" + assert "review" in ai_code_review_config["jobs"] - def test_security_check_job_exists(self, phase3_config): - assert "security-check" in phase3_config["jobs"] + def test_has_autofix_job(self, ai_code_review_config): + """Autofix job creates draft PRs with fixes.""" + assert "autofix" in ai_code_review_config["jobs"] - def test_blocked_security_job_exists(self, phase3_config): - assert "blocked-security" in phase3_config["jobs"] + def test_review_depends_on_preflight(self, ai_code_review_config): + """Review job needs preflight to complete first.""" + job = ai_code_review_config["jobs"]["review"] + needs = job.get("needs") + assert needs == "preflight" or "preflight" in needs - def test_blocked_security_has_write_permissions(self, phase3_config): - """PR #91: blocked-security job needs issues: write to post security notice.""" - job = phase3_config["jobs"]["blocked-security"] - perms = job.get("permissions", {}) - assert perms.get("issues") == "write", ( - "blocked-security job needs 'issues: write' to post security notice comments" - ) - assert perms.get("pull-requests") == "write", ( - "blocked-security job needs 'pull-requests: write' for PR comment access" - ) + def test_autofix_depends_on_preflight_and_review(self, ai_code_review_config): + """Autofix job depends on both preflight and review.""" + job = ai_code_review_config["jobs"]["autofix"] + needs = job.get("needs", []) + if isinstance(needs, str): + needs = [needs] + assert "preflight" in needs, "autofix must depend on preflight" + assert "review" in needs, "autofix must depend on review" - def test_blocked_security_depends_on_security_check(self, phase3_config): - """blocked-security runs only when security check fails.""" - job = phase3_config["jobs"]["blocked-security"] - assert "security-check" in job.get("needs", []) or job.get("needs") == "security-check" + def test_triggers_on_pull_request_and_issue_comment(self, ai_code_review_config): + """Workflow triggers on both pull_request and issue_comment events.""" + triggers = ai_code_review_config[True] # 'on' key is True in YAML + assert "pull_request" in triggers + assert "issue_comment" in triggers - def test_security_check_blocks_workflow_files(self, phase3_config): - """Security check blocks modifications to .github/workflows/.""" - steps = phase3_config["jobs"]["security-check"]["steps"] + def test_has_concurrency_control(self, ai_code_review_config): + """Workflow has concurrency group to prevent parallel runs.""" + assert "concurrency" in ai_code_review_config + concurrency = ai_code_review_config["concurrency"] + assert concurrency.get("cancel-in-progress") is True + # Should include PR or issue number in group + group = concurrency["group"] + assert "pull_request.number" in group or "issue.number" in group + + def test_preflight_checks_high_stakes_files(self, ai_code_review_config): + """Preflight job detects high-stakes files for consensus.""" + steps = ai_code_review_config["jobs"]["preflight"]["steps"] + gate_step = None + for step in steps: + if step.get("id") == "gate" or "evaluate" in step.get("name", "").lower(): + gate_step = step + break + assert gate_step is not None, "Evaluate gates step not found" + run_script = gate_step["run"] + # Should check for workflow files in high-stakes patterns + assert ".github/workflows/" in run_script + # Should output should_consensus + assert "should_consensus" in run_script.lower() + + def test_autofix_blocks_protected_files(self, ai_code_review_config): + """Autofix job prevents modifications to protected files.""" + job = ai_code_review_config["jobs"]["autofix"] + steps = job["steps"] validation_step = None for step in steps: - if "security" in step.get("name", "").lower() or step.get("id") == "check": + if ( + "protect" in step.get("name", "").lower() + or "validate" in step.get("name", "").lower() + ): validation_step = step break - assert validation_step is not None - assert ".github/workflows/" in validation_step["run"] - - def test_has_concurrency_control(self, phase3_config): - """Phase 3 has workflow-level concurrency to prevent parallel runs.""" - assert "concurrency" in phase3_config - assert phase3_config["concurrency"].get("cancel-in-progress") is True - - def test_never_auto_merges(self, phase3_config): - """Phase 3 creates draft PRs, never auto-merges.""" - # The name or comment should indicate draft PR creation - assert "Draft PR" in phase3_config["name"] or "draft" in str(phase3_config).lower() + assert validation_step is not None, "Protected file validation not found" + run_script = validation_step["run"] + # Should block workflow files, secrets, env files + assert ".github/workflows/" in run_script + assert "secrets/" in run_script or ".env" in run_script + + def test_autofix_has_write_permissions(self, ai_code_review_config): + """Autofix job needs write permissions to create PRs.""" + job = ai_code_review_config["jobs"]["autofix"] + perms = job.get("permissions", {}) + assert perms.get("contents") == "write", "autofix needs contents:write" + assert perms.get("pull-requests") == "write", "autofix needs pull-requests:write" + + def test_review_has_read_and_pr_write_permissions(self, ai_code_review_config): + """Review job needs pull-requests: write to comment.""" + job = ai_code_review_config["jobs"]["review"] + perms = job.get("permissions", {}) + assert perms.get("pull-requests") == "write", "review needs pull-requests:write" + assert perms.get("contents") == "read", "review should only read contents" + + def test_uses_github_token_not_pat(self, ai_code_review_config): + """Autofix uses GITHUB_TOKEN to prevent recursive triggers.""" + job = ai_code_review_config["jobs"]["autofix"] + steps = job["steps"] + pr_create_step = None + for step in steps: + if "create-pull-request" in step.get("uses", ""): + pr_create_step = step + break + assert pr_create_step is not None, "create-pull-request step not found" + # Should use GITHUB_TOKEN, not PAT + with_block = pr_create_step.get("with", {}) + token = with_block.get("token", "") + assert "GITHUB_TOKEN" in token, "Must use GITHUB_TOKEN not PAT for recursive safety" + assert "PAT" not in token, "Must not use PAT_TOKEN" + + def test_creates_draft_prs_only(self, ai_code_review_config): + """Autofix creates draft PRs, never auto-merges.""" + job = ai_code_review_config["jobs"]["autofix"] + steps = job["steps"] + pr_create_step = None + for step in steps: + if "create-pull-request" in step.get("uses", ""): + pr_create_step = step + break + assert pr_create_step is not None + with_block = pr_create_step.get("with", {}) + # Should explicitly set draft: true + assert with_block.get("draft") == "true" or with_block.get("draft") is True