Skip to content

internal: land #264 Bugbot fix onto docs branch (proxy workaround) #33

internal: land #264 Bugbot fix onto docs branch (proxy workaround)

internal: land #264 Bugbot fix onto docs branch (proxy workaround) #33

name: Claude BugBot (Self-Built)
on:
pull_request:
types: [opened, synchronize, ready_for_review, reopened]
merge_group:
jobs:
bugbot-review:
name: BugBot PR Review
if: github.event_name == 'pull_request' || github.event_name == 'merge_group'
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
issues: read
id-token: write
steps:
- name: Merge group (no PR-scoped review)
if: github.event_name == 'merge_group'
run: |
echo "::notice::BugBot runs on pull_request only. PR-time runs satisfy this check before merge."
exit 0
- name: Checkout repository
if: github.event_name == 'pull_request'
uses: actions/checkout@v4
with:
fetch-depth: 0
# ── Step 1: Check credentials ──────────────────────────────────────────
# Step-level guard by necessity: the `secrets` context is not available
# in job-level `if:` expressions, only at step level (via env). Do not
# "simplify" this into a job-level condition — it will silently break.
- name: Check credentials
id: credentials
if: github.event_name == 'pull_request'
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
run: |
if [ -z "$ANTHROPIC_API_KEY" ] && [ -z "$CLAUDE_CODE_OAUTH_TOKEN" ]; then
echo "::notice title=BugBot skipped::No credentials set. Add ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN in repo secrets."
echo "configured=false" >> "$GITHUB_OUTPUT"
exit 0
fi
# A present-but-invalid ANTHROPIC_API_KEY fails the whole check (KI-027):
# the review action only dies after it has started, so a presence test
# alone can never catch it. Probe the key and stand down on a definitive
# rejection. Only 401/403 disarm the review — 200/429/5xx/timeouts all
# proceed, so a transient API wobble can never silently mute BugBot and
# a real action crash with a valid key still fails the check loudly.
if [ -n "$ANTHROPIC_API_KEY" ] && [ -z "$CLAUDE_CODE_OAUTH_TOKEN" ]; then
CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
https://api.anthropic.com/v1/models) || CODE="000"
if [ "$CODE" = "401" ] || [ "$CODE" = "403" ]; then
echo "::notice title=BugBot skipped::ANTHROPIC_API_KEY is set but rejected by the API (HTTP $CODE). Replace the repo secret to re-enable BugBot."
echo "configured=false" >> "$GITHUB_OUTPUT"
exit 0
fi
fi
echo "configured=true" >> "$GITHUB_OUTPUT"
# ── Step 2: Extract PR diff ────────────────────────────────────────────
- name: Extract PR diff
id: diff
if: github.event_name == 'pull_request' && steps.credentials.outputs.configured == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
DIFF=$(gh pr diff ${{ github.event.pull_request.number }} --repo ${{ github.repository }} 2>/dev/null | head -c 8000 || echo "Diff unavailable")
DIFF_ENCODED=$(echo "$DIFF" | python3 -c "import sys,urllib.parse; print(urllib.parse.quote(sys.stdin.read()))")
echo "diff_encoded=$DIFF_ENCODED" >> "$GITHUB_OUTPUT"
# ── Step 3: Run BugBot review ──────────────────────────────────────────
- name: Run BugBot Review
id: bugbot
if: github.event_name == 'pull_request' && steps.credentials.outputs.configured == 'true'
uses: anthropics/claude-code-action@1dc994ee7a008f0ecc866d9ac23ef036b7229f84
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
plugin_marketplaces: 'https://github.com/anthropics/claude-code.git'
plugins: 'code-review@claude-code-plugins'
prompt: |
/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}
You are a BugBot for the Equismile platform. Review this PR with extreme focus on real bugs, not style.
## Equismile Domain Context
- Equismile is a practice management platform for equine dental and veterinary practitioners
- TypeScript, React, Node.js, PostgreSQL (Prisma), Vercel
- Handles patient records (horses), appointment scheduling, clinical notes, invoicing
- Multi-practice: data isolation between practices is CRITICAL
- Financial data: invoice generation, payment processing, fee structures
- Regulatory: veterinary clinical records must be accurate and auditable
- DB migrations are high-risk: production migration workflow exists — schema changes must be backward compatible
## Severity Classification — USE THESE EXACTLY
Rate every finding with one of:
- 🔴 **[CRITICAL]** — Data loss, patient record corruption, cross-practice data leak, payment errors, broken auth
- 🟠 **[HIGH]** — Logic bug causing wrong clinical/financial results, broken appointment flow, migration risk
- 🟡 **[MEDIUM]** — Non-critical bug, error handling gap, performance regression risk
- 🔵 **[LOW]** — Code smell, minor improvement, readability
- ✅ **[PASS]** — No significant issues found
## What to look for (priority order)
1. **Cross-practice data leaks** — queries missing practice ID filters, patient records accessible across tenants
2. **Clinical record integrity** — missing validation on clinical notes, appointment data, horse/patient fields
3. **Financial accuracy** — invoice calculation bugs, fee rounding errors, payment status inconsistencies
4. **DB migration safety** — destructive schema changes without backward compatibility, missing `resolve-migration-drift` handling
5. **Auth and access control** — missing permission checks, practitioners accessing other practices' data
6. **Prisma/DB bugs** — missing transactions for multi-table writes, N+1 queries on appointment/patient lists
7. **TypeScript safety** — `any` casts hiding real type errors, unsafe non-null assertions on patient/clinical fields
8. **Vercel deploy risks** — env var mismatches, build-breaking changes in `vercel-deploy-trigger.yml` touchpoints
## Output format
Start with a one-line verdict:
> **BugBot verdict: [CRITICAL|HIGH|MEDIUM|LOW|PASS]** — <one sentence summary>
Then list findings as:
```
🔴 [CRITICAL] src/practices/queries.ts:87 — Missing practiceId filter on patient query
Suggested fix: Add `where: { practiceId: session.practiceId }` to all patient lookups
```
End with:
> **Block merge: YES/NO** (YES for CRITICAL or HIGH findings)
Keep the review concise and actionable. Do not comment on formatting, linting, or things already caught by CI.