Fix integration scope save latency#90
Conversation
|
CodeAnt AI is reviewing your PR. |
ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Free Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughBroker refresh now returns early for project-scoped calls when the Relay workspace isn't started; integration option fetching uses account workspace endpoints via fetchJson (with Slack legacy fallback); integration state sync is made non-blocking and failures are logged. Tests add hoisted mocks and cover these behaviors. ChangesError resilience and integration refactoring
🎯 3 (Moderate) | ⏱️ ~25 minutes
Note 🎁 Summarized by CodeRabbit FreeYour organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces a no-op behavior for project-scoped event stream refreshes before a broker session starts, refactors integration option retrieval to use direct JSON fetching instead of workspace handles, and makes agent state synchronization asynchronous. Feedback suggests using the existing isWorkspaceNotStartedError helper in broker.ts instead of hardcoding error message strings, and emitting an error event when background agent state synchronization fails in integrations.ts so the UI can notify the user.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| } catch (err) { | ||
| if (err instanceof Error && err.message === 'Relay workspace not started — select the project first') { | ||
| return | ||
| } | ||
| throw err | ||
| } |
There was a problem hiding this comment.
Instead of hardcoding the exact error message string comparison, use the existing isWorkspaceNotStartedError(err) helper function. This is more robust, handles non-Error thrown values safely, and maintains consistency with how this error is checked elsewhere in the codebase.
} catch (err) {
if (isWorkspaceNotStartedError(err)) {
return
}
throw err
}| private syncAgentStateEventually(projectId: string, notifyAgent: boolean): void { | ||
| void this.syncAgentState(projectId, notifyAgent).catch((error) => { | ||
| console.warn('[integrations] Failed to sync integration state:', toErrorMessage(error)) | ||
| }) | ||
| } |
There was a problem hiding this comment.
Since syncAgentState is now executed asynchronously in the background via syncAgentStateEventually, any failures during the sync process (such as local mount failures or event subscription reconciliation errors) will only be logged to the console and won't be propagated to the user. Consider emitting an integration-error event on failure so the UI can display an appropriate warning banner to the user if the background sync fails.
| private syncAgentStateEventually(projectId: string, notifyAgent: boolean): void { | ||
| void this.syncAgentState(projectId, notifyAgent).catch((error) => { | ||
| console.warn('[integrations] Failed to sync integration state:', toErrorMessage(error)) | ||
| }) | ||
| } |
There was a problem hiding this comment.
Suggestion: This fire-and-forget sync introduces a race: multiple rapid updateScope calls can run syncAgentState concurrently for the same project, but downstream reconciliation is not serialized. That can interleave close/re-subscribe cycles and leave duplicate/stale event subscriptions active. Serialize background sync per project (for example with a per-project promise queue or lock) before launching another sync. [race condition]
Severity Level: Critical 🚨
- ❌ Duplicate integration events delivered to project agents.
- ⚠️ Stale subscriptions watch outdated mount paths after scope changes.
- ⚠️ Orphaned watchers leak resources in IntegrationEventBridge.reconcile.Steps of Reproduction ✅
1. Start the app and broker for a project so integration events are active; `broker:start`
is handled in `src/main/ipc-handlers.ts:5-20` and, on success, calls
`integrationsManager.notifyAgentState(projectId)` at `src/main/ipc-handlers.ts:15-16`,
which in turn awaits `this.syncAgentState(projectId, true)` in
`src/main/integrations.ts:821-823`.
2. In the renderer, open Project Settings so that integration visibility UI is available;
actions like toggling visibility or Slack channel selections ultimately call
`pear.integrations.updateScope(...)` via the preload bridge at
`src/preload/index.ts:12-24`, which invokes the IPC handler `'integrations:update-scope'`
at `src/main/ipc-handlers.ts:15-19`.
3. From the renderer (e.g., devtools console or rapid UI interactions from multiple
windows), issue two `pear.integrations.updateScope(projectId, integrationId, scope,
mountPaths)` calls in quick succession without waiting between them; both IPC requests
reach `IntegrationsManager.updateScope` at `src/main/integrations.ts:955-963`, which
persists the integration, emits an event, and then calls the fire‑and‑forget
`this.syncAgentStateEventually(projectId, integration.notifyAgent)` at
`src/main/integrations.ts:45-46`.
4. Each `syncAgentStateEventually` invocation starts an overlapping
`syncAgentState(projectId, notifyAgent)` task (defined at
`src/main/integrations.ts:1508-1517`), and both tasks call
`this.syncEventSubscriptions(projectId)` at `src/main/integrations.ts:1513`, which
delegates to `integrationEventBridge.reconcile(projectId, integrations)` at
`src/main/integration-event-bridge.ts:203-235`. Because `reconcile` performs `await
this.close(projectId)` near the top and later overwrites
`this.subscriptions.set(projectId, { subscriptions, signature })` without any per‑project
locking, the two concurrent calls can interleave such that one call's `close()` sees no
current subscription (map already cleared) and the other call later overwrites the map
entry, leaving the first call's newly created `subscriptions` array with active watchers
that are never reachable for future `close()` calls. This yields duplicate or stale event
subscriptions for the same project (multiple active Relayfile subscriptions / FS watchers)
and can be observed as duplicated integration events being delivered to agents from
`IntegrationEventBridge.injectEvent` in `src/main/integration-event-bridge.ts:63-145`.Fix in Cursor | Fix in VSCode Claude
(Use Cmd/Ctrl + Click for best experience)
Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** src/main/integrations.ts
**Line:** 1502:1506
**Comment:**
*Race Condition: This fire-and-forget sync introduces a race: multiple rapid `updateScope` calls can run `syncAgentState` concurrently for the same project, but downstream reconciliation is not serialized. That can interleave close/re-subscribe cycles and leave duplicate/stale event subscriptions active. Serialize background sync per project (for example with a per-project promise queue or lock) before launching another sync.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix|
CodeAnt AI finished reviewing your PR. |
|
Fixed the validated PR review items:
Local validation passed:
|
|
Fixed the validated PR review items:
Local validation passed:
|
|
✅ pr-reviewer applied fixes — committed and pushed Added focused regression coverage in src/main/integrations.test.ts for the PR’s integration changes:
Local verification passed:
|
|
ℹ️ pr-reviewer: review only — no file changes were applied to the PR (nothing to commit after review). The notes below are advisory and were not pushed. Reviewed PR #90 against the checked-out diff and traced the touched broker/integration paths. I did not find a current, reproducible breakage that needed a code change. Verification run:
All passed after installing dependencies from the lockfile. |
|
Reviewed PR #90 against the checked-out diff and traced the affected broker/integration paths. I did not find a current, reproducible defect requiring a code change. Verified locally:
|
|
ℹ️ pr-reviewer: review only — no file changes were applied to the PR (nothing to commit after review). The notes below are advisory and were not pushed. Reviewed PR #90 against the checked-out diff and traced the affected broker/integration paths. I did not find a current, reproducible defect requiring a code change. Verified locally:
|
|
✅ pr-reviewer applied fixes — committed and pushed Implemented the validated fixes from the PR review artifacts:
Local validation passed:
|
User description
Summary
Verification
npm exec tsc -- --noEmitnpm exec vitest -- run src/main/broker.test.tsnpm run buildCodeAnt-AI Description
Fix integration scope saves and ignore project refreshes before startup
What Changed
Impact
✅ Faster integration scope saves✅ Fewer blocked settings updates✅ Clearer Slack channel selection🔄 Retrigger CodeAnt AI Review
💡 Usage Guide
Checking Your Pull Request
Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.
Talking to CodeAnt AI
Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:
This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.
Example
Preserve Org Learnings with CodeAnt
You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:
This helps CodeAnt AI learn and adapt to your team's coding style and standards.
Example
Retrigger review
Ask CodeAnt AI to review the PR again, by typing:
Check Your Repository Health
To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.