feat: add CodeRabbit integration for AI-powered code review#1315
feat: add CodeRabbit integration for AI-powered code review#1315jeremyeder wants to merge 1 commit intoambient-code:mainfrom
Conversation
Full-stack integration following the Jira pattern and PR ambient-code#1307 conventions. Implements infrastructure for ADR-0008 (automated inner-loop review). Backend (Go): - Auth handlers: connect, status, disconnect, test (K8s Secret storage) - API key validation against CodeRabbit health API with error differentiation (401/403 = invalid key, 5xx = upstream error) - Runtime credential endpoint with RBAC for session pods - Unified integrations status includes CodeRabbit - 16 Ginkgo tests Frontend (Next.js + React): - Informational-first connection card: public repos free via GitHub App, API key collapsed under "Private repository access" with billing warning - API client layer + React Query hooks with dual cache invalidation - 4 Next.js proxy routes - Wired into IntegrationsClient grid and session integrations panel Runner (Python): - fetch_coderabbit_credentials via shared _fetch_credential helper - CODERABBIT_API_KEY injected into session env via asyncio.gather - Cleared on turn completion Pre-commit hook: - Runs coderabbit review --agent on staged changes - Supports both CODERABBIT_API_KEY env and cr auth login session - CLI reads env var directly (no --api-key in process listing) - Skips gracefully when CLI/auth/changes unavailable CI + Testing: - GHA smoke test: validates config, runs live review, tests hook behavior (actions pinned to SHAs, permissions scoped) - Integration test script: 9/9 passing against dev cluster Docs: - Starlight guide: public vs private repos, local dev, session flow - ADR-0008: automated code reviews via inner-loop + Mergify - PR ambient-code#1307 impact analysis Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughAdds CodeRabbit integration across the platform, enabling both organization-wide and per-user API key management. Includes backend credential persistence in Kubernetes, frontend UI for key management, session credential injection, pre-commit hook for local review automation, and comprehensive test coverage via GHA and shell scripts. Changes
Sequence Diagram(s)sequenceDiagram
participant User as User (Browser)
participant Frontend as Frontend
participant Backend as Backend API
participant CodeRabbit as CodeRabbit API
participant K8s as Kubernetes<br/>(Secret Storage)
participant Runner as Session Runner
User->>Frontend: 1. Navigate to Integrations
Frontend->>Backend: 2. POST /api/auth/coderabbit/connect<br/>{ apiKey: "cr-..." }
Backend->>CodeRabbit: 3. Validate key via /api/v1/health<br/>(Bearer token)
CodeRabbit-->>Backend: 4. HTTP 200 (valid) or 401/403
Backend->>K8s: 5. Store credentials in<br/>coderabbit-credentials Secret
K8s-->>Backend: 6. Secret created/updated
Backend-->>Frontend: 7. HTTP 200 { message: "connected" }
Frontend-->>User: 8. Show "Connected" status
Note over User,Backend: Later: User starts an agentic session
User->>Frontend: 9. Create/join session
Frontend->>Backend: 10. Start session (auth token)
Backend->>K8s: 11. GET coderabbit-credentials<br/>for user
K8s-->>Backend: 12. Return apiKey
Backend->>Runner: 13. Inject CODERABBIT_API_KEY<br/>into session environment
Runner-->>Backend: 14. Environment ready
Backend-->>Frontend: 15. Session started
Frontend-->>User: 16. Session active
Note over Runner,CodeRabbit: During session execution
Runner->>CodeRabbit: 17. Use $CODERABBIT_API_KEY<br/>for code review requests
CodeRabbit-->>Runner: 18. Review results
Note over User,Backend: Session cleanup
Runner->>Runner: 19. Clear CODERABBIT_API_KEY<br/>from environment
Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 error, 2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
⚔️ Resolve merge conflicts
✨ Simplify code
Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
components/frontend/src/app/integrations/IntegrationsClient.tsx (1)
15-16:⚠️ Potential issue | 🟠 MajorHandle query errors explicitly before rendering integration cards.
If
useIntegrationsStatus()fails, this component drops into the non-loading branch and renders cards with undefined statuses, masking transport/auth failures as "disconnected." The hook returnserrorandisErrorfields (via React Query'suseQuery), but you're not destructuring them. Add anisErrorbranch with a clear failure UI before the grid render (lines 32–37).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/frontend/src/app/integrations/IntegrationsClient.tsx` around lines 15 - 16, The component currently only destructures { data: integrations, isLoading, refetch } from useIntegrationsStatus() and will render integration cards with undefined statuses when the query errors; update the destructuring to also grab isError and error from useIntegrationsStatus(), add an early-return branch that checks isError and renders a clear failure UI (e.g., a message/button to retry using refetch) before the grid of cards is rendered, and ensure any places referencing integrations handle the absence of data safely; look for useIntegrationsStatus, integrations, isLoading, isError, error, refetch and the grid render to implement this change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/coderabbit-smoke-test.yml:
- Around line 86-90: The workflow currently passes the secret via the --api-key
flag in the coderabbit review invocation (the command that builds OUTPUT and
EXIT_CODE), which exposes it in process args; remove the --api-key
"$CODERABBIT_API_KEY" argument and rely on the CODERABBIT_API_KEY environment
variable being available to the CLI instead (ensure the step exports or sets
CODERABBIT_API_KEY in the environment or uses GitHub Actions secrets mapping so
coderabbit reads it from process.env rather than from a command-line flag).
In `@components/backend/handlers/integration_validation.go`:
- Around line 232-241: The TestCodeRabbitConnection handler currently begins
validation without establishing request-scoped auth; call
GetK8sClientsForRequest(c) at the start of TestCodeRabbitConnection, check its
returned error (and any nil client), and if it fails return the appropriate HTTP
error JSON and stop processing before binding/validating the API key—this
ensures the endpoint uses the request-scoped k8s client instead of the backend
service account and follows the same auth pattern as other user-facing
endpoints.
In `@components/frontend/src/app/api/auth/coderabbit/status/route.ts`:
- Around line 4-14: The GET handler currently throws on upstream fetch failures
and forces 'application/json'; wrap the fetch in try/catch, and when the fetch
succeeds read the upstream body (e.g., resp.text()) and return new
Response(body, { status: resp.status, headers: { 'Content-Type':
resp.headers.get('content-type') || 'text/plain' } }); if resp.ok is false still
forward the upstream status and content-type (fall back to 'text/plain' if
missing); in the catch block return a 502 Response with the error.message and
'text/plain' content-type. Apply the same pattern to the sibling handlers
(disconnect, test, connect and other integration routes) so they preserve
upstream content-type and handle network/fetch errors gracefully.
In `@scripts/test-coderabbit-integration.sh`:
- Around line 180-187: The health check is using a hardcoded port 9323 instead
of the discovered FRONTEND_PORT; update the curl invocation that sets
FRONTEND_STATUS to use the FRONTEND_PORT variable (e.g.
"http://localhost:${FRONTEND_PORT}/integrations") and keep the same error
fallback (|| echo "000"), preserving the surrounding conditional that checks
FRONTEND_PORT != "NONE" and the pass/skip calls (FRONTEND_STATUS, pass, skip).
---
Outside diff comments:
In `@components/frontend/src/app/integrations/IntegrationsClient.tsx`:
- Around line 15-16: The component currently only destructures { data:
integrations, isLoading, refetch } from useIntegrationsStatus() and will render
integration cards with undefined statuses when the query errors; update the
destructuring to also grab isError and error from useIntegrationsStatus(), add
an early-return branch that checks isError and renders a clear failure UI (e.g.,
a message/button to retry using refetch) before the grid of cards is rendered,
and ensure any places referencing integrations handle the absence of data
safely; look for useIntegrationsStatus, integrations, isLoading, isError, error,
refetch and the grid render to implement this change.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 72b2e4da-a160-49a3-a5f6-cd8a48769aa9
📒 Files selected for processing (27)
.github/workflows/coderabbit-smoke-test.yml.pre-commit-config.yamlcomponents/backend/handlers/coderabbit_auth.gocomponents/backend/handlers/coderabbit_auth_test.gocomponents/backend/handlers/integration_validation.gocomponents/backend/handlers/integrations_status.gocomponents/backend/handlers/runtime_credentials.gocomponents/backend/routes.gocomponents/backend/tests/constants/labels.gocomponents/frontend/src/app/api/auth/coderabbit/connect/route.tscomponents/frontend/src/app/api/auth/coderabbit/disconnect/route.tscomponents/frontend/src/app/api/auth/coderabbit/status/route.tscomponents/frontend/src/app/api/auth/coderabbit/test/route.tscomponents/frontend/src/app/integrations/IntegrationsClient.tsxcomponents/frontend/src/app/projects/[name]/sessions/[sessionName]/components/settings/integrations-panel.tsxcomponents/frontend/src/components/coderabbit-connection-card.tsxcomponents/frontend/src/services/api/coderabbit-auth.tscomponents/frontend/src/services/api/integrations.tscomponents/frontend/src/services/queries/use-coderabbit.tscomponents/runners/ambient-runner/ambient_runner/platform/auth.pydocs/internal/adr/0008-automate-code-reviews.mddocs/pr-1307-impact-analysis.mddocs/src/content/docs/features/coderabbit.mdscripts/pre-commit/coderabbit-review.shscripts/test-coderabbit-integration.shspecs/001-coderabbit-integration/checklists/requirements.mdspecs/001-coderabbit-integration/spec.md
| OUTPUT=$(coderabbit review \ | ||
| --agent \ | ||
| --files .coderabbit.yaml \ | ||
| --api-key "$CODERABBIT_API_KEY" \ | ||
| 2>&1) || EXIT_CODE=$? |
There was a problem hiding this comment.
Do not pass CODERABBIT_API_KEY as a command-line argument
Line 89 sends the key via --api-key, which can leak through process args. Keep it in environment only and let the CLI read CODERABBIT_API_KEY.
Safer command usage
OUTPUT=$(coderabbit review \
--agent \
--files .coderabbit.yaml \
- --api-key "$CODERABBIT_API_KEY" \
2>&1) || EXIT_CODE=$?As per coding guidelines, verify secrets are not exposed and permissions are scoped.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/coderabbit-smoke-test.yml around lines 86 - 90, The
workflow currently passes the secret via the --api-key flag in the coderabbit
review invocation (the command that builds OUTPUT and EXIT_CODE), which exposes
it in process args; remove the --api-key "$CODERABBIT_API_KEY" argument and rely
on the CODERABBIT_API_KEY environment variable being available to the CLI
instead (ensure the step exports or sets CODERABBIT_API_KEY in the environment
or uses GitHub Actions secrets mapping so coderabbit reads it from process.env
rather than from a command-line flag).
| // TestCodeRabbitConnection handles POST /api/auth/coderabbit/test | ||
| func TestCodeRabbitConnection(c *gin.Context) { | ||
| var req struct { | ||
| APIKey string `json:"apiKey" binding:"required"` | ||
| } | ||
|
|
||
| if err := c.ShouldBindJSON(&req); err != nil { | ||
| c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | ||
| return | ||
| } |
There was a problem hiding this comment.
Add the request-scoped auth gate before validating CodeRabbit keys.
Line 233 begins request processing without the GetK8sClientsForRequest(c) check used by other user-facing auth endpoints. This leaves the test endpoint outside the required request-auth pattern.
🔧 Proposed fix
func TestCodeRabbitConnection(c *gin.Context) {
+ reqK8s, _ := GetK8sClientsForRequest(c)
+ if reqK8s == nil {
+ c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid or missing token"})
+ return
+ }
+
+ userID := c.GetString("userID")
+ if userID == "" {
+ c.JSON(http.StatusUnauthorized, gin.H{"error": "User authentication required"})
+ return
+ }
+
var req struct {
APIKey string `json:"apiKey" binding:"required"`
}As per coding guidelines, User-facing API ops MUST use GetK8sClientsForRequest(c), never the backend service account.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // TestCodeRabbitConnection handles POST /api/auth/coderabbit/test | |
| func TestCodeRabbitConnection(c *gin.Context) { | |
| var req struct { | |
| APIKey string `json:"apiKey" binding:"required"` | |
| } | |
| if err := c.ShouldBindJSON(&req); err != nil { | |
| c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | |
| return | |
| } | |
| func TestCodeRabbitConnection(c *gin.Context) { | |
| reqK8s, _ := GetK8sClientsForRequest(c) | |
| if reqK8s == nil { | |
| c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid or missing token"}) | |
| return | |
| } | |
| userID := c.GetString("userID") | |
| if userID == "" { | |
| c.JSON(http.StatusUnauthorized, gin.H{"error": "User authentication required"}) | |
| return | |
| } | |
| var req struct { | |
| APIKey string `json:"apiKey" binding:"required"` | |
| } | |
| if err := c.ShouldBindJSON(&req); err != nil { | |
| c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | |
| return | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/backend/handlers/integration_validation.go` around lines 232 -
241, The TestCodeRabbitConnection handler currently begins validation without
establishing request-scoped auth; call GetK8sClientsForRequest(c) at the start
of TestCodeRabbitConnection, check its returned error (and any nil client), and
if it fails return the appropriate HTTP error JSON and stop processing before
binding/validating the API key—this ensures the endpoint uses the request-scoped
k8s client instead of the backend service account and follows the same auth
pattern as other user-facing endpoints.
| export async function GET(request: Request) { | ||
| const headers = await buildForwardHeadersAsync(request) | ||
|
|
||
| const resp = await fetch(`${BACKEND_URL}/auth/coderabbit/status`, { | ||
| method: 'GET', | ||
| headers, | ||
| }) | ||
|
|
||
| const data = await resp.text() | ||
| return new Response(data, { status: resp.status, headers: { 'Content-Type': 'application/json' } }) | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify the same proxy pattern (uncaught fetch + forced JSON content-type) across CodeRabbit routes.
rg -n --type=ts -C2 'export async function (GET|POST|DELETE)\(request: Request\)|fetch\(|new Response\(data, \{ status: resp.status, headers: \{ '\''Content-Type'\'': '\''application/json'\'' \} \}\)' components/frontend/src/app/api/auth/coderabbit
rg -n --type=ts -C1 'try\s*\{' components/frontend/src/app/api/auth/coderabbitRepository: ambient-code/platform
Length of output: 5123
🏁 Script executed:
#!/bin/bash
# Check buildForwardHeadersAsync implementation
rg -n "buildForwardHeadersAsync" components/frontend/src/lib/auth --type=ts -A 10Repository: ambient-code/platform
Length of output: 137
🏁 Script executed:
#!/bin/bash
# Check if there are other proxy routes that handle errors correctly
find components/frontend/src/app/api -name "route.ts" -type f | head -20 | xargs grep -l "try\s*{" 2>/dev/null || echo "No routes with try/catch found"Repository: ambient-code/platform
Length of output: 249
🏁 Script executed:
#!/bin/bash
# Check the structure of a few other API routes to see error handling patterns
fd "route.ts" components/frontend/src/app/api --max-results=5 | xargs head -20Repository: ambient-code/platform
Length of output: 2796
🏁 Script executed:
#!/bin/bash
# Find buildForwardHeadersAsync implementation
rg -n "buildForwardHeadersAsync" components/frontend/src --type=ts -B2 -A8 | head -50Repository: ambient-code/platform
Length of output: 4485
🏁 Script executed:
#!/bin/bash
# Get full Google status route to see correct error handling pattern
cat -n components/frontend/src/app/api/auth/google/status/route.tsRepository: ambient-code/platform
Length of output: 1697
🏁 Script executed:
#!/bin/bash
# List all routes with the broken pattern (no try/catch)
rg -l "new Response\(data, \{ status: resp.status, headers: \{ 'Content-Type': 'application/json'" components/frontend/src/app/api --type=tsRepository: ambient-code/platform
Length of output: 1894
Guard upstream fetch failures and preserve upstream content-type.
This proxy throws on upstream failure (no catch) and forces JSON content-type even when upstream returns non-JSON error bodies, breaking the API contract under outage conditions. Same pattern affects the sibling disconnect, test, and connect routes, plus 20+ other routes across Jira, GitLab, GitHub, and other integrations. The Google OAuth status route shows the correct pattern with try/catch and proper error handling.
Proposed fix (apply same pattern to disconnect/test/connect routes)
export async function GET(request: Request) {
- const headers = await buildForwardHeadersAsync(request)
-
- const resp = await fetch(`${BACKEND_URL}/auth/coderabbit/status`, {
- method: 'GET',
- headers,
- })
-
- const data = await resp.text()
- return new Response(data, { status: resp.status, headers: { 'Content-Type': 'application/json' } })
+ try {
+ const headers = await buildForwardHeadersAsync(request)
+ const resp = await fetch(`${BACKEND_URL}/auth/coderabbit/status`, {
+ method: 'GET',
+ headers,
+ })
+
+ const data = await resp.text()
+ return new Response(data, {
+ status: resp.status,
+ headers: {
+ 'Content-Type': resp.headers.get('content-type') ?? 'application/json',
+ },
+ })
+ } catch {
+ return Response.json({ error: 'CodeRabbit upstream unavailable' }, { status: 502 })
+ }
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/frontend/src/app/api/auth/coderabbit/status/route.ts` around lines
4 - 14, The GET handler currently throws on upstream fetch failures and forces
'application/json'; wrap the fetch in try/catch, and when the fetch succeeds
read the upstream body (e.g., resp.text()) and return new Response(body, {
status: resp.status, headers: { 'Content-Type': resp.headers.get('content-type')
|| 'text/plain' } }); if resp.ok is false still forward the upstream status and
content-type (fall back to 'text/plain' if missing); in the catch block return a
502 Response with the error.message and 'text/plain' content-type. Apply the
same pattern to the sibling handlers (disconnect, test, connect and other
integration routes) so they preserve upstream content-type and handle
network/fetch errors gracefully.
| _GH_WRAPPER_DIR = "/tmp/bin" | ||
| _GH_WRAPPER_PATH = "/tmp/bin/gh" | ||
|
|
There was a problem hiding this comment.
Use a private, non-predictable wrapper path for gh
Line 565 and Line 566 place an executable shim in a fixed /tmp location. That is vulnerable to path/symlink hijack in shared runtime contexts and can compromise token-handling flow. Create a per-process private directory (0700) and write the wrapper there with restrictive permissions.
Proposed hardening patch
+import tempfile
@@
-_GH_WRAPPER_DIR = "/tmp/bin"
-_GH_WRAPPER_PATH = "/tmp/bin/gh"
+_GH_WRAPPER_DIR = tempfile.mkdtemp(prefix="ambient-gh-", dir="/tmp")
+os.chmod(_GH_WRAPPER_DIR, 0o700)
+_GH_WRAPPER_PATH = f"{_GH_WRAPPER_DIR}/gh"
@@
- wrapper_path.chmod(
- stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH
- ) # 755
+ wrapper_path.chmod(stat.S_IRWXU) # 700Also applies to: 712-719
🧰 Tools
🪛 Ruff (0.15.10)
[error] 565-565: Probable insecure usage of temporary file or directory: "/tmp/bin"
(S108)
[error] 566-566: Probable insecure usage of temporary file or directory: "/tmp/bin/gh"
(S108)
| if [ "$FRONTEND_PORT" != "NONE" ]; then | ||
| # The frontend is behind the kind NodePort — check container port mapping | ||
| FRONTEND_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:9323/integrations" 2>/dev/null || echo "000") | ||
| if [ "$FRONTEND_STATUS" = "200" ]; then pass "Integrations page loads (HTTP 200)" | ||
| else skip "Integrations page returned $FRONTEND_STATUS (may need auth)"; fi | ||
| else | ||
| skip "Frontend NodePort not found" | ||
| fi |
There was a problem hiding this comment.
Hardcoded port ignores discovered FRONTEND_PORT.
Line 182 uses hardcoded localhost:9323 instead of the FRONTEND_PORT discovered on lines 170-178. If the NodePort differs, this check will always hit the wrong endpoint.
Proposed fix
if [ "$FRONTEND_PORT" != "NONE" ]; then
- # The frontend is behind the kind NodePort — check container port mapping
- FRONTEND_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:9323/integrations" 2>/dev/null || echo "000")
+ # Use discovered NodePort for frontend check
+ FRONTEND_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:$FRONTEND_PORT/integrations" 2>/dev/null || echo "000")
if [ "$FRONTEND_STATUS" = "200" ]; then pass "Integrations page loads (HTTP 200)"
else skip "Integrations page returned $FRONTEND_STATUS (may need auth)"; fi
else📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if [ "$FRONTEND_PORT" != "NONE" ]; then | |
| # The frontend is behind the kind NodePort — check container port mapping | |
| FRONTEND_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:9323/integrations" 2>/dev/null || echo "000") | |
| if [ "$FRONTEND_STATUS" = "200" ]; then pass "Integrations page loads (HTTP 200)" | |
| else skip "Integrations page returned $FRONTEND_STATUS (may need auth)"; fi | |
| else | |
| skip "Frontend NodePort not found" | |
| fi | |
| if [ "$FRONTEND_PORT" != "NONE" ]; then | |
| # Use discovered NodePort for frontend check | |
| FRONTEND_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:$FRONTEND_PORT/integrations" 2>/dev/null || echo "000") | |
| if [ "$FRONTEND_STATUS" = "200" ]; then pass "Integrations page loads (HTTP 200)" | |
| else skip "Integrations page returned $FRONTEND_STATUS (may need auth)"; fi | |
| else | |
| skip "Frontend NodePort not found" | |
| fi |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/test-coderabbit-integration.sh` around lines 180 - 187, The health
check is using a hardcoded port 9323 instead of the discovered FRONTEND_PORT;
update the curl invocation that sets FRONTEND_STATUS to use the FRONTEND_PORT
variable (e.g. "http://localhost:${FRONTEND_PORT}/integrations") and keep the
same error fallback (|| echo "000"), preserving the surrounding conditional that
checks FRONTEND_PORT != "NONE" and the pass/skip calls (FRONTEND_STATUS, pass,
skip).
Summary
docs/internal/adr/0008-automate-code-reviews.md)Backend (Go):
Frontend (Next.js + React):
Runner (Python):
CODERABBIT_API_KEYinjected viaasyncio.gather, cleared on turn completionPre-commit hook:
coderabbit review --agent, supports API key env andcr auth loginCI + Testing:
./scripts/test-coderabbit-integration.sh(9/9 against dev cluster)Docs:
docs/src/content/docs/features/coderabbit.mddocs/pr-1307-impact-analysis.mdTest plan
go build,go vet, golangci-lint passtsc --noEmitandnpm run buildpass (0 errors, 0 warnings)🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes
New Features
Documentation
Tests