diff --git a/.github/claude-review-prompt.md b/.github/claude-review-prompt.md new file mode 100644 index 0000000000..496820a8f0 --- /dev/null +++ b/.github/claude-review-prompt.md @@ -0,0 +1,63 @@ +You are an experienced senior React Native engineer reviewing a pull +request in the Bluesky Social app — a cross-platform (iOS, Android, Web) +React Native + Expo application. Read the repo's CLAUDE.md before forming +an opinion; it describes the architecture, the ALF design system, and the +codebase conventions. + +Your audience is other senior engineers. Write peer-to-peer, not +teacher-to-junior. Most PRs in this repo are fine; a review that says so +is a valid and common outcome. + +Report a finding only if you can name a concrete scenario — specific +input, platform, navigation path, or operating condition — in which the +change causes incorrect behavior, a crash, a visual regression, a test +failure, a security issue, or a real regression visible to users. Style, +naming, and micro-optimizations are out of scope unless they introduce a +defect. Do not speculate that a change "might" break unrelated code +without pointing to the specific caller or code path. Do not repeat what +the diff does. + +Where this codebase differs from a typical web app: + +- Three platforms from one codebase. Web-only APIs (DOM, window), + native-only modules, and platform-specific files (.web.tsx, .ios.tsx, + .android.tsx) are common sources of single-platform breakage. When a + change touches shared code, consider all three targets. +- User-facing strings must go through Lingui (the `Trans` macro / + `useLingui`). Hardcoded English strings in UI are a finding. Do not + flag missing translations in catalog files — extraction and + compilation run in CI. +- New UI should use ALF (`#/alf`, `#/components`) rather than legacy + patterns (`#/view/com`, StyleSheet.create); flag newly written code + that adopts deprecated patterns, but don't flag pre-existing code the + PR merely touches. +- Server state lives in TanStack Query under src/state/queries. Watch + for cache-shape changes without corresponding invalidation updates, + and optimistic updates that can leave stale cache on failure. +- List rendering is performance-critical (the main feed). Changes to + feed items, FlatList usage, or anything in a hot render path deserve + scrutiny for re-render storms — unstable callback/object identities + passed to memoized children, missing memoization on expensive + computation. +- Moderation and content-filtering logic (labels, mutes, blocks, + hidden posts) is trust-and-safety-critical: a regression that shows + content that should be filtered is a blocking finding. +- Deep links, push-notification routing, and the navigation state + machine have platform-specific edge cases; changes there should name + the platforms they were verified on. +- The embed (bskyembed) and web deployment surfaces (bskyweb, link, + ogcard services in Go) ship separately from the app; changes there + have their own blast radius. + +For each finding, state the scenario in one or two sentences, cite +file:line, and mark severity (blocking / non-blocking). If you are +uncertain but the potential impact is high (crash on startup, moderation +bypass, broken auth), include it and say what you are uncertain about. +Otherwise, prefer silence over guessing. + +If there are no findings that meet this bar, say briefly that the PR +looks fine and note what you checked. + +Post your review as a single top-level PR comment. Per-finding inline +comments are also welcome where they'd anchor a reader to the specific +lines involved. diff --git a/.github/workflows/claude-mention.yml b/.github/workflows/claude-mention.yml new file mode 100644 index 0000000000..355a05adfb --- /dev/null +++ b/.github/workflows/claude-mention.yml @@ -0,0 +1,76 @@ +name: claude-mention + +# @claude mention handler on PR conversation comments, inline review +# comments, and review bodies. See the header comment in +# claude-review.yml for why this is self-contained rather than calling +# the org reusable workflows. +# +# Authorization note: this repo is public, and the load-bearing gate +# against drive-by commenters is INSIDE claude-code-action — the action +# verifies via the API that the triggering actor has write permission +# before doing anything (`allowed_non_write_users` and `allowed_bots` +# both default to deny). The `if:` below is a cheap pre-filter to avoid +# spinning up runners for the 99% of comments that don't mention +# @claude; it is not the security boundary. + +on: + issue_comment: + types: [created] + pull_request_review_comment: + types: [created] + pull_request_review: + types: [submitted] + +permissions: + contents: read + pull-requests: write + issues: write + actions: read + id-token: write + +jobs: + mention: + # Skip comments/reviews that don't mention @claude, non-PR issue + # comments, and events from claude[bot] itself (its review prose + # often quotes "@claude" and must not retrigger the workflow). + if: > + ( + (github.event_name == 'issue_comment' && + github.event.issue.pull_request != null && + 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.actor != 'claude[bot]' + + runs-on: ubuntu-latest + timeout-minutes: 20 + + concurrency: + group: claude-mention-${{ github.repository }}-${{ github.event.issue.number || github.event.pull_request.number }} + cancel-in-progress: false + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 1 + + - name: Configure AWS credentials (OIDC) + uses: aws-actions/configure-aws-credentials@v6 + with: + role-to-assume: ${{ secrets.AWS_BEDROCK_REVIEW_ROLE_ARN }} + aws-region: us-east-2 + + - name: Claude + uses: anthropics/claude-code-action@v1 + with: + use_bedrock: 'true' + additional_permissions: | + actions: read + track_progress: true + claude_args: | + --model global.anthropic.claude-opus-4-8 + --allowedTools "mcp__github_inline_comment__create_inline_comment,mcp__github_ci__get_ci_status,mcp__github_ci__download_job_log,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*)" diff --git a/.github/workflows/claude-review.yml b/.github/workflows/claude-review.yml new file mode 100644 index 0000000000..9b7cdaa57d --- /dev/null +++ b/.github/workflows/claude-review.yml @@ -0,0 +1,68 @@ +name: claude-review + +# Automatic Claude review on PR creation/update, via Bedrock (OIDC, no +# long-lived tokens). Self-contained: this intentionally uses upstream +# claude-code-action defaults rather than the org reusable workflows in +# bluesky-social/.github (which a public repo cannot call, and whose +# customizations added no value over upstream). +# +# Review guidance lives in .github/claude-review-prompt.md. + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + +permissions: + contents: read + pull-requests: write + actions: read + id-token: write + +jobs: + review: + # Internal PRs only. This repo is public: fork PRs are the bulk of + # community traffic and MUST NOT trigger reviews (no Bedrock spend on + # unvetted code, and fork PRs can't mint the OIDC token anyway — + # belt-and-braces with this explicit guard). Branch PRs can only be + # created by people with write access, i.e. org members. + # Bot-authored PRs (dependabot, changesets) are also skipped. + if: > + github.event.pull_request.draft == false && + github.event.pull_request.head.repo.full_name == github.repository && + github.event.pull_request.user.type != 'Bot' + + runs-on: ubuntu-latest + timeout-minutes: 20 + + concurrency: + group: claude-review-${{ github.repository }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 1 + + - name: Configure AWS credentials (OIDC) + uses: aws-actions/configure-aws-credentials@v6 + with: + role-to-assume: ${{ secrets.AWS_BEDROCK_REVIEW_ROLE_ARN }} + aws-region: us-east-2 + + - name: Claude review + uses: anthropics/claude-code-action@v1 + with: + use_bedrock: 'true' + additional_permissions: | + actions: read + track_progress: true + claude_args: | + --model global.anthropic.claude-opus-4-8 + --allowedTools "mcp__github_inline_comment__create_inline_comment,mcp__github_ci__get_ci_status,mcp__github_ci__download_job_log,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*)" + prompt: | + REPO: ${{ github.repository }} + PR NUMBER: ${{ github.event.pull_request.number }} + + Read .github/claude-review-prompt.md in the checked-out repo + and review this pull request following its guidance. diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml deleted file mode 100644 index 7e4f9ca1bc..0000000000 --- a/.github/workflows/claude.yml +++ /dev/null @@ -1,54 +0,0 @@ -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_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: read - issues: read - id-token: write - actions: read # Required for Claude to read CI results on PRs - steps: - - name: Checkout repository - uses: actions/checkout@v5 - 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 }} - - # This is an optional setting that allows Claude to read CI results on PRs - additional_permissions: | - actions: read - - # Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it. - # prompt: 'Update the pull request description to include a summary of changes.' - - # Optional: Add claude_args to customize behavior and configuration - # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md - # or https://code.claude.com/docs/en/cli-reference for available options - # claude_args: '--allowed-tools Bash(gh pr:*)' - - # NOTE(sfn): we can add a custom system prompt here - - claude_args: | - --model claude-opus-4-8