diff --git a/.claude/review-prompt.md b/.claude/review-prompt.md new file mode 100644 index 0000000..5f448d1 --- /dev/null +++ b/.claude/review-prompt.md @@ -0,0 +1,41 @@ +# Claude Code Review Prompt + +You are reviewing pull requests for moqueue. Apply the project rules from CLAUDE.md/AGENTS.md (loaded separately) in addition to this guidance. + +**Only comment when you have actionable feedback. Never post "looks good", "no issues found", or summary-only comments.** + +## What to Evaluate + +- **Correctness**: the change should solve the stated problem, handle edge cases, and be free of bugs and regressions. +- **Code quality, readability, and maintainability**: the change should be clean, easy to follow, and sustainable to maintain and modify. +- **Conventions**: the change should follow this repo's existing patterns and conventions. It should avoid introducing or perpetuating anti-patterns. Do not re-teach language basics — ground convention feedback in what this codebase actually does. +- **Performance**: the change should avoid N+1 queries, excess API calls, unnecessary re-renders, unbounded list operations, and avoidable complexity. +- **Security**: the change should validate and sanitize inputs, enforce authentication and authorization correctly, protect sensitive data, and avoid common web security risks. +- **Testing**: the change should include coverage for changed behavior, failure modes, and important edge cases. Tests should assert meaningful behavior, not just exercise code. +- **Design, architecture, and scope**: the change should be well-designed, make worthwhile tradeoffs, fit the rest of the codebase cleanly, and remain reasonably scoped. +- **Dependencies**: any added library should be necessary and used appropriately. + +## Findings + +Categorize each finding: + +- **Critical**: broken behavior, likely regressions, data loss, security issues, missing authorization, or defects that should block merge +- **Suggestion**: meaningful improvements to correctness, resilience, performance, maintainability, or test coverage +- **Nit**: minor style or readability feedback that is clearly optional +- **Question**: uncertainty about intent, tradeoffs, or missing context + +## PR Context + +- Link to the project pitch or Jira ticket in the PR body when applicable + +## Review Behavior + +- Focus on issues that matter. Do not pad the review with trivial comments. +- Explain what is wrong, why it matters, and suggest a concrete fix when possible. If unsure, say so and phrase it as a question. +- Flag change hygiene issues when unrelated changes are mixed in or the work should be split into smaller reviewable pieces. +- Call out genuinely good choices when they are specific and notable. +- Group related issues into a single review comment +- Reference specific lines using GitHub line-link format +- Skip auto-generated files (lockfiles, codegen output, Vite build artifacts) +- If the entire PR looks good, do not post a comment at all +- Read all previous review comments before posting. Do not repeat feedback that has already been given — whether by a human reviewer or a prior bot review. If a previous comment flagged an issue and it remains unfixed, you may briefly note it persists but do not re-explain. diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml new file mode 100644 index 0000000..7634740 --- /dev/null +++ b/.github/workflows/claude-code-review.yml @@ -0,0 +1,128 @@ +name: Claude Code Review + +on: + pull_request: + types: [opened, synchronize, ready_for_review, reopened] + issue_comment: + types: [created] + pull_request_review_comment: + types: [created] + pull_request_review: + types: [submitted] + +permissions: + contents: read + pull-requests: write + issues: read + id-token: write + +jobs: + review: + if: | + (github.event_name == 'pull_request' && github.event.pull_request.draft == false) || + (github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(github.event.comment.body, '@claude')) || + (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) || + (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) + concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.event.issue.number || github.run_id }} + cancel-in-progress: true + runs-on: ubuntu-latest + steps: + - name: Resolve PR number + id: pr + run: | + if [ "${{ github.event_name }}" = "issue_comment" ]; then + echo "number=${{ github.event.issue.number }}" >> "$GITHUB_OUTPUT" + else + echo "number=${{ github.event.pull_request.number }}" >> "$GITHUB_OUTPUT" + fi + + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: refs/pull/${{ steps.pr.outputs.number }}/head + fetch-depth: 0 + + - name: Fetch PR diff + env: + GH_TOKEN: ${{ github.token }} + run: | + gh pr diff "${{ steps.pr.outputs.number }}" > /tmp/pr-diff.patch + + - name: Fetch previous reviews + env: + GH_TOKEN: ${{ github.token }} + run: | + PR_NUM="${{ steps.pr.outputs.number }}" + REPO="${{ github.repository }}" + { + echo "=== Review Summaries ===" + gh api "repos/${REPO}/pulls/${PR_NUM}/reviews" --paginate \ + --jq '.[] | select(.state != "PENDING") | "[\(.user.login)] \(.state)\n\(.body)\n---"' 2>/dev/null || true + + echo "" + echo "=== Inline Review Comments ===" + gh api "repos/${REPO}/pulls/${PR_NUM}/comments" --paginate \ + --jq '.[] | "[\(.user.login)] \(.path):\(.line // .original_line)\n\(.body)\n---"' 2>/dev/null || true + + echo "" + echo "=== PR Conversation Comments ===" + gh api "repos/${REPO}/issues/${PR_NUM}/comments" --paginate \ + --jq '.[] | "[\(.user.login)]\n\(.body)\n---"' 2>/dev/null || true + } > /tmp/previous-reviews.txt + + - name: Build review prompt + id: prompt + run: | + DELIMITER="PROMPT_EOF_$(uuidgen)" + { + echo "REVIEW_PROMPT<<${DELIMITER}" + for f in CLAUDE.md AGENTS.md; do + if [ -f "$f" ]; then + cat "$f" + echo "" + fi + done + if [ -f ".claude/review-prompt.md" ]; then + cat .claude/review-prompt.md + fi + echo "${DELIMITER}" + } >> "$GITHUB_OUTPUT" + + - name: Resolve claude_args + id: args + env: + MODEL: ${{ vars.CLAUDE_REVIEW_MODEL }} + run: | + ALLOWED_TOOLS='Read,Glob,Grep,mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr view:*)' + if [ -n "$MODEL" ]; then + echo "value=--model ${MODEL} --max-turns 10 --allowedTools \"${ALLOWED_TOOLS}\"" >> "$GITHUB_OUTPUT" + else + echo "value=--max-turns 10 --allowedTools \"${ALLOWED_TOOLS}\"" >> "$GITHUB_OUTPUT" + fi + + - name: Claude Code Review + continue-on-error: true + uses: anthropics/claude-code-action@v1 + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + allowed_bots: "custom-ink-triton" + prompt: | + You are reviewing PR #${{ steps.pr.outputs.number }}. + The full diff is in /tmp/pr-diff.patch — Read that file first. + Previous review comments are in /tmp/previous-reviews.txt — Read that file next. + Do NOT run gh pr diff or git diff to re-fetch it. + Use Read on source files if you need more context beyond the diff. + + IMPORTANT: Do NOT repeat or reiterate feedback that has already been given + in previous reviews. If a prior reviewer (human or bot) already flagged an + issue and the author has not addressed it in the current diff, you may + reference it briefly but do not re-explain. Focus your review on NEW issues + not covered by prior feedback. + + Post inline review comments and stop. + + Review this pull request using the following guidelines: + + ${{ steps.prompt.outputs.REVIEW_PROMPT }} + claude_args: "${{ steps.args.outputs.value }}" diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..88a70b3 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,26 @@ +# Moqueue + +Ruby gem for mocking the AMQP library in tests. Allows testing AMQP-based code without running a broker by providing mock replacements for queues, exchanges, and channels. + +## Tech Stack + +- **Language:** Ruby (see `moqueue.gemspec` for dependencies) +- **Type:** Gem (consumed via Bundler) +- **Key dependency:** `amqp` >= 0.9.0 + +## Local Development & Testing + +Run `bundle install` and `bundle exec rake spec`. Test framework: RSpec. + +## Key Directories + +| Path | Purpose | +|------|---------| +| `lib/moqueue/` | Mock implementations (MockQueue, MockExchange, MockChannel) | +| `spec/` | RSpec suite | + +## Architecture Notes + +- Provides `overload_amqp` to globally replace AMQP classes with mocks +- Captures published messages in a `routed_messages` array for assertions +- Used by other Custom Ink services (e.g. journal-service) for testing AMQP workflows diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..224a109 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,3 @@ +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +@AGENTS.md