Repository navigation
fix(integrations): auto-refresh builtin provider configs on startup - #820
Conversation
…epics 7-9 - Fix packages/lib typecheck: replace `as ReturnType<typeof vi.fn>` casts with `vi.mocked()` in export-utils.test.ts (4 errors → 0) - Mark Epic 6 (6c, 6d) as COMPLETE in parent plan - Add Standards & Rules section to Epics 7, 8, 9 (matches 6a-6d) - Note missing getTenantByStripeSubscription method in Epic 7 - Note integration test DB requirement in Epic 8 - Document Caddy/Traefik coexistence in Epic 3 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Expand GitHub provider from 6 to 19 tools enabling full code review workflows. Add built-in import_from_github tool for bringing GitHub code into PageSpace as CODE pages. New capabilities: - Code browsing: get_repo, get_repo_content, get_repo_tree, list_branches, search_code, get_commit - PR review: get_pr_diff, get_pr_reviews, get_pr_review_comments, create_pr_review, create_pr_review_comment - Issue management: update_issue, list_issue_comments, create_issue_comment (renamed from create_pr_comment) - Code import: import_from_github with file/pr/directory modes Also includes: - Data migration (0091) for create_pr_comment → create_issue_comment grant references - Shared language-detection utility extracted from CodePageView - Activity logging for imported pages - 72 provider tests + ai-tools aggregation test fix Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Scope resolveGitHubToken to caller's userId (prevent cross-user token access) - Encode path segments in githubFetch to handle special characters (#, ?, %) - Make broadcastPageEvent best-effort (catch errors, don't fail imports) - Add typesVersions entry for utils/language-detection - Make path optional in get_repo_content (allows root directory listing) - Expose truncated flag in get_repo_tree output mapping - Require body in create_pr_review (needed for REQUEST_CHANGES/COMMENT) - Require commit_id in create_pr_review_comment (GitHub API requirement) - Require line in create_pr_review inline comments array - Update all corresponding test assertions Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cast connection.credentials as Record<string, string> instead of Record<string, unknown> to match decryptCredentials parameter type. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The project's custom logger uses (message: string, error?: Error, meta?: object) not pino's (mergingObject, message) signature. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
warn() takes 2 args (message, metadata?) not 3 like error() does. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Enforce driveId ownership for drive-scoped GitHub connections - Add pull_request.html_url to get_issues output (distinguish PRs from issues) - Default get_repo_tree to recursive=1 (always returns full tree) - Remove unused recursive input param from get_repo_tree schema Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… path test - Add line to required fields in create_pr_review_comment schema - Update description with guidance on where to get commit_id and line - Add test for get_repo_content with omitted path (repo root listing) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
.claude/*.local.md files are ephemeral loop state and should not be committed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Batch importedAt timestamp per operation instead of per-file, refactor processDirectory to return results instead of mutating closed-over state, remove unsafe double type assertion in resolveGitHubToken, and add 54 tests for github-import-tools and language-detection. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use Record<string, unknown> instead of narrow type assertion for extractionMetadata access in test, fixing CI typecheck failure. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- update_issue: add 'duplicate' to state_reason enum, allow null milestone - create_pr_review_comment: relax required fields to support reply mode (only owner/repo/pull_number/body required; path/commit_id/line optional) - github-import-tools: normalize maxFiles (floor, clamp 1..50), validate pullNumber is a positive integer Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The CI typecheck requires casting through unknown before narrowing CreatePageInput to Record types in test assertions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
seedBuiltinProviders only inserts new providers — it never updates existing ones. This means new tools added to githubProvider in code never reach the database, so agents still see the old 6-tool config. Add refreshBuiltinProviders() that compares the in-memory tool IDs against the DB and updates the config column when they've changed. Called lazily alongside the existing seed logic on first API call. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 5 minutes and 54 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds a builtin provider refresh flow: after lazy seeding, the GET providers route calls a new Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Route as ProvidersRoute
participant Registry as BuiltinRegistry
participant DB as Database
participant Logger
Client->>Route: GET /api/integrations/providers
Route->>DB: listEnabledProviders() (initial)
Route->>Registry: read builtinProviderList
Route->>DB: seedBuiltinProviders() (lazy seed)
Route->>Route: call refreshBuiltinProviders(db, builtinProviderList)
Route->>DB: for each builtin: find provider by slug & providerType='builtin'
alt config differs
DB->>DB: update provider.config
Route->>DB: listEnabledProviders() (re-query)
else no changes
Note right of Route: no re-query
end
Route->>Logger: warn on refresh error (non-fatal)
Route->>Client: return safeProviders (configs stripped)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 06fc4e6afb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
🧹 Nitpick comments (2)
packages/lib/src/integrations/repositories/provider-repository.test.ts (1)
616-632: Consider adding an empty-to-empty tools test case.The current tests cover
config: {}→ tools added, but not the case where both existing and new have empty/undefined tools (should skip update). This would verify the'' === ''comparison branch.it('given provider with no tools and builtin with no tools, should skip update', async () => { mockDb.query.integrationProviders.findFirst.mockResolvedValue({ id: 'prov-1', slug: 'github', name: 'GitHub', config: { tools: [] }, enabled: true, isSystem: true, }); const result = await refreshBuiltinProviders(mockDb, [ { id: 'github', name: 'GitHub', tools: [] }, ]); expect(result).toBe(0); expect(mockDb.update).not.toHaveBeenCalled(); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/lib/src/integrations/repositories/provider-repository.test.ts` around lines 616 - 632, Add a test in provider-repository.test.ts that exercises the empty-to-empty tools branch: stub mockDb.query.integrationProviders.findFirst to return a provider whose config.tools is an empty array, call refreshBuiltinProviders with a builtin provider whose tools is also an empty array, and assert the returned value is 0 and mockDb.update was not called; reference the existing test structure that uses refreshBuiltinProviders, mockDb.query.integrationProviders.findFirst, and mockDb.update to mirror the other case and ensure the no-op path is covered.apps/web/src/app/api/integrations/providers/route.ts (1)
50-56: Consider rate-limiting refresh checks for high-traffic scenarios.The refresh runs on every GET request, executing N queries (one per builtin) even when configs haven't changed. For admin-facing endpoints with low traffic this is fine, but if this endpoint sees heavy load, consider:
- A deployment-time migration or startup hook instead of per-request checks
- Caching the "last refreshed" timestamp and skipping checks within a time window
- A version marker in the DB to detect when refresh is needed
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/src/app/api/integrations/providers/route.ts` around lines 50 - 56, The per-request call to refreshBuiltinProviders in the GET handler causes N DB queries on every request; change this to a rate-limited refresh by adding a cached "lastRefreshed" check or a DB version marker so refreshBuiltinProviders(db, builtinProviderList) only runs when needed: implement an in-memory timestamp (e.g., lastBuiltinRefreshAt) or read a single refresh_version field from the DB and skip calling refreshBuiltinProviders if the timestamp/version is recent; ensure when a refresh does run you still update providers via listEnabledProviders(db) and update the cached timestamp/version accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/web/src/app/api/integrations/providers/route.ts`:
- Around line 50-56: The per-request call to refreshBuiltinProviders in the GET
handler causes N DB queries on every request; change this to a rate-limited
refresh by adding a cached "lastRefreshed" check or a DB version marker so
refreshBuiltinProviders(db, builtinProviderList) only runs when needed:
implement an in-memory timestamp (e.g., lastBuiltinRefreshAt) or read a single
refresh_version field from the DB and skip calling refreshBuiltinProviders if
the timestamp/version is recent; ensure when a refresh does run you still update
providers via listEnabledProviders(db) and update the cached timestamp/version
accordingly.
In `@packages/lib/src/integrations/repositories/provider-repository.test.ts`:
- Around line 616-632: Add a test in provider-repository.test.ts that exercises
the empty-to-empty tools branch: stub
mockDb.query.integrationProviders.findFirst to return a provider whose
config.tools is an empty array, call refreshBuiltinProviders with a builtin
provider whose tools is also an empty array, and assert the returned value is 0
and mockDb.update was not called; reference the existing test structure that
uses refreshBuiltinProviders, mockDb.query.integrationProviders.findFirst, and
mockDb.update to mirror the other case and ensure the no-op path is covered.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f5d3fc7d-9d1e-49f8-8b51-21b9bf3868fa
📒 Files selected for processing (4)
apps/web/src/app/api/integrations/providers/route.tspackages/lib/src/integrations/index.tspackages/lib/src/integrations/repositories/provider-repository.test.tspackages/lib/src/integrations/repositories/provider-repository.ts
Use stable recursive JSON serialization to detect any config change (schemas, rate limits, execution config) — not just added/removed tools. PostgreSQL JSONB doesn't preserve key order, so keys are sorted at all nesting levels before comparison. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
packages/lib/src/integrations/repositories/provider-repository.test.ts (1)
550-685: Add DB failure-path tests for refresh flow.The new suite covers change detection well, but it doesn’t verify behavior when
findFirstorupdaterejects. Addrejects.toThrowcases so error propagation remains intentional for the route-level non-fatal catch path.Suggested tests
+ it('given findFirst throws, should propagate error', async () => { + mockDb.query.integrationProviders.findFirst.mockRejectedValue(new Error('read failed')); + await expect( + refreshBuiltinProviders(mockDb, [{ id: 'github', name: 'GitHub', tools: [{ id: 'list_repos' }] }]) + ).rejects.toThrow('read failed'); + }); + + it('given update throws, should propagate error', async () => { + mockDb.query.integrationProviders.findFirst.mockResolvedValue({ + id: 'prov-1', + slug: 'github', + name: 'GitHub', + config: { id: 'github', name: 'GitHub', tools: [] }, + enabled: true, + isSystem: true, + }); + mockDb.update.mockReturnValue({ + set: vi.fn().mockReturnValue({ + where: vi.fn().mockReturnValue({ + returning: vi.fn().mockRejectedValue(new Error('write failed')), + }), + }), + }); + + await expect( + refreshBuiltinProviders(mockDb, [{ id: 'github', name: 'GitHub', tools: [{ id: 'list_repos' }] }]) + ).rejects.toThrow('write failed'); + });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/lib/src/integrations/repositories/provider-repository.test.ts` around lines 550 - 685, Add tests for DB failure paths in the refreshBuiltinProviders test suite: create one test where mockDb.query.integrationProviders.findFirst.mockRejectedValue(new Error(...)) and assert await expect(refreshBuiltinProviders(mockDb, [...])).rejects.toThrow(), and another where findFirst returns a provider but mockDb.update.mockReturnValue(...).set(...).where(...).returning.mockRejectedValue(new Error(...)) and assert the same rejects.toThrow; reference refreshBuiltinProviders, mockDb.query.integrationProviders.findFirst and mockDb.update so the tests simulate and verify intentional error propagation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/lib/src/integrations/repositories/provider-repository.test.ts`:
- Around line 529-548: The test defines a local helper function named
refreshBuiltinProviders which duplicates the production logic and hides the real
implementation; remove the local refreshBuiltinProviders definition in
provider-repository.test.ts and import the exported refreshBuiltinProviders from
packages/lib/src/integrations/repositories/provider-repository.ts instead,
updating the test to call that imported function (and adjust any typings or mock
db shape to match the production function signature) so the suite validates the
actual implementation rather than a duplicate.
---
Nitpick comments:
In `@packages/lib/src/integrations/repositories/provider-repository.test.ts`:
- Around line 550-685: Add tests for DB failure paths in the
refreshBuiltinProviders test suite: create one test where
mockDb.query.integrationProviders.findFirst.mockRejectedValue(new Error(...))
and assert await expect(refreshBuiltinProviders(mockDb,
[...])).rejects.toThrow(), and another where findFirst returns a provider but
mockDb.update.mockReturnValue(...).set(...).where(...).returning.mockRejectedValue(new
Error(...)) and assert the same rejects.toThrow; reference
refreshBuiltinProviders, mockDb.query.integrationProviders.findFirst and
mockDb.update so the tests simulate and verify intentional error propagation.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: d5e88732-a078-4757-9ac8-4c16f63c0c61
📒 Files selected for processing (2)
packages/lib/src/integrations/repositories/provider-repository.test.tspackages/lib/src/integrations/repositories/provider-repository.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/lib/src/integrations/repositories/provider-repository.ts
…ders Verify that findFirst and update errors propagate to the caller rather than being silently swallowed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
githubProvider) are stored in the DB at first seed and never updated — new tools added in code deployments don't reach agentsrefreshBuiltinProviders()that compares in-memory tool IDs against the DB and updates theconfigcolumn when they've changed/api/integrations/providersGET call after bootRoot cause
seedBuiltinProviders()only inserts providers that don't exist. After PR #818 added 13 new GitHub tools (19 total), agents still saw the original 6 because the DB config was frozen from first deployment.Changes
provider-repository.ts: newrefreshBuiltinProviders(db, builtins)— idempotent, only writes when tool IDs differindex.ts: export the new functionroute.ts: call refresh after seed check in GET handlerprovider-repository.test.ts: 4 new tests (stale update, current skip, missing skip, empty-to-new)Test plan
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Tests
Robustness