Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions .github/workflows/claude-code-review.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
name: Claude Code Review

on:
pull_request:
types: [opened, synchronize, ready_for_review, reopened]

jobs:
claude-review:
if: github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]'
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
issues: read
id-token: write

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1

- name: Run Claude Code Review
id: claude-review
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
prompt: |
REPO: ${{ github.repository }}
PR NUMBER: ${{ github.event.pull_request.number }}

You are an expert Laravel code reviewer. Read CLAUDE.md first — it defines the project stack, versions, conventions, and architecture (Laravel + Livewire + Tailwind + Pest). Every review comment must be grounded in those standards and in composer.json for exact versions.

This is the backend for Git Slot Machine: a public leaderboard site plus the play-recording API. The play API is intentionally **server-authoritative** — `PlayController` recomputes the pattern and payout from the commit hash via `App\Services\PatternDetector` and never trusts client-supplied pattern/payout. Treat any change that would let a client influence its own payout, bypass that server-side computation, or otherwise game the leaderboard as a serious correctness/security issue.

If the PR body references issues (e.g., "Closes #N"), read them with `gh issue view N` for context before reviewing.

If this PR already has prior review comments (a re-review after new commits), read them with `gh pr view ${{ github.event.pull_request.number }} --json comments` and treat them as context. Acknowledge or build on what previous reviews said and how the author responded — don't re-flag points that were already addressed, and don't restate issues the author pushed back on without engaging with their reasoning.

Review this PR with the rigor of a senior Laravel engineer. Focus on:

**Laravel & PHP quality:**
- Correct Eloquent use (relationships, eager loading, no N+1, no raw `DB::` unless justified)
- Form Requests for validation (not inline in controllers)
- Return type declarations and parameter type hints on all methods
- Modern PHP used appropriately (constructor promotion, enums, match)
- Config via `config()` not `env()` outside config files
- Named routes with `route()` for URL generation
- Queued jobs for heavy/slow operations (e.g. Browsershot image generation)

**Architecture & conventions:**
- Follows the existing directory structure and naming (check sibling files)
- Controllers stay thin; pattern/scoring logic stays in the shared service so the CLI and server agree
- No new dependencies without clear justification
- Migrations are safe and repeat all column attributes when modifying

**Security:**
- No mass-assignment, SQL injection, or SSRF vectors
- No XSS in Blade (`{{ }}` not `{!! !!}` unless justified)
- Public/unauthenticated endpoints validate and rate-appropriately; user-supplied content runs through the profanity filter / moderation where relevant
- OWASP Top 10 awareness

**Testing (Pest):**
- Every change has corresponding Pest tests in tests/Feature or tests/Unit
- Tests use model factories (check for existing factory states before manual setup)
- Happy paths, failure paths, and edge cases covered
- Assertions use specific helpers (assertForbidden, assertNotFound) over assertStatus(...)

**Code style:**
- Curly braces on all control structures
- PHPDoc blocks preferred over inline comments
- Descriptive names (isRegisteredForDiscounts, not discount())

**What NOT to flag:**
- Features or refactors beyond the PR's stated scope
- Formatting that Pint handles
- Intentional design decisions documented in CLAUDE.md

Be direct and constructive. If the PR is solid, say so briefly — don't invent issues. If there are real problems, be specific about what is wrong and what the fix should be.

IMPORTANT: Do NOT use #N (e.g. #1, #2, #3) to number your feedback items — GitHub auto-links #N to issue numbers. Use bullet points, letters (a, b, c), or descriptive headers instead.

Use `gh pr comment` with your Bash tool to leave your review as a comment on the PR.

claude_args: '--model opus --allowed-tools "Bash(gh issue view:*),Bash(gh search:*),Bash(gh issue list:*),Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*),Bash(php artisan test:*),Bash(vendor/bin/pint:*)"'
52 changes: 52 additions & 0 deletions .github/workflows/claude.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: Claude Code

on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
issues:
types: [opened, assigned]
pull_request_review:
types: [submitted]

jobs:
claude:
if: |
(
github.event.sender.login == github.repository_owner ||
github.event.comment.author_association == 'OWNER' ||
github.event.comment.author_association == 'MEMBER' ||
github.event.comment.author_association == 'COLLABORATOR' ||
github.event.issue.author_association == 'OWNER' ||
github.event.issue.author_association == 'MEMBER' ||
github.event.issue.author_association == 'COLLABORATOR' ||
github.event.review.author_association == 'OWNER' ||
github.event.review.author_association == 'MEMBER' ||
github.event.review.author_association == 'COLLABORATOR'
) && (
(github.event_name == 'issue_comment' && 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')) ||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
)
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
issues: write
id-token: write
actions: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1

- name: Run Claude Code
id: claude
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
additional_permissions: |
actions: read
Loading